diff --git a/custom_components/houseplan/const.py b/custom_components/houseplan/const.py index 9aa103ba..13e3ad8f 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.59.0-beta.8" +VERSION = "1.59.0-beta.9" DEFAULT_CONFIG: dict = { "spaces": [], diff --git a/custom_components/houseplan/frontend/houseplan-card.js b/custom_components/houseplan/frontend/houseplan-card.js index cfa97412..c729d259 100755 --- a/custom_components/houseplan/frontend/houseplan-card.js +++ b/custom_components/houseplan/frontend/houseplan-card.js @@ -1,10 +1,10 @@ -const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,g=_?_.emptyScript:"",m=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?g: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}},b=(t,e)=>!a(t,e),y={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:b};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=y){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??y}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=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??b)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,m?.({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,T=`<${D}>`,z=document,P=()=>z.createComment(""),A=t=>null===t||"object"!=typeof t&&"function"!=typeof t,R=Array.isArray,E="[ \t\n\f\r]",N=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,O=/-->/g,I=/>/g,F=RegExp(`>|${E}(?:([^\\s"'>=/]+)(${E}*=${E}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,H=/"/g,q=/^(?: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"),V=Symbol.for("lit-nothing"),G=new WeakMap,K=z.createTreeWalker(z,129);function Y(t,e){if(!R(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const X=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",r=N;for(let e=0;e"===l[0]?(r=n??N,c=-1):void 0===l[1]?c=-2:(c=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?F:'"'===l[3]?H:L):r===H||r===L?r=F:r===O||r===I?r=N:(r=F,n=void 0);const d=r===F&&t[e+1].startsWith("/>")?" ":"";o+=r===N?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,o+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const r=t.length-1,a=this.parts,[l,c]=X(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iR(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!==V&&A(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=G.get(t.strings);return void 0===e&&G.set(t.strings,e=new Z(t)),e}k(t){R(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(P()),this.O(P()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=V}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=J(this,t,e,0),o=!A(t)||t!==this._$AH&&t!==j,o&&(this._$AH=t);else{const s=t;let r,a;for(t=n[0],r=0;r{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(P(),t),t,void 0,i??{})}return n._$AI(t),n})(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 ct=at.litElementPolyfillSupport;ct?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ht=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),dt=[{pattern:"протечк|leak|water sensor",icon:"mdi:water-alert"},{pattern:"клапан|valve",icon:"mdi:pipe-valve"},{pattern:"дым|smoke",icon:"mdi:smoke-detector"},{pattern:"термоголов|trv|radiator",icon:"mdi:radiator"},{pattern:"чайник|kettle|термопот",icon:"mdi:kettle"},{pattern:"сауна|sauna|harvia|парная|парилк",icon:"mdi:hot-tub"},{pattern:"температ|temperature|climate sensor",icon:"mdi:thermometer"},{pattern:"qingping|air monitor|молекул|air quality",icon:"mdi:air-filter"},{pattern:"штор|curtain|blind|shade",icon:"mdi:roller-shade"},{pattern:"розетк|plug|socket|outlet",icon:"mdi:power-socket-de"},{pattern:"выключат|switch",icon:"mdi:light-switch"},{pattern:"лампа|лампочк|bulb|gx53|светильник|rgb|lamp|light strip",icon:"mdi:lightbulb"},{pattern:"камер|camera",icon:"mdi:cctv"},{pattern:"замок|ttlock|lock|sn609|sn9161",icon:"mdi:lock"},{pattern:"ворота|garage|gate",icon:"mdi:garage-variant"},{pattern:"калитк|door|открыт|contact",icon:"mdi:door"},{pattern:"счётчик|счетчик|kws|meter",icon:"mdi:meter-electric"},{pattern:"вводный автомат|breaker|wifimcbn",icon:"mdi:electric-switch"},{pattern:"myheat|котёл|котел|boiler|отоплен|heating",icon:"mdi:water-boiler"},{pattern:"холодильник|fridge",icon:"mdi:fridge"},{pattern:"стиральн|washer|washing",icon:"mdi:washing-machine"},{pattern:"сушилк|dryer",icon:"mdi:tumble-dryer"},{pattern:"пылесос|vacuum|dreame|roborock",icon:"mdi:robot-vacuum"},{pattern:"soundbar",icon:"mdi:soundbar"},{pattern:"колонк|станц|speaker|яндекс|yandex|алиса|alice",icon:"mdi:speaker"},{pattern:"tv|телевизор|hyundaitv|mitv|television",icon:"mdi:television"},{pattern:"keenetic|роутер|router|mesh|access point",icon:"mdi:router-wireless"},{pattern:"ибп|ups|kirpich",icon:"mdi:battery-charging-high"},{pattern:"slzb|координат|zigbee|coordinator",icon:"mdi:zigbee"},{pattern:"motion|движен|presence|присутств",icon:"mdi:motion-sensor"},{pattern:"humidity|влажн",icon:"mdi:water-percent"}];function pt(t){const e=[];for(const i of t)if(i&&"string"==typeof i.pattern&&i.icon)try{e.push({re:new RegExp(i.pattern,"i"),icon:i.icon})}catch{}return e}const ut=pt(dt),_t={temperature:"mdi:thermometer",humidity:"mdi:water-percent",motion:"mdi:motion-sensor",occupancy:"mdi:motion-sensor",door:"mdi:door",window:"mdi:window-closed",garage_door:"mdi:garage-variant",smoke:"mdi:smoke-detector",moisture:"mdi:water-alert",gas:"mdi:gas-cylinder",power:"mdi:meter-electric",energy:"mdi:meter-electric",illuminance:"mdi:brightness-5",co2:"mdi:molecule-co2",pm25:"mdi:air-filter",battery:"mdi:battery"},gt="mdi:chip";function mt(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??ut)if(t.test(s))return e;return gt}const ft=["light","switch","cover","valve","lock","climate","fan","media_player","camera","vacuum","humidifier","water_heater","alarm_control_panel","sensor","binary_sensor","event","button","number","select","update"];var vt=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,bt=Math.ceil,yt=Math.floor,wt="[BigNumber Error] ",kt=wt+"Number primitive has more than 15 significant digits: ",$t=1e14,xt=14,St=9007199254740991,Mt=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],Ct=1e7,Dt=1e9;function Tt(t){var e=0|t;return t>0||t===e?e:e-1}function zt(t){for(var e,i,s=1,n=t.length,o=t[0]+"";sc^i?1:-1;for(a=(l=n.length)<(c=o.length)?l:c,r=0;ro[r]^i?1:-1;return l==c?0:l>c^i?1:-1}function At(t,e,i,s){if(ti||t!==yt(t))throw Error(wt+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Rt(t){var e=t.c.length-1;return Tt(t.e/xt)==e&&t.c[e]%2!=0}function Et(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Nt(t,e,i){var s,n;if(e<0){for(n=i+".";++e;n+=i);t=n+t}else if(++e>(s=t.length)){for(n=i,e-=s;--e;n+=i);t+=n}else eb?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>b?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!vt.test(d=String(t)))return n(p,d,c);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(At(e,2,x.length,"Base"),10==e&&S)return z(p=new M(t),_+p.e+1,g);if(d=String(t),c="number"==typeof t){if(0*t!=0)return n(p,d,c,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(kt+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,h=d.length;la){a=h;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return n(p,String(t),c,e)}c=!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(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(t>St||t!==yt(t)))throw Error(kt+p.s*t);if((a=a-l-1)>b)p.c=p.e=null;else if(a=f)?Et(l,r):Nt(l,r,"0");else if(o=(t=z(new M(t),e,i)).e,a=(l=zt(t.c)).length,1==s||2==s&&(e<=o||o<=m)){for(;ar),l=Nt(l,o,"0"),o+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=o-a)>0)for(o+1==a&&(l+=".");e--;l+="0");return t.s<0&&n?"-"+l:l}function D(t,e){for(var i,s,n=1,o=new M(t[0]);n=10;n/=10,s++);return(i=s+i*xt-1)>b?t.c=t.e=null:i=10;a/=10,n++);if((o=e-n)<0)o+=xt,r=e,l=d[c=0],h=yt(l/p[n-r-1]%10);else if((c=bt((o+1)/xt))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,n=1,r=(o%=xt)-xt+1}else{for(l=a=d[c],n=1;a>=10;a/=10,n++);h=(r=(o%=xt)-xt+n)<0?0:yt(l/p[n-r-1]%10)}if(s=s||e<0||null!=d[c+1]||(r<0?l:l%p[n-r-1]),s=i<4?(h||s)&&(0==i||i==(t.s<0?3:2)):h>5||5==h&&(4==i||s||6==i&&(o>0?r>0?l/p[n-r]:0:d[c-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[(xt-e%xt)%xt],t.e=-e||0):d[0]=t.e=0,t;if(0==o?(d.length=c,a=1,c--):(d.length=c+1,a=p[xt-o],d[c]=r>0?yt(l/p[n-r]%p[r])*a:0),s)for(;;){if(0==c){for(o=1,r=d[0];r>=10;r/=10,o++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);o!=a&&(t.e++,d[0]==$t&&(d[0]=1));break}if(d[c]+=a,d[c]!=$t)break;d[c--]=0,a=1}for(o=d.length;0===d[--o];d.pop());}t.e>b?t.c=t.e=null:t.e=f?Et(e,i):Nt(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(wt+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(At(i=t[e],0,Dt,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(At(i=t[e],0,8,e),g=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(At(i[0],-Dt,0,e),At(i[1],0,Dt,e),m=i[0],f=i[1]):(At(i,-Dt,Dt,e),m=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)At(i[0],-Dt,-1,e),At(i[1],1,Dt,e),v=i[0],b=i[1];else{if(At(i,-Dt,Dt,e),!i)throw Error(wt+e+" cannot be zero: "+i);v=-(b=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(wt+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw y=!i,Error(wt+"crypto unavailable");y=i}else y=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(At(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(At(i=t[e],0,Dt,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(wt+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(wt+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:g,EXPONENTIAL_AT:[m,f],RANGE:[v,b],CRYPTO:y,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,n=t.e,o=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===o||-1===o)&&n>=-Dt&&n<=Dt&&n===yt(n)){if(0===s[0]){if(0===n&&1===s.length)return!0;break t}if((e=(n+1)%xt)<1&&(e+=xt),String(s[0]).length==e){for(e=0;e=$t||i!==yt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===n&&(null===o||1===o||-1===o))return!0;throw Error(wt+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(o=9007199254740992,r=Math.random()*o&2097151?function(){return yt(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,n,o,a=0,l=[],c=new M(u);if(null==t?t=_:At(t,0,Dt),n=bt(t/xt),y)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(n*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(o%1e14),a+=2);a=n/2}else{if(!crypto.randomBytes)throw y=!1,Error(wt+"crypto unavailable");for(e=crypto.randomBytes(n*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(o%1e14),a+=7);a=n/7}if(!y)for(;a=10;o/=10,a++);ai-1&&(null==r[n+1]&&(r[n+1]=0),r[n+1]+=r[n]/i|0,r[n]%=i)}return r.reverse()}return function(s,n,o,r,a){var l,c,h,d,p,u,m,f,v=s.indexOf("."),b=_,y=g;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(n)).pow(s.length-v),k=d,f.c=e(Nt(zt(u.c),u.e,"0"),10,o,t),f.e=f.c.length),h=d=(m=e(s,n,o,a?(l=x,t):(l=t,x))).length;0==m[--d];m.pop());if(!m[0])return l.charAt(0);if(v<0?--h:(u.c=m,u.e=h,u.s=r,m=(u=i(u,f,b,y,o)).c,p=u.r,h=u.e),v=m[c=h+b+1],d=o/2,p=p||c<0||null!=m[c+1],p=y<4?(null!=v||p)&&(0==y||y==(u.s<0?3:2)):v>d||v==d&&(4==y||p||6==y&&1&m[c-1]||y==(u.s<0?8:7)),c<1||!m[0])s=p?Nt(l.charAt(1),-b,l.charAt(0)):l.charAt(0);else{if(m.length=c,p)for(--o;++m[--c]>o;)m[c]=0,c||(++h,m=[1].concat(m));for(d=m.length;!m[--d];);for(v=0,s="";v<=d;s+=l.charAt(m[v++]));s=Nt(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,n,o,r,a=0,l=t.length,c=e%Ct,h=e/Ct|0;for(t=t.slice();l--;)a=((n=c*(o=t[l]%Ct)+(s=h*o+(r=t[l]/Ct|0)*c)%Ct*Ct+a)/i|0)+(s/Ct|0)+h*r,t[l]=n%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var n,o;if(i!=s)o=i>s?1:-1;else for(n=o=0;ne[n]?1:-1;break}return o}function i(t,e,i,s){for(var n=0;i--;)t[i]-=n,n=t[i]1;t.splice(0,1));}return function(s,n,o,r,a){var l,c,h,d,p,u,_,g,m,f,v,b,y,w,k,$,x,S=s.s==n.s?1:-1,C=s.c,D=n.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&n.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(m=(g=new M(S)).c=[],S=o+(c=s.e-n.e)+1,a||(a=$t,c=Tt(s.e/xt)-Tt(n.e/xt),S=S/xt|0),h=0;D[h]==(C[h]||0);h++);if(D[h]>(C[h]||0)&&c--,S<0)m.push(1),d=!0;else{for(w=C.length,$=D.length,h=0,S+=2,(p=yt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),$=D.length,w=C.length),y=$,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(b=f[0],$!=v&&(b=b*a+(f[1]||0)),(p=yt(b/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,h++);z(g,o+(g.e=h+c*xt-1)+1,r,d)}else g.e=c,g.r=+d;return g}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,n=function(t,e,i,s){var n,o=i?e:e.replace(d,"");if(h.test(o))t.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(a,function(t,e,i){return n="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=n?t:e}),s&&(n=s,o=o.replace(l,"$1").replace(c,"0.$1")),e!=o))return new M(o,n);if(M.DEBUG)throw Error(wt+"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 Pt(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,n,o=this;if(null!=t)return At(t,0,Dt),null==e?e=g:At(e,0,8),z(new M(o),t+o.e+1,e);if(!(i=o.c))return null;if(s=((n=i.length-1)-Tt(this.e/xt))*xt,n=i[n])for(;n%10==0;n/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,g)},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,n,o,r,a,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(wt+"Exponent not an integer: "+P(t));if(null!=e&&(e=new M(e)),r=t.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!t.c||!t.c[0])return c=new M(Math.pow(+P(h),r?t.s*(2-Rt(t)):+P(t))),e?c.mod(e):c;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&h.isInteger()&&e.isInteger())&&(h=h.mod(e))}else{if(t.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||r&&h.c[1]>=24e7:h.c[0]<8e13||r&&h.c[0]<=9999975e7)))return o=h.s<0&&Rt(t)?-0:0,h.e>-1&&(o=1/o),new M(a?1/o:o);k&&(o=bt(k/xt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Rt(t)):l=(n=Math.abs(+P(t)))%2,c=new M(u);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):s&&(c=c.mod(e))}if(n){if(0===(n=yt(n/2)))break;l=n%2}else if(z(t=t.times(i),t.e+1,1),t.e>14)l=Rt(t);else{if(0===(n=+P(t)))break;l=n%2}h=h.times(h),o?h.c&&h.c.length>o&&(h.c.length=o):s&&(h=h.mod(e))}return s?c:(a&&(c=u.div(c)),e?c.mod(e):o?z(c,k,g,void 0):c)},p.integerValue=function(t){var e=new M(this);return null==t?t=g:At(t,0,8),z(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===Pt(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return Pt(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=Pt(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&Tt(this.e/xt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return Pt(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=Pt(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,n,o,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/xt,c=t.e/xt,h=r.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?r:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?r:3==g?-0:0)}if(l=Tt(l),c=Tt(c),h=h.slice(),a=l-c){for((o=a<0)?(a=-a,n=h):(c=l,n=d),n.reverse(),e=a;e--;n.push(0));n.reverse()}else for(s=(o=(a=h.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;h[i++]=0);for(e=$t-1;s>a;){if(h[--s]=0;){for(i=0,p=b[n]%m,u=b[n]/m|0,o=n+(r=l);o>n;)i=((c=p*(c=v[--r]%m)+(a=u*c+(h=v[r]/m|0)*p)%m*m+_[o]+i)/g|0)+(a/m|0)+u*h,_[o--]=c%g;_[o]=i}return i?++s:_.splice(0,1),T(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,n=s.s;if(e=(t=new M(t,e)).s,!n||!e)return new M(NaN);if(n!=e)return t.s=-e,s.minus(t);var o=s.e/xt,r=t.e/xt,a=s.c,l=t.c;if(!o||!r){if(!a||!l)return new M(n/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*n)}if(o=Tt(o),r=Tt(r),a=a.slice(),n=o-r){for(n>0?(r=o,i=l):(n=-n,i=a),i.reverse();n--;i.push(0));i.reverse()}for((n=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=n),n=0;e;)n=(a[--e]=a[e]+l[e]+n)/$t|0,a[e]=$t===a[e]?0:a[e]%$t;return n&&(a=[n].concat(a),++r),T(t,a,r)},p.precision=p.sd=function(t,e){var i,s,n,o=this;if(null!=t&&t!==!!t)return At(t,1,Dt),null==e?e=g:At(e,0,8),z(new M(o),t,e);if(!(i=o.c))return null;if(s=(n=i.length-1)*xt+1,n=i[n]){for(;n%10==0;n/=10,s--);for(n=i[0];n>=10;n/=10,s++);}return t&&o.e+1>s&&(s=o.e+1),s},p.shiftedBy=function(t){return At(t,-9007199254740991,St),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,n,o,r=this,a=r.c,l=r.s,c=r.e,h=_+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(+P(r)))||l==1/0?(((e=zt(a)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=Tt((c+1)/2)-(c<0||c%2),s=new M(e=l==1/0?"5e"+c:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+c)):s=new M(l+""),s.c[0])for((l=(c=s.e)+h)<3&&(l=0);;)if(o=s,s=d.times(o.plus(i(r,o,h,1))),zt(o.c).slice(0,l)===(e=zt(s.c)).slice(0,l)){if(s.e0&&_>0){for(o=_%a||a,h=u.substr(0,o);o<_;o+=a)h+=c+u.substr(o,a);l>0&&(h+=c+u.slice(o)),p&&(h="-"+h)}s=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,n,o,r,a,l,c,h,d,p,_,m=this,f=m.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(wt+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+P(l));if(!f)return new M(m);for(e=new M(u),h=s=new M(u),n=c=new M(u),_=zt(f),r=e.e=_.length-m.e-1,e.c[0]=Mt[(a=r%xt)<0?xt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:h:l,a=b,b=1/0,l=new M(_),c.c[0]=0;d=i(l,e,0,1),1!=(o=s.plus(d.times(n))).comparedTo(t);)s=n,n=o,h=c.plus(d.times(o=h)),c=o,e=l.minus(d.times(o=e)),l=o;return o=i(t.minus(s),n,0,1),c=c.plus(o.times(h)),s=s.plus(o.times(n)),c.s=h.s=m.s,p=i(h,n,r*=2,g).minus(m).abs().comparedTo(i(c,s,r,g).minus(m).abs())<1?[h,n]:[c,s],b=a,p},p.toNumber=function(){return+P(this)},p.toPrecision=function(t,e){return null!=t&&At(t,1,Dt),C(this,t,e,2)},p.toString=function(t){var e,i=this,n=i.s,o=i.e;return null===o?n?(e="Infinity",n<0&&(e="-"+e)):e="NaN":(null==t?e=o<=m||o>=f?Et(zt(i.c),o):Nt(zt(i.c),o,"0"):10===t&&S?e=Nt(zt((i=z(new M(i),_+o+1,g)).c),i.e,"0"):(At(t,2,x.length,"Base"),e=s(Nt(zt(i.c),o,"0"),10,t,n,!0)),n<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return P(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),It=class{key;left=null;right=null;constructor(t){this.key=t}},Ft=class extends It{constructor(t){super(t)}},Lt=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,n=null,o=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==n?o=r:n.right=r,n=r,r=e}}return null!=n&&(n.right=r.left,r.left=o),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)}}},Ht=class t extends Lt{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 Ft(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ft(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 Ft(t.key);return function t(e,i){let s,n;do{if(s=e.left,n=e.right,null!=s){const e=new Ft(s.key);i.left=e,t(s,e)}if(null!=n){const t=new Ft(n.key);i.right=t,e=n,i=t}}while(null!=n)}(t,e),e}toSet(){return this.clone()}entries(){return new Wt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Ut(this.wrap())}[Symbol.toStringTag]="[object Set]"},qt=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}},Ut=class extends qt{getValue(t){return t.key}},Wt=class extends qt{getValue(t){return[t.key,t.key]}},Bt=t=>()=>t,jt=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Bt(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Vt(t){const e=t?(e,i,s,n,o)=>e.exponentiatedBy(2).isLessThanOrEqualTo(n.minus(i).exponentiatedBy(2).plus(o.minus(s).exponentiatedBy(2)).times(t)):Bt(!1);return(t,i,s)=>{const n=t.x,o=t.y,r=s.x,a=s.y,l=o.minus(a).times(i.x.minus(r)).minus(n.minus(r).times(i.y.minus(a)));return e(l,n,o,r,a)?0:l.comparedTo(0)}}var Gt=t=>t,Kt=t=>{if(t){const e=new Ht(jt(t)),i=new Ht(jt(t)),s=(t,e)=>e.addAndReturn(t),n=t=>({x:s(t.x,e),y:s(t.y,i)});return n({x:new Ot(0),y:new Ot(0)}),n}return Gt},Yt=t=>({set:t=>{Xt=Yt(t)},reset:()=>Yt(t),compare:jt(t),snap:Kt(t),orient:Vt(t)}),Xt=Yt(),Zt=(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),Jt=(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}}},Qt=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),te=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ee=t=>te(t,t).sqrt(),ie=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return Qt(n,s).div(ee(n)).div(ee(s))},se=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return te(n,s).div(ee(n)).div(ee(s))},ne=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},oe=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},re=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:_e.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:ie(this.point,t.point,s.point),cosine:se(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:n,cosine:o}=e.get(t),{sine:r,cosine:a}=e.get(s);return n.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?o.isLessThan(a)?1:o.isGreaterThan(a)?-1:0:n.isLessThan(0)&&r.isLessThan(0)?o.isLessThan(a)?-1:o.isGreaterThan(a)?1:0:r.isLessThan(n)?-1:r.isGreaterThan(n)?1:0}}},ae=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,n=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}}},le=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&&n){const t=s.getIntersection(n);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(c))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(c))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(n.isLessThan(o)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(n.isGreaterThan(o)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!n.eq(o)){const t=l.minus(r),e=n.minus(i),h=c.minus(a),d=o.minus(s);if(t.isGreaterThan(e)&&h.isLessThan(d))return 1;if(t.isLessThan(e)&&h.isGreaterThan(d))return-1}return n.isGreaterThan(o)?1:n.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++ue,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 n,o,r;const a=re.comparePoints(e,i);if(a<0)n=e,o=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);n=i,o=e,r=-1}const l=new re(n,!0),c=new re(o,!1);return new t(l,c,[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 Xt.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=Jt(e,i);if(null===s)return null;const n=this.leftSE.point,o=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=Zt(e,r)&&0===this.comparePoint(r),c=Zt(i,n)&&0===t.comparePoint(n),h=Zt(e,a)&&0===this.comparePoint(a),d=Zt(i,o)&&0===t.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?a:null;if(c)return h&&n.x.eq(a.x)&&n.y.eq(a.y)?null:n;if(l)return d&&o.x.eq(r.x)&&o.y.eq(r.y)?null:r;if(d&&h)return null;if(d)return o;if(h)return a;const p=((t,e,i,s)=>{if(e.x.isZero())return oe(i,s,t.x);if(s.x.isZero())return oe(t,e,i.x);if(e.y.isZero())return ne(i,s,t.y);if(s.y.isZero())return ne(t,e,i.y);const n=Qt(e,s);if(n.isZero())return null;const o={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=Qt(o,e).div(n),a=Qt(o,s).div(n),l=t.x.plus(a.times(e.x)),c=i.x.plus(r.times(s.x)),h=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(n,this.vector(),r,t.vector());return null===p?null:Zt(s,p)?Xt.snap(p):null}split(e){const i=[],s=void 0!==e.events,n=new re(e,!0),o=new re(e,!1),r=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(n);const a=new t(n,r,this.rings.slice(),this.windings.slice());return re.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),re.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(n.checkForConsuming(),o.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}},ge=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=Xt.snap({x:new Ot(t[0][0]),y:new Ot(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let n=s;for(let e=1,i=t.length;epe.run("union",t,e),be=(t,...e)=>pe.run("intersection",t,e),ye=(t,...e)=>pe.run("difference",t,e);function we(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function ke(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 $e(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 xe(t,e,i=1){const s=t[0].toFixed(i),n=t[1].toFixed(i),o=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 Me(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 Ce(t){const e=[],i=new Set;for(const s of t||[]){const t=Se(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),o=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(o,Math.min(i-o,a));const l=a/i;n={x:e+l*c,y:r+l*h,angle:t}}else n={x:u[0],y:u[1],angle:t}}}return n}function Te(t,e,i,s,n,o=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,c=o;for(const e of Ce(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])>o||s(i[1])>o)continue;const n=(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],h=Math.min(n,r),d=Math.max(n,r),p=h>0?h:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],m=(h+d)/2;return{wallA:g(h),wallB:g(d),sideA:u,sideB:_,midA:g((h-p)/2),midB:g((p+d)/2),wallCenter:g(m),centered:Math.abs(m)<=n}}function ze(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}function Pe(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}function Re(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;let r=o?((t[0]-e[0])*s+(t[1]-e[1])*n)/o:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*n))}function Ee(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let n=0;nn&&r<-n||o<-n&&r>n)&&(a>n&&l<-n||a<-n&&l>n)}function Le(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1;let c=0,h=0,d=0;for(let e=0;e1e-9?[h/(3*c),d/(3*c)]:[(n+o)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!Ae([e,i],t))return-1/0;let s=1/0;for(let n=0;ng&&(g=c,_=[s,l])}if(_){const[t,i]=_,s=(o-n)/e,l=(a-r)/e;for(let e=-4;e<=4;e++)for(let n=-4;n<=4;n++){const o=t+s*e/4,r=i+l*n/4,a=u(o,r);a>g&&(g=a,_=[o,r])}}return _||He(t)||t[0]}function He(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(Oe(s,t,e))return s;for(let s=0;s[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ve(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=ve(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}function Ge(t,e,i){for(let s=0;s1&&Pe(i[0],i[i.length-1],e)&&i.pop(),i}function Ye(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function Xe(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 Ze(t,e,i,s){if(t.length<2)return;const n=e.x+s,o=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}function ai(t){return"opening"===t||"closing"===t}const li=["automation","script","scene"];function ci(t,e,i){const s=String(e??"").trim();if(!s)return null;const n=t?.states?.[s];if(!n)return null;const o=String(i??"").trim(),r=t=>t.slice(0,60);if(o){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)}(n.attributes?.[o]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,n,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=n.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function hi(t,e,i){const s=String(e??"").trim(),n=String(i??"").trim()||s;if(!n)return t.text;const o=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`${o} ${n}`}function di(t,e,i){const s=t??"";if(!(e?.entity||"").trim())return s;const n=function(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],n=s?.state;if(!s||null==n||""===n||"unavailable"===n||"unknown"===n)return"—";const o=(e?.attr||"").trim(),r=ci(t,i,o||null);return null===r?"—":hi(r,o?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}(i,e),o=s.indexOf("{}");return o>=0?s.slice(0,o)+n+s.slice(o+2):s?`${s} ${n}`:n}function pi(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 ui(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}function _i(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 gi="#55606c",mi=.55;function fi(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:gi,opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):mi,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 vi(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 bi={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}},yi=/^#[0-9a-f]{6}$/i;function wi(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(bi)){const s=bi[t],n=i[t];e[t]={c:n&&"string"==typeof n.c&&yi.test(n.c)?n.c:s.c,a:n&&"number"==typeof n.a?Math.min(1,Math.max(0,n.a)):s.a}}return e}function ki(t,e,i){const s=Math.min(1,Math.max(0,i)),n=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),o=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=n.map((t,e)=>Math.round(t+(o[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function $i(t,e,i,s,n,o,r){if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:ki(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(n,o),e=Math.max(n,o);return se?r.temp_hot:r.temp_ok}return null}const xi={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"]},Si=[["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 Mi(t){for(const e of[...Object.values(xi),...Si])if(t===e[0]||t===e[1])return e;return null}function Ci(t,e,i,s,n){if(!s||"unavailable"===s||"unknown"===s)return t;if(n){const i="cover"===e?Mi(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=xi[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const n=Mi(t);return n?"closed"===s?n[0]:n[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 Di(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}function Ti(t,e){if(!t||"on"!==t.state)return null;const i=t.attributes||{},s=Number(i.brightness),n=Number.isFinite(s)&&s>0?Math.max(.15,Math.min(1,s/255)):1,o=i.rgb_color;if(Array.isArray(o)&&o.length>=3&&o.every(t=>Number.isFinite(t)))return{c:`rgb(${o[0]}, ${o[1]}, ${o[2]})`,bri:n};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),n=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,o=t=>Math.round(Math.min(255,Math.max(0,t)));return[o(i),o(s),o(n)]}(r);return{c:`rgb(${t}, ${e}, ${i})`,bri:n}}return{c:e,bri:n}}function zi(t,e,i,s,n=170){const o=Math.hypot(e[0]-t[0],e[1]-t[1]),r=Math.hypot(i[0]-t[0],i[1]-t[1]);if(o<1e-6||r<1e-6||Math.min(o,r)>=s)return null;let a=Math.atan2(e[1]-t[1],e[0]-t[0]),l=Math.atan2(i[1]-t[1],i[0]-t[0])-a;for(;l>Math.PI;)l-=2*Math.PI;for(;l<-Math.PI;)l+=2*Math.PI;const c=n*Math.PI/180;if(Math.abs(l)>c){const t=a+l/2;l=c*Math.sign(l),a=t-l/2}const h=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=a+l*e/8;h.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return h}function Pi(t,e,i,s,n){const o=e*Math.PI/180,r=[-Math.sin(o),Math.cos(o)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*n*a,t[1]+r[1]*n*a];return s.some(t=>Oe(l,t,1e-9))}function Ai(t){return t.startsWith("light.")||t.startsWith("switch.")}function Ri(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let n=0;np||l>p)continue;const u=(n[0]-o[0])*h+(n[1]-o[1])*d,_=(r[0]-o[0])*h+(r[1]-o[1])*d,g=Math.max(0,Math.min(u,_)),m=Math.min(c,Math.max(u,_));m-g>i&&s.push([o[0]+h*g,o[1]+d*g,o[0]+h*m,o[1]+d*m])}}return s}function Ei(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let n=!0;for(;n;){n=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const o of e)if(o.id&&i.has(o.id)&&s(t,o)){i.add(t.id),n=!0;break}}return i}function Ni(t,e,i=1e-6){const s=[];for(const n of t){const t=[n[0],n[1]],o=[n[2],n[3]],r=o[0]-t[0],a=o[1]-t[1],l=Math.hypot(r,a);if(lo||n>o)continue;const r=(s[0]-t[0])*c+(s[1]-t[1])*h,a=(s[2]-t[0])*c+(s[3]-t[1])*h,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],o[0],o[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,n]of d)e-p>i&&s.push([t[0]+c*p,t[1]+h*p,t[0]+c*e,t[1]+h*e]),p=Math.max(p,n);l-p>i&&s.push([t[0]+c*p,t[1]+h*p,o[0],o[1]])}return s}const Oi=864e5,Ii=576e5;function Fi(t){const e=new Set,i=t=>{if("string"!=typeof t||!t)return;const i=Li(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 Li(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 Hi(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i?i:t}function qi(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Ui(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Wi(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Bi(t,e){const i=e[2]-e[0],s=e[3]-e[1],n=i*i+s*s;if(!n)return Math.hypot(t[0]-e[0],t[1]-e[1]);let o=((t[0]-e[0])*i+(t[1]-e[1])*s)/n;return o=Math.max(0,Math.min(1,o)),Math.hypot(t[0]-(e[0]+o*i),t[1]-(e[1]+o*s))}const ji=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Vi=(t,e)=>[t[0]-e[0],t[1]-e[1]],Gi=(t,e)=>[t[0]+e[0],t[1]+e[1]],Ki=(t,e)=>t[0]*e[0]+t[1]*e[1],Yi=t=>Math.hypot(t[0],t[1]);function Xi(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function Qi(t,e,i,s){const n=s||Ji(t,e),o=(e+1)%t.length;return t.map((t,s)=>s===e||s===o?[t[0]+n[0]*i,t[1]+n[1]*i]:[...t])}function ts(t,e,i,s){const n=[],o=Vi(i,e),r=Yi(o);if(rs||h>s)continue;const d=Ki(Vi(o,e),a),p=Ki(Vi(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&n.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return n}function es(t,e,i,s,n){const o=ts(t,e,i,n);if(!o.length)return null;const r=t=>o.some(([e,i])=>Zi(t,e,i)<=n),a=t.length,l=[];for(let o=0;on||f>n)continue;const v=Ki(Vi(e,c),u),b=Ki(Vi(i,c),u),y=Math.max(0,Math.min(v,b)),w=Math.min(p,Math.max(v,b));if(!(w-y<=n)){if(y>n&&yn&&wYi(Vi(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const n=i[(s-1+i.length)%i.length],o=i[(s+1)%i.length],r=(t[0]-n[0])*(o[1]-n[1])-(t[1]-n[1])*(o[0]-n[0]),a=Yi(Vi(o,n))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function ss(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-n[0])*a[1]-(t[1]-n[1])*a[0],c=t=>(t[0]-n[0])*a[0]+(t[1]-n[1])*a[1],h=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const n=c(e);n<=h||n>=d||tMath.abs(r+(t-p)/m*(a-r));s=Math.min(s,f(_),f(g))}}return s}function os(t,e,i){if(We(t,e,i))return!0;if(Ue(t,e,i)||Ue(e,t,i))return!1;let s=0;try{const i=be([[...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+=Be(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function rs(t,e,i,s,n){const o=[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]=Qi(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[Gi(i.a,o),Gi(i.b,o)]];continue}const t=ts(e.poly,i.a,i.b,n);if(!t.length)continue;const a=es(e.poly,i.a,i.b,o,n);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[Gi(t,o),Gi(e,o)]))}for(const t of e)Zi([t.x,t.y],i.a,i.b)<=n&&(r.openings[t.id]=[t.x+o[0],t.y+o[1]]);return r}function as(t,e,i){for(const s of e)for(let e=0;ei)continue;const c=(t.x-n[0])*l[0]+(t.y-n[1])*l[1];if(c-t.length/2>=-i&&c+t.length/2<=a+i)return!0}return!1}function ls(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),n=a.polys[e];if(!ss(n))return!1;const l=Xi(s.poly),h=Xi(n);if(Math.abs(h)t.find(t=>t.id===e).poly),d=t.map(c);for(const t of ls(e,h,2*r)){const e=a.openings[t.id];if(!as(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function hs(t,e,i,s,n,o){const r=t=>[s[0]+(t[0]-s[0])*n,s[1]+(t[1]-s[1])*n],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 c=t.filter(t=>t.id!==i).map(t=>t.poly),h=hs(l,e,c,s,n,2*a),d=h.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 n=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;n.length>=2&&i(n[n.length-2],n[n.length-1],s)<=0;)n.pop();n.push(s)}return s.pop(),n.pop(),s.concat(n)}(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 ls(e,[l.poly],2*a)){const e=h.openings[t.id];if(!as(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function ps(t,e,i){const s=i/e;return Be(t)*s*s/1e4}function us(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const _s=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],gs=t=>Math.min(1,Math.max(0,t));function ms(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=_s[_s.length-1][1];for(let t=1;t<_s.length;t++){const[s,n]=_s[t-1],[o,r]=_s[t];if(e<=o){i=ki(n,r,(e-s)/(o-s));break}}return{bg:i,planDim:.1*gs((10-e)/16),warmth:e<0?1:gs(1-e/10)}}function fs(t,e,i=6){const s=t.angle*Math.PI/180,n=[Math.sin(s),-Math.cos(s)],o=s=>{const o=[t.x+n[0]*i*s,t.y+n[1]*i*s];return e.find(t=>t.poly.length>=3&&Ae(o,t.poly))||null},r=o(1),a=o(-1);return r&&a?null:r||a?r?{normal:[-n[0],-n[1]],roomId:r.id}:{normal:n,roomId:a.id}:null}function vs(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function bs(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 ys(t,e){try{const i=be([[...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 ws(t,e,i,s,n,o,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,n),l=[-a[0],-a[1]],c=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),h=[];for(const i of e){if(!(i.length>0))continue;const e=fs(i,t);if(!e||!vs(e.normal,a,s))continue;const n=t.find(t=>t.id===e.roomId);if(!n)continue;const d=o&&o[e.roomId]||n.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],g=Math.max(0,r?.[i.id]||0),m=i.x+_[0]*g/2,f=i.y+_[1]*g/2,v=Math.cos(p)*u,b=Math.sin(p)*u,y=[m-v,f-b],w=[m+v,f+b],k=c*i.length,$=ys(bs(y,w,l,k),d);if(!$.length)continue;const x=l[0]*_[0]+l[1]*_[1];h.push({openingId:i.id,roomId:e.roomId,polys:$,a:y,b:w,dir:l,len:k,normal:_,depth:k*x})}return h}function ks(t){return Math.round(10*(Number(t)||0))/10}const $s={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 xs=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Ss(t,e){const i=xs(e?.north_deg);return null!==i?i:xs(t?.north_deg)}function Ms(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function Cs(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Ds(t){const e=t?.weather_entity;return"string"==typeof e&&e.trim()?e.trim():null}function Ts(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 zs=1e3;function Ps(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,n=s>=1?e:e*s,o=s>=1?e/s:e;return{x:(e-n)/2,y:(e-o)/2,w:n,h:o}}const As=.01;function Rs(t,e=1e3){const i=Ps(t?.plan_aspect,e),s=Number(t?.plan_scale),n=Number.isFinite(s)&&s>0?Math.min(100,Math.max(As,s)):1,o=Number(t?.plan_x),r=Number(t?.plan_y);return{x:i.x+(Number.isFinite(o)?Bs(o):0)*e,y:i.y+(Number.isFinite(r)?Bs(r):0)*e,w:i.w*n,h:i.h*n}}function Es(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 Ns(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=zs,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]*zs,i[1]*e,i[2]*zs,i[3]*e],bg:t.plan_url?{href:Li(t.plan_url),...Rs(t,zs)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...Es(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*zs:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*zs:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*zs,t[1]*e]):void 0}})}}):[]}const Os=5e3,Is=Os*zs,Fs=240,Ls=zs/Fs,Hs=1/Fs;function qs(t){if(!Number.isFinite(t))return t;const e=Math.round(t*Fs/zs)*zs/Fs;return Math.abs(e-t)<=1e-9*Ls?t:e}function Us(t){return{x:qs(t.x),y:qs(t.y)}}function Ws(t){return Number.isFinite(t)?Math.min(Is,Math.max(-Is,t)):0}function Bs(t){return Number.isFinite(t)?Math.min(Os,Math.max(-Os,t)):0}const js=1/3,Vs=200;function Gs(t){let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t){const t=Number(o[0]),r=Number(o[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>n&&(n=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:n}}function Ks(t){return t.poly&&t.poly.length?Gs(t.poly):null==t.x||null==t.y?null:Gs([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function Ys(t,e){const i=[];for(const e of t.rooms||[]){const t=Ks(e);t&&i.push(t)}t.bg&&i.push({minX:t.bg.x,minY:t.bg.y,maxX:t.bg.x+t.bg.w,maxY:t.bg.y+t.bg.h});for(const t of e||[])if(Array.isArray(t)){const e=Gs([t]);e&&i.push(e)}else i.push(t);return i}const Xs=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},Zs=t=>{let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t)o.minXs&&(s=o.maxX),o.maxY>n&&(n=o.maxY);return e>s||i>n?null:{x:e,y:i,w:s-e,h:n-i}};function Js(t,e){let{x:i,y:s,w:n,h:o}=t;n<30&&(i=i+n/2-100,n=Vs),o<30&&(s=s+o/2-100,o=Vs);const r=Math.max(n,o)*e;return{x:i-r,y:s-r,w:n+2*r,h:o+2*r}}function Qs(t,e={}){const i=e.pad??.05,s=e.k??10,n=e.minSpread??50,o=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=Is&&Math.abs(t.maxX)<=Is&&Math.abs(t.minY)<=Is&&Math.abs(t.maxY)<=Is);if(!o.length)return{core:null,all:null,outliers:0};const r=Zs(o);if(o.length<4){const t=Js(r,i);return{core:t,all:t,outliers:0}}const a=o.map(t=>(t.minX+t.maxX)/2),l=o.map(t=>(t.minY+t.maxY)/2),c=Xs(a),h=Xs(l),d=o.map((t,e)=>Math.max(Math.abs(a[e]-c),Math.abs(l[e]-h))),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),n),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,g=_&&_<=o.length*js?o.filter((t,e)=>!u[e]):o;return{core:Js(Zs(g)||r,i),all:Js(r,i),outliers:g===o?0:_}}function tn(t,e,i=.05){const s=Qs(Ys(t,e),{pad:i});if(s.core)return s.core;const n=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,zs,zs];return{x:n[0],y:n[1],w:n[2],h:n[3]}}function en(t){const e=tn(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function sn(t){const e=[];for(const i of t.rooms||[]){const t=Ks(i);t&&e.push(t)}const i=Qs(e,{pad:0}).core;return i?Math.max(zs,Math.min(Is,Math.max(i.w,i.h))):zs}function nn(t,e,i,s=1){const n=Number(i),o=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(n)||n<=0?t*o:t*sn(e)*o/n}const on=[1,2,5,10,20,50,100,200,500,1e3];function rn(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),n=Math.min(...i);return{x:s,y:n,w:Math.max(...e)-s,h:Math.max(...i)-n}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function an(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 ln=["furniture","appliance","sanitary","other"],cn=[{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]]}],hn=new Map(cn.map(t=>[t.id,t]));function dn(t){return t&&hn.get(t)||null}function pn(t,e,i=Ls,s=1e3){const n=Number(e)>0?Number(e):5;return(Number(t)||0)/n*i/s}const un=5e-4,_n=Os;function gn(t){return Number.isFinite(t)?Math.max(un,Math.min(_n,t)):un}const mn=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function fn(t,e,i){const s=dn(t);if(!(s&&e>0&&i>0))return"";const n=t=>mn(t*e),o=t=>mn(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}H${n(e+s)}V${o(i+a)}H${n(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}L${n(s)} ${o(a)}`)}else if("e"===t[0]){const[,s,a,l,c]=t;r.push(`M${n(s-l)} ${o(a)}A${mn(l*e)} ${mn(c*i)} 0 0 1 ${n(s+l)} ${o(a)}A${mn(l*e)} ${mn(c*i)} 0 0 1 ${n(s-l)} ${o(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${n(e[0])} ${o(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,n,o=0){let r=null,a=n;for(const n of s){const[s,l,c,h]=n,d=c-s,p=h-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let g=((t-s)*d+(e-l)*p)/u;g=Math.max(0,Math.min(1,g));let m=s+g*d,f=l+g*p;const v=Math.hypot(t-m,e-f);if(!(v0){const t=Math.round(g*_/o)*o;m=s+t/_*d,f=l+t/_*p}r={cx:m+b*(i/2),cy:f+y*(i/2),angle:vn(180*Math.atan2(-b,y)/Math.PI),dist:v}}return r}function yn(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function wn(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function kn(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function $n(t,e){const i="number"==typeof t?t:parseFloat(String(t).trim().replace(",","."));if(!Number.isFinite(i)||i<=0)return null;return wn(e?2.54*i:i)}function xn(t,e,i){const s=Number(e)>0?Number(e):5;return wn(t)/s*i}function Sn(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function Mn(t,e){let i=e[0]-t[0],s=e[1]-t[1];const n=Math.hypot(i,s);return n<1e-12?[1,0]:(i/=n,s/=n,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function Cn(t,e,i){const s=Sn((t[0]+e[0])/2,i),n=Sn((t[1]+e[1])/2,i),[o,r]=Mn(t,e);let a=Math.atan2(r,o);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,c=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(c)},${n.toFixed(c)}@${l.toFixed(4)}`}function Dn(t,e,i,s){return 1===s?Cn(t,e,i):Cn([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function Tn(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[n,o]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[n,o,r].every(Number.isFinite)&&s.push({w:e,x:n*i,y:o*i,ang:r})}return s}function zn(t,e){const[i,s]=Mn(t,e);let n=Math.atan2(s,i);return n<0&&(n+=Math.PI),n}function Pn(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function An(t,e,i,s,n=1){if(!t?.length)return null;const o=Dn(e,i,s,n),r=t.find(t=>t.key===o);if(r)return r;const a=n>0?n:1,l=(e[0]+i[0])/2,c=(e[1]+i[1])/2,h=zn(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of Tn(t,a))if(Pn(e.ang,h)&&Math.hypot(e.x-l,e.y-c)<=d)return e.w;return null}function Rn(t,e,i,s,n=1){const o=An(t,e,i,s,n);return o&&o.cm>0?wn(o.cm):0}function En(t,e,i,s=1,n=[]){if(!t?.length)return[];const o=new Set;for(const t of Ce(e))o.add(Dn([t[0],t[1]],[t[2],t[3]],i,s));const r=e||[],a=Math.max(i*s*.02,1e-9);for(let t=0;to.has(t.key)&&t.cm>=1&&t.cm<=100)}function Nn(t,e,i,s=8){const[n,o]=Mn(t,e);let r=Math.atan2(o,n);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 On(t,e,i,s,n,o=1){const r=Dn(e,i,n,o),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,{key:r,cm:wn(s)}]}function In(t,e,i,s,n,o=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,n=1){const o=Gn(t,e,i,s,n);if(!o)return[];const r=[];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),n=Bn(i,t);if(s&&n)return`${jn(s)} ${jn(Vn(n))}`}}let s="";for(let i=0;i.05)&&Jn(r,a,t[0],t[1],t[2],t[3])<=o)return!0}return!1}function Hn(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function qn(t,e){const i=t[e],s=t[(e+1)%t.length],n=s[0]-i[0],o=s[1]-i[1],r=Math.hypot(n,o)||1;let a=-o/r,l=n/r;const c=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,n=e.length-1;st[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r+0)+o&&(i=!i)}return i}([c[0]+.001*a,c[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function Un(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 Wn(t,e,i,s){const n=e[0]*s[1]-e[1]*s[0];if(Math.abs(n)<1e-12)return null;const o=[i[0]-t[0],i[1]-t[1]],r=(o[0]*s[1]-o[1]*s[0])/n;return[t[0]+r*e[0],t[1]+r*e[1]]}function Bn(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 n=0;n0||d>0)){s.push([t[n][0],t[n][1]]);continue}if(Un(y,w)){const e=t[n],i=[e[0]+p*h,e[1]+u*h],o=[e[0]+_*d,e[1]+g*d];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=Wn(k,y,$,w),S=Math.max(h,d,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}h>0&&s.push([t[n][0]+p*h,t[n][1]+u*h]),d>0&&s.push([t[n][0]+_*d,t[n][1]+g*d]),h>0||d>0||s.push([t[n][0],t[n][1]])}return s.length>=3?s:null}function jn(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),r=Se(o);if(!r||r.length<3)return null;const a=Hn(s,n),l=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Se(i);if(t)for(const e of Ri(r,t,a))l.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])l.push([t[0],t[1]],[t[2],t[3]]);const c=[],h=[];for(let t=0;ta)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=n||r>=1-n||(o.some(t=>Math.abs(t-r)*s<=2*a)||o.push(r))}o.sort((t,e)=>t-e);for(const s of o)c.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),h.push(t)}return{poly:c,parent:h,orig:r}}function Kn(t,e,i,s){const n=e.poly.length,o=new Array(n).fill(0);if(!t?.length)return o;const r=new Set,a=[];for(let l=0;l0?(o[l]=wn(c.cm),r.add(c.key)):a.push(l)}if(!a.length)return o;const l=s>0?s:1,c=Math.max(.5*i,1e-9)*l,h=Tn(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],n=e.orig[(t+1)%e.orig.length],a=zn(s,n),l=(s[0]+n[0])/2,d=(s[1]+n[1])/2;let p=null;for(const t of h){if(r.has(t.w.key))continue;if(!Pn(t.ang,a))continue;if(Jn(t.x,t.y,s[0],s[1],n[0],n[1])>c)continue;const e=Math.hypot(t.x-l,t.y-d);(!p||et?.id===e),n=Se(s);if(!n)return[];const o=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Se(s);if(t)for(const e of Ri(n,t,i))o.push(e)}return o}(t,e,Hn(n,a)),h=function(t,e,i,s,n){const o=Hn(s,n),r=[];for(let a=0;aJn(h,d,t[0],t[1],t[2],t[3])<=o);r.push(p?"shared":"outer")}return r}(l.poly,c,s,n,a),d=Kn(i,l,n,a),p=d.map((t,e)=>h[e]&&t>0?xn(t,o,r)/2:0);return{...l,kinds:h,cms:d,offsets:p}}function Xn(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Yn(t,l.id,e,i,s,n,o,r);if(c)for(let t=0;t4*l||(!p||tt?.id===e),c=Se(l);if(!c||c.length<3)return null;if(!i?.length)return c.map(t=>[t[0],t[1]]);const h=Yn(t,e,i,s,n,o,r,a);return h&&h.offsets.some(t=>t>0)&&Bn(h.poly,h.offsets)||c.map(t=>[t[0],t[1]])}function to(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=[],n,o,r,a=1){if(!e?.length)return null;const l=[],c=[];let h=0;for(const s of t||[]){if(!s?.id)continue;const d=Yn(t,s.id,e,i,n,o,r,a);if(!d||d.poly.length<3||!d.offsets.some(t=>t>0))continue;for(const t of d.offsets)t>0&&(h=Math.max(h,2*t));const p=io(d.poly,d.offsets),u=Bn(d.poly,d.offsets);p&&l.push(p),u&&c.push(u)}if(!l.length)return null;try{let t=to(l[0]);for(let e=1;e0))continue;const i=t.angle*Math.PI/180,s=Math.cos(i),o=Math.sin(i),r=-o,l=s,c=t.length/2,d=1.25*Math.max(h,n*a),p=[[t.x-s*c-r*d,t.y-o*c-l*d],[t.x+s*c-r*d,t.y+o*c-l*d],[t.x+s*c+r*d,t.y+o*c+l*d],[t.x-s*c+r*d,t.y-o*c+l*d]];e=ye(e,to(p))}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?" ":"")+jn(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:h}:null}catch{const s=function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Yn(t,l.id,e,i,s,n,o,r);if(!c||c.poly.length<3||!c.offsets.some(t=>t>0))continue;const h=io(c.poly,c.offsets),d=Bn(c.poly,c.offsets);if(!h||!d)continue;const p=`${jn(h)} ${jn(Vn(d))}`;let u="",_="outer",g=0,m=0;for(let t=0;t0){u=Dn(c.poly[t],c.poly[(t+1)%c.poly.length],s,r),_=c.kinds[t]||"outer",g=c.cms[t],m=xn(g,n,o);break}a.push({d:p,key:u,kind:_,cm:g,depthUnits:m})}return a}(t,e,i,n,o,r,a);return s.length?{d:s.map(t=>t.d).join(" "),depthUnits:h}: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]]);Vn(t),e.slice().reverse();const s=[];for(let n=0;n0||a>0)){s.push([t[n][0],t[n][1]]);continue}if(Un(y,w)){const e=t[n],i=[e[0]-l*r,e[1]-c*r],o=[e[0]-h*a,e[1]-d*a];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=Wn(k,y,$,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[n][0]-l*r,t[n][1]-c*r]),a>0&&s.push([t[n][0]-h*a,t[n][1]-d*a])}return s.length>=3?s:null}function so(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){const c=Se(l);if(c&&c.length>=3){const h=Yn(t,l.id,e,i,s,n,o,r),d=h&&h.offsets.some(t=>t>0)?io(h.poly,h.offsets):null,p=d||c;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 no(t,e,i,s,n,o,r=1){let a=null;for(const i of t||[]){const t=Se(i);if(t)for(let s=0;ss*r)return{ox:0,oy:0,cm:0};const l=Rn(i,a.a,a.b,s,r);if(!(l>0))return{ox:0,oy:0,cm:0};const c=xn(l,n,o),h=Se(a.room),[d,p]=qn(h,a.edge),u=e.flip_v?-1:1,_=c/2;return{ox:d*_*u,oy:p*_*u,cm:l}}function oo(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function ro(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function ao(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function lo(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!ao(t.a)||!ao(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],n=[Number(t.b[0]),Number(t.b[1])];Math.hypot(n[0]-s[0],n[1]-s[1])<.001||e.push({a:s,b:n})}return e}function co(t,e){const i=e[0],s=e[1],n=e[2]-i,o=e[3]-s,r=n*n+o*o;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*n+(t[1]-s)*o)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*n,s+a*o];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function ho(t,e){return co(t,e).q}function po(t,e,i,s,n){const o=co(t,e).q;let r=o,a=1/0;for(const t of i){const e=Math.hypot(o[0]-t[0],o[1]-t[1]);e<=n&&e0?s:1,g=Math.round(u/_)*_,m=Math.max(0,Math.min(p,g))/p;return[l+h*m,c+d*m]}function uo(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt?.id);let o=null;for(let e=0;et.seg)}(e,s);let o=null;for(const r of e||[]){if(!r?.id)continue;const e=Se(r);if(e)for(let a=0;ai)continue;let p=!1;for(const t of n)if(Bi([(l[0]+c[0])/2,(l[1]+c[1])/2],t)<2*s){p=!0;break}p||(!o||dt?.id),r=lo(e);if(r.length)return r.map(t=>ro(t,i));if(!n)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;too([t[0],t[1]],[t[2],t[3]],e))}function vo(t,e,i){let s=null;for(const n of e){const e=Bi(t,n);e<=i&&(!s||e.05)continue;const d=Rn(t,[e[0],e[1]],[e[2],e[3]],s,n);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-h[0],p[1]-h[1]);u0?l:o}(t,e,i,s,n,o);return On(t,[e[0],e[1]],[e[2],e[3]],r,s,n)}function yo(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],n=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const o of e)if(!(Bi(n,o)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Bi([o[0],o[1]],t)>i))for(const e of[[o[0],o[1]],[o[2],o[3]]])Bi(e,t)<=2*i&&s.push(e);return s}class wo{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=Li(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],n=s?this.now()-s.at:1/0;return n{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 n=0;for(const o of i){const i=t?.urls?.[o];"string"==typeof i&&i?(s[o]={url:i,at:e},this.retry.delete(o),n++):this.backOff(o)}n&&(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,n]of Object.entries(this.signed))e.has(t)&&i-n.at{const e=Number(t);return Number.isFinite(e)?e:null};function So(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=xo(e.x)&&null!=xo(e.y)?{x:xo(e.x),y:xo(e.y),a:xo(e.a??e.angle??e.theta)}:null;let s=null;const n=t.path?.points??t.path;if(Array.isArray(n)&&n.length){s=[];for(const t of n){const e=xo(Array.isArray(t)?t[0]:t?.x),i=xo(Array.isArray(t)?t[1]:t?.y);null!=e&&null!=i&&s.push([e,i])}s.length||(s=null)}const o=[],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=xo(e.cx??e.center?.x),n=xo(e.cy??e.center?.y);if(null==s||null==n){const t=xo(e.x0),i=xo(e.y0),o=xo(e.x1),r=xo(e.y1);null!=t&&null!=i&&null!=o&&null!=r&&(s=(t+o)/2,n=(i+r)/2)}if(null!=s&&null!=n||(s=xo(e.x),n=xo(e.y)),i&&null!=s&&null!=n){const r={id:t,name:i,cx:s,cy:n},a=xo(e.x0),l=xo(e.y0),c=xo(e.x1),h=xo(e.y1);null!=a&&null!=l&&null!=c&&null!=h&&(r.x0=Math.min(a,c),r.y0=Math.min(l,h),r.x1=Math.max(a,c),r.y1=Math.max(l,h)),o.push(r)}}const l=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||o.length||s?{pos:i,path:s,rooms:o,mapId:l}:null}function Mo(t){const e=t?.attributes;return!(!e||!e.vacuum_position&&!e.robot_position)}const Co=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function Do(t,e){const i=new Map(e.map(t=>[Co(t.name),t])),s=[],n=[];for(const e of t){const t=i.get(Co(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),n.push(e.name))}if(s.length<3)return null;const o=function(t){if(t.length<3)return null;let e=0,i=0,s=0,n=0,o=0,r=0,a=0,l=0,c=0,h=0,d=0,p=0;for(const[[u,_],[g,m]]of t){if(![u,_,g,m].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,n+=_*_,o+=_,r+=1,a+=u*g,l+=_*g,c+=g,h+=u*m,d+=_*m,p+=m}const u=[e,i,s,i,n,o,s,o,r],_=t=>{const[e,i,s,n,o,r,a,l,c]=u,h=e*(o*c-r*l)-i*(n*c-r*a)+s*(n*l-o*a);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-r*l)/h,(s*l-i*c)/h,(i*r-s*o)/h,(r*a-n*c)/h,(e*c-s*a)/h,(s*n-e*r)/h,(n*l-o*a)/h,(i*a-e*l)/h,(e*o-i*n)/h];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]]},g=_([a,l,c]),m=_([h,d,p]);if(!g||!m)return null;const f=[g[0],g[1],g[2],m[0],m[1],m[2]];return f.every(Number.isFinite)?f:null}(s);return o?{matrix:o,matched:n,residual:$o(o,s)}:null}function To(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 n=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[n,o]=s.pop(),[r,a]=t[n],[l,c]=t[o],h=l-r,d=c-a,p=Math.hypot(h,d)||1e-9;let u=0,_=-1;for(let e=n+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([n,_],[_,o]))}const n=[];for(let e=0;e600&&(n=n.filter((t,e)=>e%2==0||e===n.length-1)),n}function zo(t){return"cleaning"===t||"returning"===t||"on"===t}const Po={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function Ao(t){const[e,i]=Po[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 Ro(t,e,i,s){const[n,o]=ko(Ao(e),i,s),r=Ao({...t,ox:0,oy:0}),[a,l]=ko(r,i,s);return{...t,ox:n-a,oy:o-l}}function Eo(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}function No(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 Oo(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 Io(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)}function Fo(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),n=[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 n){const i=e.find(e=>Io(t,e.eid));if(i)return i.eid}for(const t of n)for(const e of ft){const i=t.find(t=>t.eid.split(".")[0]===e);if(i)return i.eid}for(const t of n)if(t.length)return t[0].eid}function Lo(t,e){const i=!0===e.marker?.is_light?[...e.marker?.controls||[],...e.entities]:e.entities.filter(t=>t.startsWith("light."));return i.find(e=>"on"===t.states[e]?.state)||null}function Ho(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const n=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===n){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const o=e.attributes?.linkquality??e.attributes?.lqi;if(null!=o){const t=parseFloat(o);isNaN(t)||i.push(t)}}return Ye(i)}function qo(t,e){for(const i of e){if(!Io(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 Uo(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 Wo(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 Bo(t,e){for(const i of e){if(!Wo(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 jo(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden)continue;let n=null;if("group"===s.platform)n=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if("Group"!==e?.model)continue;n=e.area_id||s.area_id||null}}if(!n)continue;const o=t.states[e];i.push({eid:e,name:s.name||o?.attributes?.friendly_name||e,area:n})}return i}function Vo(t,e,i,s,n){const o=mt(e,i,n);if(o!==gt)return o;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=_t[e];if(t)return t}return null}(r)??gt}function Go(t,e){t.marker=e,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 Ko(t){const{hass:e,areaToSpace:i,markers:s,settings:n,excluded:o,showAll:r,firstSpaceId:a,loc:l,iconRules:c}=t,h=!1!==n.group_lights,d=jo(e,h),p=new Set(d.map(t=>t.area)),u=No(e),_=new Set;for(const t of s){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||_.add(t.binding)}const g=(t,e)=>s.find(i=>i.binding===t+":"+e),m={},f=[];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(_.has("device:"+t.id))continue;const a=g("device",t.id);if(a&&a.hidden&&!n.filter_seeded)continue;const d=u[t.id]||[],v=Oo(e,t,d),b=!n.filter_seeded;if(b&&!r){if(o.has(v))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===v&&t.via_device_id)continue}const y=(t.name_by_user||t.name||l("device.unnamed")).trim(),w=y+"|"+s;let k=Vo(e,y,t.model,d,c);if(d.some(t=>t.startsWith("lock."))&&(k="mdi:lock"),b&&!r&&h&&"mdi:lightbulb"===k&&p.has(s))continue;m[w]=(m[w]||0)+1;const $=m[w]>1?y+" "+m[w]:y,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=Fo(e,d,k),"mdi:thermometer"!==k&&"mdi:air-filter"!==k||(x.temp=qo(e,d)),x.primary&&Wo(e,x.primary)&&(x.hum=Bo(e,d)),f.push(x)}for(const t of d)i[t.area]&&(_.has("entity:"+t.eid)||f.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.hidden&&!n.filter_seeded)continue;const[s,o]=t.binding.split(":");if("device"===s){const s=e.devices[o],n=t.area||s?.area_id||"",r=n&&i[n]||t.space||a,h=s&&u[s.id]||[];let d=s?Vo(e,s.name_by_user||s.name||"",s.model,h,c):"mdi:help-circle";h.some(t=>t.startsWith("lock."))&&(d="mdi:lock");const p={id:t.id,name:s?.name_by_user||s?.name||l("device.fallback"),model:s?.model||"",area:n,space:r,icon:d,entities:h,bindingKind:"device",bindingRef:o};p.primary=Fo(e,h,d),"mdi:thermometer"!==d&&"mdi:air-filter"!==d||(p.temp=qo(e,h)),p.primary&&Wo(e,p.primary)&&(p.hum=Bo(e,h)),p.primary&&Wo(e,p.primary)&&(p.hum=Bo(e,h)),Go(p,t),f.push(p)}else if("entity"===s){const s=e.entities[o],n=t.area||s?.area_id||s?.device_id&&e.devices[s.device_id]?.area_id||"",r=n&&i[n]||t.space||a,l=e.states[o],h=s?.name||l?.attributes?.friendly_name||o;let d=Vo(e,h,"",[o],c);o.startsWith("lock.")&&(d="mdi:lock");const p={id:t.id,name:h,model:"",area:n,space:r,icon:d,entities:[o],primary:o,bindingKind:"entity",bindingRef:o};"mdi:thermometer"!==d&&"mdi:air-filter"!==d||(p.temp=qo(e,[o])),Wo(e,o)&&(p.hum=Bo(e,[o])),Go(p,t),f.push(p)}else{const e=t.area||"",s=t.space||e&&i[e]||a,n={id:t.id,name:t.name||l("device.virtual"),model:t.model||"",area:e,space:s,icon:t.icon||"mdi:map-marker",entities:[],bindingKind:"virtual",virtual:!0};Go(n,t),f.push(n)}}return f}function Yo(t,e,i){let s=!1;for(const n of e)if(n.area===i&&!n.hidden)for(const e of n.entities)if(e.startsWith("light.")&&(s=!0,"on"===t.states[e]?.state))return"on";return s?"off":"none"}function Xo(t,e,i){if(!e)return null;const s=e.indexOf(":");if(s<0)return null;const n=e.slice(0,s),o=e.slice(s+1);if(!o)return null;if("entity"===n){const e=parseFloat(t.states[o]?.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===o).map(([t])=>t);return"temp"===i?qo(t,e):Bo(t,e)}return null}const Zo=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 Jo={"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.remove":"Remove","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":"Add a room: connect grid dots with lines until the outline closes","title.markup_merge":"Merge rooms: 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: click a virtual wall to make it solid; a shared solid wall merges the two rooms; an outer wall or inside a room deletes the room (with confirm).","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":"Add","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","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} · Esc/Ctrl+Z — undo a dot · close the outline by clicking the first one","markup.hint_start":"click a grid dot to start the outline","tip.lqi":"average zigbee signal:","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 badge","display.ripple":"Ripple only","display.icon_ripple":"Icon + ripple","marker.ripple_size":"Ripple 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":'Remove "{name}" from the plan?',"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.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 removed 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","editor.aspect_ratio":"Aspect ratio (e.g. 16:9 or auto)","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.color":"Color","decor.width":"Line width","decor.w_thin":"Thin","decor.w_mid":"Medium","decor.w_thick":"Thick","decor.fill":"Fill","decor.text_title":"Text label","decor.text_label":"Text","decor.text_hint":"A new line here is a new line on the plan; the block is centred. Drag a corner to resize it, the handle above it to turn it.","decor.text_hint_live":"{} is where the value lands. Without it the value is added at the end. A new line here is a new line on the plan.","decor.live_group":"Live value","decor.live_hint":"The label may show one entity's value — as Home Assistant reports it, with no rounding. A dead entity shows a dash.","decor.live_entity":"Entity","decor.live_entity_ph":"none — a plain label","decor.live_attr":"Attribute","decor.live_attr_ph":"the state","decor.live_unit":"Unit","decor.live_preview":"Preview","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag the picture to move it, pull a corner to resize it. Shift — off the grid.","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)","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 light sources","marker.controls_hint":"With tap action “Toggle”, a click flips all bound lights at once (any on → all off). The icon mirrors their state.","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":"Wall thickness","title.markup_wallthick":"Wall 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 for new room walls. Empty or zero — thin walls. 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","markup.openwall":"Open boundary","title.markup_openwall":"Open boundary — click a point on a shared wall, then a second point on the same wall (within the nearest corners) to make that stretch virtual. Click a dashed stretch to close it. 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.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 device from plan","marker.hide_tip":'The device is not drawn on the plan but still counts toward the room signal. To see it: the "Show hidden" button in the device editor.',"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":"Grid","gs.grid_hint":"Everything you draw or drag now lands on the grid. Hold Shift to place something between the nodes on purpose.","gs.align_all":"Align everything to the grid","gs.align_title":"Align everything to the grid","gs.align_none":"Everything is already on the grid — nothing to move.","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_warn":"This rewrites the plan and the device positions in one go. There is no undo: the only way back is to move things by hand. If some elements are deliberately between the nodes, cancel.","gs.align_run":"Align","gs.align_done":"Aligned to the grid: {n} elements moved","decor.furniture":"Furniture","furn.title":"Furniture library","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 Qo={en:Jo,ru:{"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.remove":"Убрать","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":"Добавить комнату: соединяйте точки сетки линиями до замкнутого контура","title.markup_merge":"Объединить комнаты: клик по одной, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить: виртуальная стена → обычная; общая глухая → слияние комнат; внешняя или клик внутрь → удалить комнату (с подтверждением)","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":"Проём","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} · Esc/Ctrl+Z — убрать точку · замкните контур кликом по первой","markup.hint_start":"кликните точку сетки, чтобы начать контур","tip.lqi":"средний сигнал zigbee:","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.ripple":"Только пульсация","display.icon_ripple":"Значок + пульсация","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.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.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":"Путь дашборда (куда вести)","editor.aspect_ratio":"Соотношение сторон (напр. 16:9 или auto)","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.color":"Цвет","decor.width":"Толщина линии","decor.w_thin":"Тонкая","decor.w_mid":"Средняя","decor.w_thick":"Толстая","decor.fill":"Залить","decor.text_title":"Надпись","decor.text_label":"Текст","decor.text_hint":"Перевод строки здесь — перевод строки на плане; блок выравнивается по центру. Тяните угол — меняет размер, ручка сверху — поворот.","decor.text_hint_live":"{} — место, куда встанет значение. Без него значение добавится в конец. Перевод строки здесь — перевод строки на плане.","decor.live_group":"Живое значение","decor.live_hint":"Надпись может показывать значение одной сущности — так, как его отдаёт Home Assistant, без округления. Мёртвая сущность — прочерк.","decor.live_entity":"Сущность","decor.live_entity_ph":"нет — обычная надпись","decor.live_attr":"Атрибут","decor.live_attr_ph":"состояние","decor.live_unit":"Единица","decor.live_preview":"Предпросмотр","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — двигается, тяните угол — меняет размер. Shift — мимо сетки.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","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":"Толщина стен","title.markup_wallthick":"Толщина стен — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина стен новой комнаты. Пустое или ноль — без толщины. Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","markup.openwall":"Открытая граница","title.markup_openwall":"Открытая граница — клик по точке на общей стене, затем второй клик на той же стене (не дальше ближайших углов) открывает отрезок. Клик по пунктиру закрывает. Курсор — крестик.","toast.openwall_pick":"Кликните по стене, разделяющей две комнаты","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.openwall_opened_span":"Виртуальный отрезок открыт","toast.openwall_closed_span":"Виртуальный отрезок закрыт","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":"Устройство не отображается на плане, но участвует в расчёте сигнала комнаты. Показать: кнопка «Показать скрытые» в редакторе устройств.","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":"Всё, что вы рисуете и перетаскиваете, ложится строго на сетку. Shift — временно поставить элемент между узлами.","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_warn":"Действие разом перезапишет план и позиции устройств. Отмены нет: вернуть можно только руками. Если какие-то элементы стоят между узлами намеренно — отмените.","gs.align_run":"Выровнять","gs.align_done":"Выровнено по сетке: сдвинуто элементов — {n}","decor.furniture":"Мебель","furn.title":"Библиотека мебели","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 tr(t,e){if(e&&e in Qo)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function er(t,e,i){return _i(Qo[t][e]??Jo[e]??e,i)}class ir 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 tr(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:er(e,"editor.lang_auto")},{value:"en",label:er(e,"editor.lang_en")},{value:"ru",label:er(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 V;this._loadSpaces();const t=this._lang,e={title:er(t,"editor.title"),default_floor:er(t,"editor.default_floor"),language:er(t,"editor.language"),icon_size:er(t,"editor.icon_size"),show_temperature:er(t,"editor.show_temperature"),live_states:er(t,"editor.live_states"),show_signal:er(t,"editor.show_signal"),kiosk:er(t,"editor.kiosk"),cycle:er(t,"editor.cycle")};return W`{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,g=_?_.emptyScript:"",m=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?g: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}},b=(t,e)=>!a(t,e),y={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:b};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=y){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??y}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=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??b)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,m?.({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,T=`<${D}>`,z=document,P=()=>z.createComment(""),A=t=>null===t||"object"!=typeof t&&"function"!=typeof t,R=Array.isArray,E="[ \t\n\f\r]",N=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,O=/-->/g,I=/>/g,F=RegExp(`>|${E}(?:([^\\s"'>=/]+)(${E}*=${E}*(?:[^ \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"),V=Symbol.for("lit-nothing"),G=new WeakMap,K=z.createTreeWalker(z,129);function Y(t,e){if(!R(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const X=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",r=N;for(let e=0;e"===l[0]?(r=n??N,c=-1):void 0===l[1]?c=-2:(c=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===O||r===I?r=N:(r=F,n=void 0);const d=r===F&&t[e+1].startsWith("/>")?" ":"";o+=r===N?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,o+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const r=t.length-1,a=this.parts,[l,c]=X(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iR(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!==V&&A(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=G.get(t.strings);return void 0===e&&G.set(t.strings,e=new Z(t)),e}k(t){R(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(P()),this.O(P()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=V}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=J(this,t,e,0),o=!A(t)||t!==this._$AH&&t!==j,o&&(this._$AH=t);else{const s=t;let r,a;for(t=n[0],r=0;r{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(P(),t),t,void 0,i??{})}return n._$AI(t),n})(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 ct=at.litElementPolyfillSupport;ct?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ht=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),dt=[{pattern:"протечк|leak|water sensor",icon:"mdi:water-alert"},{pattern:"клапан|valve",icon:"mdi:pipe-valve"},{pattern:"дым|smoke",icon:"mdi:smoke-detector"},{pattern:"термоголов|trv|radiator",icon:"mdi:radiator"},{pattern:"чайник|kettle|термопот",icon:"mdi:kettle"},{pattern:"сауна|sauna|harvia|парная|парилк",icon:"mdi:hot-tub"},{pattern:"температ|temperature|climate sensor",icon:"mdi:thermometer"},{pattern:"qingping|air monitor|молекул|air quality",icon:"mdi:air-filter"},{pattern:"штор|curtain|blind|shade",icon:"mdi:roller-shade"},{pattern:"розетк|plug|socket|outlet",icon:"mdi:power-socket-de"},{pattern:"выключат|switch",icon:"mdi:light-switch"},{pattern:"лампа|лампочк|bulb|gx53|светильник|rgb|lamp|light strip",icon:"mdi:lightbulb"},{pattern:"камер|camera",icon:"mdi:cctv"},{pattern:"замок|ttlock|lock|sn609|sn9161",icon:"mdi:lock"},{pattern:"ворота|garage|gate",icon:"mdi:garage-variant"},{pattern:"калитк|door|открыт|contact",icon:"mdi:door"},{pattern:"счётчик|счетчик|kws|meter",icon:"mdi:meter-electric"},{pattern:"вводный автомат|breaker|wifimcbn",icon:"mdi:electric-switch"},{pattern:"myheat|котёл|котел|boiler|отоплен|heating",icon:"mdi:water-boiler"},{pattern:"холодильник|fridge",icon:"mdi:fridge"},{pattern:"стиральн|washer|washing",icon:"mdi:washing-machine"},{pattern:"сушилк|dryer",icon:"mdi:tumble-dryer"},{pattern:"пылесос|vacuum|dreame|roborock",icon:"mdi:robot-vacuum"},{pattern:"soundbar",icon:"mdi:soundbar"},{pattern:"колонк|станц|speaker|яндекс|yandex|алиса|alice",icon:"mdi:speaker"},{pattern:"tv|телевизор|hyundaitv|mitv|television",icon:"mdi:television"},{pattern:"keenetic|роутер|router|mesh|access point",icon:"mdi:router-wireless"},{pattern:"ибп|ups|kirpich",icon:"mdi:battery-charging-high"},{pattern:"slzb|координат|zigbee|coordinator",icon:"mdi:zigbee"},{pattern:"motion|движен|presence|присутств",icon:"mdi:motion-sensor"},{pattern:"humidity|влажн",icon:"mdi:water-percent"}];function pt(t){const e=[];for(const i of t)if(i&&"string"==typeof i.pattern&&i.icon)try{e.push({re:new RegExp(i.pattern,"i"),icon:i.icon})}catch{}return e}const ut=pt(dt),_t={temperature:"mdi:thermometer",humidity:"mdi:water-percent",motion:"mdi:motion-sensor",occupancy:"mdi:motion-sensor",door:"mdi:door",window:"mdi:window-closed",garage_door:"mdi:garage-variant",smoke:"mdi:smoke-detector",moisture:"mdi:water-alert",gas:"mdi:gas-cylinder",power:"mdi:meter-electric",energy:"mdi:meter-electric",illuminance:"mdi:brightness-5",co2:"mdi:molecule-co2",pm25:"mdi:air-filter",battery:"mdi:battery"},gt="mdi:chip";function mt(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??ut)if(t.test(s))return e;return gt}const ft=["light","switch","cover","valve","lock","climate","fan","media_player","camera","vacuum","humidifier","water_heater","alarm_control_panel","sensor","binary_sensor","event","button","number","select","update"];var vt=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,bt=Math.ceil,yt=Math.floor,wt="[BigNumber Error] ",kt=wt+"Number primitive has more than 15 significant digits: ",$t=1e14,xt=14,St=9007199254740991,Mt=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],Ct=1e7,Dt=1e9;function Tt(t){var e=0|t;return t>0||t===e?e:e-1}function zt(t){for(var e,i,s=1,n=t.length,o=t[0]+"";sc^i?1:-1;for(a=(l=n.length)<(c=o.length)?l:c,r=0;ro[r]^i?1:-1;return l==c?0:l>c^i?1:-1}function At(t,e,i,s){if(ti||t!==yt(t))throw Error(wt+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Rt(t){var e=t.c.length-1;return Tt(t.e/xt)==e&&t.c[e]%2!=0}function Et(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Nt(t,e,i){var s,n;if(e<0){for(n=i+".";++e;n+=i);t=n+t}else if(++e>(s=t.length)){for(n=i,e-=s;--e;n+=i);t+=n}else eb?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>b?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!vt.test(d=String(t)))return n(p,d,c);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(At(e,2,x.length,"Base"),10==e&&S)return z(p=new M(t),_+p.e+1,g);if(d=String(t),c="number"==typeof t){if(0*t!=0)return n(p,d,c,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(kt+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,h=d.length;la){a=h;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return n(p,String(t),c,e)}c=!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(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(t>St||t!==yt(t)))throw Error(kt+p.s*t);if((a=a-l-1)>b)p.c=p.e=null;else if(a=f)?Et(l,r):Nt(l,r,"0");else if(o=(t=z(new M(t),e,i)).e,a=(l=zt(t.c)).length,1==s||2==s&&(e<=o||o<=m)){for(;ar),l=Nt(l,o,"0"),o+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=o-a)>0)for(o+1==a&&(l+=".");e--;l+="0");return t.s<0&&n?"-"+l:l}function D(t,e){for(var i,s,n=1,o=new M(t[0]);n=10;n/=10,s++);return(i=s+i*xt-1)>b?t.c=t.e=null:i=10;a/=10,n++);if((o=e-n)<0)o+=xt,r=e,l=d[c=0],h=yt(l/p[n-r-1]%10);else if((c=bt((o+1)/xt))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,n=1,r=(o%=xt)-xt+1}else{for(l=a=d[c],n=1;a>=10;a/=10,n++);h=(r=(o%=xt)-xt+n)<0?0:yt(l/p[n-r-1]%10)}if(s=s||e<0||null!=d[c+1]||(r<0?l:l%p[n-r-1]),s=i<4?(h||s)&&(0==i||i==(t.s<0?3:2)):h>5||5==h&&(4==i||s||6==i&&(o>0?r>0?l/p[n-r]:0:d[c-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[(xt-e%xt)%xt],t.e=-e||0):d[0]=t.e=0,t;if(0==o?(d.length=c,a=1,c--):(d.length=c+1,a=p[xt-o],d[c]=r>0?yt(l/p[n-r]%p[r])*a:0),s)for(;;){if(0==c){for(o=1,r=d[0];r>=10;r/=10,o++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);o!=a&&(t.e++,d[0]==$t&&(d[0]=1));break}if(d[c]+=a,d[c]!=$t)break;d[c--]=0,a=1}for(o=d.length;0===d[--o];d.pop());}t.e>b?t.c=t.e=null:t.e=f?Et(e,i):Nt(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(wt+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(At(i=t[e],0,Dt,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(At(i=t[e],0,8,e),g=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(At(i[0],-Dt,0,e),At(i[1],0,Dt,e),m=i[0],f=i[1]):(At(i,-Dt,Dt,e),m=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)At(i[0],-Dt,-1,e),At(i[1],1,Dt,e),v=i[0],b=i[1];else{if(At(i,-Dt,Dt,e),!i)throw Error(wt+e+" cannot be zero: "+i);v=-(b=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(wt+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw y=!i,Error(wt+"crypto unavailable");y=i}else y=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(At(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(At(i=t[e],0,Dt,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(wt+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(wt+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:g,EXPONENTIAL_AT:[m,f],RANGE:[v,b],CRYPTO:y,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,n=t.e,o=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===o||-1===o)&&n>=-Dt&&n<=Dt&&n===yt(n)){if(0===s[0]){if(0===n&&1===s.length)return!0;break t}if((e=(n+1)%xt)<1&&(e+=xt),String(s[0]).length==e){for(e=0;e=$t||i!==yt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===n&&(null===o||1===o||-1===o))return!0;throw Error(wt+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(o=9007199254740992,r=Math.random()*o&2097151?function(){return yt(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,n,o,a=0,l=[],c=new M(u);if(null==t?t=_:At(t,0,Dt),n=bt(t/xt),y)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(n*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(o%1e14),a+=2);a=n/2}else{if(!crypto.randomBytes)throw y=!1,Error(wt+"crypto unavailable");for(e=crypto.randomBytes(n*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(o%1e14),a+=7);a=n/7}if(!y)for(;a=10;o/=10,a++);ai-1&&(null==r[n+1]&&(r[n+1]=0),r[n+1]+=r[n]/i|0,r[n]%=i)}return r.reverse()}return function(s,n,o,r,a){var l,c,h,d,p,u,m,f,v=s.indexOf("."),b=_,y=g;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(n)).pow(s.length-v),k=d,f.c=e(Nt(zt(u.c),u.e,"0"),10,o,t),f.e=f.c.length),h=d=(m=e(s,n,o,a?(l=x,t):(l=t,x))).length;0==m[--d];m.pop());if(!m[0])return l.charAt(0);if(v<0?--h:(u.c=m,u.e=h,u.s=r,m=(u=i(u,f,b,y,o)).c,p=u.r,h=u.e),v=m[c=h+b+1],d=o/2,p=p||c<0||null!=m[c+1],p=y<4?(null!=v||p)&&(0==y||y==(u.s<0?3:2)):v>d||v==d&&(4==y||p||6==y&&1&m[c-1]||y==(u.s<0?8:7)),c<1||!m[0])s=p?Nt(l.charAt(1),-b,l.charAt(0)):l.charAt(0);else{if(m.length=c,p)for(--o;++m[--c]>o;)m[c]=0,c||(++h,m=[1].concat(m));for(d=m.length;!m[--d];);for(v=0,s="";v<=d;s+=l.charAt(m[v++]));s=Nt(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,n,o,r,a=0,l=t.length,c=e%Ct,h=e/Ct|0;for(t=t.slice();l--;)a=((n=c*(o=t[l]%Ct)+(s=h*o+(r=t[l]/Ct|0)*c)%Ct*Ct+a)/i|0)+(s/Ct|0)+h*r,t[l]=n%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var n,o;if(i!=s)o=i>s?1:-1;else for(n=o=0;ne[n]?1:-1;break}return o}function i(t,e,i,s){for(var n=0;i--;)t[i]-=n,n=t[i]1;t.splice(0,1));}return function(s,n,o,r,a){var l,c,h,d,p,u,_,g,m,f,v,b,y,w,k,$,x,S=s.s==n.s?1:-1,C=s.c,D=n.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&n.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(m=(g=new M(S)).c=[],S=o+(c=s.e-n.e)+1,a||(a=$t,c=Tt(s.e/xt)-Tt(n.e/xt),S=S/xt|0),h=0;D[h]==(C[h]||0);h++);if(D[h]>(C[h]||0)&&c--,S<0)m.push(1),d=!0;else{for(w=C.length,$=D.length,h=0,S+=2,(p=yt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),$=D.length,w=C.length),y=$,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(b=f[0],$!=v&&(b=b*a+(f[1]||0)),(p=yt(b/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,h++);z(g,o+(g.e=h+c*xt-1)+1,r,d)}else g.e=c,g.r=+d;return g}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,n=function(t,e,i,s){var n,o=i?e:e.replace(d,"");if(h.test(o))t.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(a,function(t,e,i){return n="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=n?t:e}),s&&(n=s,o=o.replace(l,"$1").replace(c,"0.$1")),e!=o))return new M(o,n);if(M.DEBUG)throw Error(wt+"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 Pt(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,n,o=this;if(null!=t)return At(t,0,Dt),null==e?e=g:At(e,0,8),z(new M(o),t+o.e+1,e);if(!(i=o.c))return null;if(s=((n=i.length-1)-Tt(this.e/xt))*xt,n=i[n])for(;n%10==0;n/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,g)},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,n,o,r,a,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(wt+"Exponent not an integer: "+P(t));if(null!=e&&(e=new M(e)),r=t.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!t.c||!t.c[0])return c=new M(Math.pow(+P(h),r?t.s*(2-Rt(t)):+P(t))),e?c.mod(e):c;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&h.isInteger()&&e.isInteger())&&(h=h.mod(e))}else{if(t.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||r&&h.c[1]>=24e7:h.c[0]<8e13||r&&h.c[0]<=9999975e7)))return o=h.s<0&&Rt(t)?-0:0,h.e>-1&&(o=1/o),new M(a?1/o:o);k&&(o=bt(k/xt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Rt(t)):l=(n=Math.abs(+P(t)))%2,c=new M(u);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):s&&(c=c.mod(e))}if(n){if(0===(n=yt(n/2)))break;l=n%2}else if(z(t=t.times(i),t.e+1,1),t.e>14)l=Rt(t);else{if(0===(n=+P(t)))break;l=n%2}h=h.times(h),o?h.c&&h.c.length>o&&(h.c.length=o):s&&(h=h.mod(e))}return s?c:(a&&(c=u.div(c)),e?c.mod(e):o?z(c,k,g,void 0):c)},p.integerValue=function(t){var e=new M(this);return null==t?t=g:At(t,0,8),z(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===Pt(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return Pt(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=Pt(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&Tt(this.e/xt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return Pt(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=Pt(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,n,o,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/xt,c=t.e/xt,h=r.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?r:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?r:3==g?-0:0)}if(l=Tt(l),c=Tt(c),h=h.slice(),a=l-c){for((o=a<0)?(a=-a,n=h):(c=l,n=d),n.reverse(),e=a;e--;n.push(0));n.reverse()}else for(s=(o=(a=h.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;h[i++]=0);for(e=$t-1;s>a;){if(h[--s]=0;){for(i=0,p=b[n]%m,u=b[n]/m|0,o=n+(r=l);o>n;)i=((c=p*(c=v[--r]%m)+(a=u*c+(h=v[r]/m|0)*p)%m*m+_[o]+i)/g|0)+(a/m|0)+u*h,_[o--]=c%g;_[o]=i}return i?++s:_.splice(0,1),T(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,n=s.s;if(e=(t=new M(t,e)).s,!n||!e)return new M(NaN);if(n!=e)return t.s=-e,s.minus(t);var o=s.e/xt,r=t.e/xt,a=s.c,l=t.c;if(!o||!r){if(!a||!l)return new M(n/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*n)}if(o=Tt(o),r=Tt(r),a=a.slice(),n=o-r){for(n>0?(r=o,i=l):(n=-n,i=a),i.reverse();n--;i.push(0));i.reverse()}for((n=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=n),n=0;e;)n=(a[--e]=a[e]+l[e]+n)/$t|0,a[e]=$t===a[e]?0:a[e]%$t;return n&&(a=[n].concat(a),++r),T(t,a,r)},p.precision=p.sd=function(t,e){var i,s,n,o=this;if(null!=t&&t!==!!t)return At(t,1,Dt),null==e?e=g:At(e,0,8),z(new M(o),t,e);if(!(i=o.c))return null;if(s=(n=i.length-1)*xt+1,n=i[n]){for(;n%10==0;n/=10,s--);for(n=i[0];n>=10;n/=10,s++);}return t&&o.e+1>s&&(s=o.e+1),s},p.shiftedBy=function(t){return At(t,-9007199254740991,St),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,n,o,r=this,a=r.c,l=r.s,c=r.e,h=_+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(+P(r)))||l==1/0?(((e=zt(a)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=Tt((c+1)/2)-(c<0||c%2),s=new M(e=l==1/0?"5e"+c:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+c)):s=new M(l+""),s.c[0])for((l=(c=s.e)+h)<3&&(l=0);;)if(o=s,s=d.times(o.plus(i(r,o,h,1))),zt(o.c).slice(0,l)===(e=zt(s.c)).slice(0,l)){if(s.e0&&_>0){for(o=_%a||a,h=u.substr(0,o);o<_;o+=a)h+=c+u.substr(o,a);l>0&&(h+=c+u.slice(o)),p&&(h="-"+h)}s=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,n,o,r,a,l,c,h,d,p,_,m=this,f=m.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(wt+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+P(l));if(!f)return new M(m);for(e=new M(u),h=s=new M(u),n=c=new M(u),_=zt(f),r=e.e=_.length-m.e-1,e.c[0]=Mt[(a=r%xt)<0?xt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:h:l,a=b,b=1/0,l=new M(_),c.c[0]=0;d=i(l,e,0,1),1!=(o=s.plus(d.times(n))).comparedTo(t);)s=n,n=o,h=c.plus(d.times(o=h)),c=o,e=l.minus(d.times(o=e)),l=o;return o=i(t.minus(s),n,0,1),c=c.plus(o.times(h)),s=s.plus(o.times(n)),c.s=h.s=m.s,p=i(h,n,r*=2,g).minus(m).abs().comparedTo(i(c,s,r,g).minus(m).abs())<1?[h,n]:[c,s],b=a,p},p.toNumber=function(){return+P(this)},p.toPrecision=function(t,e){return null!=t&&At(t,1,Dt),C(this,t,e,2)},p.toString=function(t){var e,i=this,n=i.s,o=i.e;return null===o?n?(e="Infinity",n<0&&(e="-"+e)):e="NaN":(null==t?e=o<=m||o>=f?Et(zt(i.c),o):Nt(zt(i.c),o,"0"):10===t&&S?e=Nt(zt((i=z(new M(i),_+o+1,g)).c),i.e,"0"):(At(t,2,x.length,"Base"),e=s(Nt(zt(i.c),o,"0"),10,t,n,!0)),n<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return P(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),It=class{key;left=null;right=null;constructor(t){this.key=t}},Ft=class extends It{constructor(t){super(t)}},Lt=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,n=null,o=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==n?o=r:n.right=r,n=r,r=e}}return null!=n&&(n.right=r.left,r.left=o),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)}}},qt=class t extends Lt{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 Ft(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ft(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 Ft(t.key);return function t(e,i){let s,n;do{if(s=e.left,n=e.right,null!=s){const e=new Ft(s.key);i.left=e,t(s,e)}if(null!=n){const t=new Ft(n.key);i.right=t,e=n,i=t}}while(null!=n)}(t,e),e}toSet(){return this.clone()}entries(){return new Wt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Ut(this.wrap())}[Symbol.toStringTag]="[object Set]"},Ht=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}},Ut=class extends Ht{getValue(t){return t.key}},Wt=class extends Ht{getValue(t){return[t.key,t.key]}},Bt=t=>()=>t,jt=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Bt(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Vt(t){const e=t?(e,i,s,n,o)=>e.exponentiatedBy(2).isLessThanOrEqualTo(n.minus(i).exponentiatedBy(2).plus(o.minus(s).exponentiatedBy(2)).times(t)):Bt(!1);return(t,i,s)=>{const n=t.x,o=t.y,r=s.x,a=s.y,l=o.minus(a).times(i.x.minus(r)).minus(n.minus(r).times(i.y.minus(a)));return e(l,n,o,r,a)?0:l.comparedTo(0)}}var Gt=t=>t,Kt=t=>{if(t){const e=new qt(jt(t)),i=new qt(jt(t)),s=(t,e)=>e.addAndReturn(t),n=t=>({x:s(t.x,e),y:s(t.y,i)});return n({x:new Ot(0),y:new Ot(0)}),n}return Gt},Yt=t=>({set:t=>{Xt=Yt(t)},reset:()=>Yt(t),compare:jt(t),snap:Kt(t),orient:Vt(t)}),Xt=Yt(),Zt=(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),Jt=(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}}},Qt=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),te=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ee=t=>te(t,t).sqrt(),ie=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return Qt(n,s).div(ee(n)).div(ee(s))},se=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return te(n,s).div(ee(n)).div(ee(s))},ne=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},oe=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},re=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:_e.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:ie(this.point,t.point,s.point),cosine:se(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:n,cosine:o}=e.get(t),{sine:r,cosine:a}=e.get(s);return n.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?o.isLessThan(a)?1:o.isGreaterThan(a)?-1:0:n.isLessThan(0)&&r.isLessThan(0)?o.isLessThan(a)?-1:o.isGreaterThan(a)?1:0:r.isLessThan(n)?-1:r.isGreaterThan(n)?1:0}}},ae=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,n=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}}},le=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&&n){const t=s.getIntersection(n);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(c))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(c))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(n.isLessThan(o)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(n.isGreaterThan(o)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!n.eq(o)){const t=l.minus(r),e=n.minus(i),h=c.minus(a),d=o.minus(s);if(t.isGreaterThan(e)&&h.isLessThan(d))return 1;if(t.isLessThan(e)&&h.isGreaterThan(d))return-1}return n.isGreaterThan(o)?1:n.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++ue,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 n,o,r;const a=re.comparePoints(e,i);if(a<0)n=e,o=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);n=i,o=e,r=-1}const l=new re(n,!0),c=new re(o,!1);return new t(l,c,[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 Xt.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=Jt(e,i);if(null===s)return null;const n=this.leftSE.point,o=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=Zt(e,r)&&0===this.comparePoint(r),c=Zt(i,n)&&0===t.comparePoint(n),h=Zt(e,a)&&0===this.comparePoint(a),d=Zt(i,o)&&0===t.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?a:null;if(c)return h&&n.x.eq(a.x)&&n.y.eq(a.y)?null:n;if(l)return d&&o.x.eq(r.x)&&o.y.eq(r.y)?null:r;if(d&&h)return null;if(d)return o;if(h)return a;const p=((t,e,i,s)=>{if(e.x.isZero())return oe(i,s,t.x);if(s.x.isZero())return oe(t,e,i.x);if(e.y.isZero())return ne(i,s,t.y);if(s.y.isZero())return ne(t,e,i.y);const n=Qt(e,s);if(n.isZero())return null;const o={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=Qt(o,e).div(n),a=Qt(o,s).div(n),l=t.x.plus(a.times(e.x)),c=i.x.plus(r.times(s.x)),h=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(n,this.vector(),r,t.vector());return null===p?null:Zt(s,p)?Xt.snap(p):null}split(e){const i=[],s=void 0!==e.events,n=new re(e,!0),o=new re(e,!1),r=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(n);const a=new t(n,r,this.rings.slice(),this.windings.slice());return re.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),re.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(n.checkForConsuming(),o.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}},ge=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=Xt.snap({x:new Ot(t[0][0]),y:new Ot(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let n=s;for(let e=1,i=t.length;epe.run("union",t,e),be=(t,...e)=>pe.run("intersection",t,e),ye=(t,...e)=>pe.run("difference",t,e);function we(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function ke(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 $e(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 xe(t,e,i=1){const s=t[0].toFixed(i),n=t[1].toFixed(i),o=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 Me(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 Ce(t){const e=[],i=new Set;for(const s of t||[]){const t=Se(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),o=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(o,Math.min(i-o,a));const l=a/i;n={x:e+l*c,y:r+l*h,angle:t}}else n={x:u[0],y:u[1],angle:t}}}return n}function Te(t,e,i,s,n,o=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,c=o;for(const e of Ce(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])>o||s(i[1])>o)continue;const n=(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],h=Math.min(n,r),d=Math.max(n,r),p=h>0?h:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],m=(h+d)/2;return{wallA:g(h),wallB:g(d),sideA:u,sideB:_,midA:g((h-p)/2),midB:g((p+d)/2),wallCenter:g(m),centered:Math.abs(m)<=n}}function ze(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}function Pe(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}function Re(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;let r=o?((t[0]-e[0])*s+(t[1]-e[1])*n)/o:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*n))}function Ee(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let n=0;nn&&r<-n||o<-n&&r>n)&&(a>n&&l<-n||a<-n&&l>n)}function Le(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1;let c=0,h=0,d=0;for(let e=0;e1e-9?[h/(3*c),d/(3*c)]:[(n+o)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!Ae([e,i],t))return-1/0;let s=1/0;for(let n=0;ng&&(g=c,_=[s,l])}if(_){const[t,i]=_,s=(o-n)/e,l=(a-r)/e;for(let e=-4;e<=4;e++)for(let n=-4;n<=4;n++){const o=t+s*e/4,r=i+l*n/4,a=u(o,r);a>g&&(g=a,_=[o,r])}}return _||qe(t)||t[0]}function qe(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(Oe(s,t,e))return s;for(let s=0;s[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ve(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=ve(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}function Ge(t,e,i){for(let s=0;s1&&Pe(i[0],i[i.length-1],e)&&i.pop(),i}function Ye(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function Xe(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 Ze(t,e,i,s){if(t.length<2)return;const n=e.x+s,o=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}function ai(t){return"opening"===t||"closing"===t}const li=["automation","script","scene"];function ci(t,e,i){const s=String(e??"").trim();if(!s)return null;const n=t?.states?.[s];if(!n)return null;const o=String(i??"").trim(),r=t=>t.slice(0,60);if(o){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)}(n.attributes?.[o]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,n,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=n.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function hi(t,e,i){const s=String(e??"").trim(),n=String(i??"").trim()||s;if(!n)return t.text;const o=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`${o} ${n}`}function di(t,e,i){const s=t??"";if(!(e?.entity||"").trim())return s;const n=function(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],n=s?.state;if(!s||null==n||""===n||"unavailable"===n||"unknown"===n)return"—";const o=(e?.attr||"").trim(),r=ci(t,i,o||null);return null===r?"—":hi(r,o?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}(i,e),o=s.indexOf("{}");return o>=0?s.slice(0,o)+n+s.slice(o+2):s?`${s} ${n}`:n}function pi(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 ui(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}function _i(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 gi="#55606c",mi=.55;function fi(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:gi,opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):mi,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 vi(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 bi={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}},yi=/^#[0-9a-f]{6}$/i;function wi(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(bi)){const s=bi[t],n=i[t];e[t]={c:n&&"string"==typeof n.c&&yi.test(n.c)?n.c:s.c,a:n&&"number"==typeof n.a?Math.min(1,Math.max(0,n.a)):s.a}}return e}function ki(t,e,i){const s=Math.min(1,Math.max(0,i)),n=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),o=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=n.map((t,e)=>Math.round(t+(o[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function $i(t,e,i,s,n,o,r){if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:ki(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(n,o),e=Math.max(n,o);return se?r.temp_hot:r.temp_ok}return null}const xi={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"]},Si=[["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 Mi(t){for(const e of[...Object.values(xi),...Si])if(t===e[0]||t===e[1])return e;return null}function Ci(t,e,i,s,n){if(!s||"unavailable"===s||"unknown"===s)return t;if(n){const i="cover"===e?Mi(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=xi[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const n=Mi(t);return n?"closed"===s?n[0]:n[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 Di(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}function Ti(t,e){if(!t||"on"!==t.state)return null;const i=t.attributes||{},s=Number(i.brightness),n=Number.isFinite(s)&&s>0?Math.max(.15,Math.min(1,s/255)):1,o=i.rgb_color;if(Array.isArray(o)&&o.length>=3&&o.every(t=>Number.isFinite(t)))return{c:`rgb(${o[0]}, ${o[1]}, ${o[2]})`,bri:n};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),n=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,o=t=>Math.round(Math.min(255,Math.max(0,t)));return[o(i),o(s),o(n)]}(r);return{c:`rgb(${t}, ${e}, ${i})`,bri:n}}return{c:e,bri:n}}function zi(t,e,i,s,n=170){const o=Math.hypot(e[0]-t[0],e[1]-t[1]),r=Math.hypot(i[0]-t[0],i[1]-t[1]);if(o<1e-6||r<1e-6||Math.min(o,r)>=s)return null;let a=Math.atan2(e[1]-t[1],e[0]-t[0]),l=Math.atan2(i[1]-t[1],i[0]-t[0])-a;for(;l>Math.PI;)l-=2*Math.PI;for(;l<-Math.PI;)l+=2*Math.PI;const c=n*Math.PI/180;if(Math.abs(l)>c){const t=a+l/2;l=c*Math.sign(l),a=t-l/2}const h=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=a+l*e/8;h.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return h}function Pi(t,e,i,s,n){const o=e*Math.PI/180,r=[-Math.sin(o),Math.cos(o)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*n*a,t[1]+r[1]*n*a];return s.some(t=>Oe(l,t,1e-9))}function Ai(t){return t.startsWith("light.")||t.startsWith("switch.")}function Ri(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let n=0;np||l>p)continue;const u=(n[0]-o[0])*h+(n[1]-o[1])*d,_=(r[0]-o[0])*h+(r[1]-o[1])*d,g=Math.max(0,Math.min(u,_)),m=Math.min(c,Math.max(u,_));m-g>i&&s.push([o[0]+h*g,o[1]+d*g,o[0]+h*m,o[1]+d*m])}}return s}function Ei(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let n=!0;for(;n;){n=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const o of e)if(o.id&&i.has(o.id)&&s(t,o)){i.add(t.id),n=!0;break}}return i}function Ni(t,e,i=1e-6){const s=[];for(const n of t){const t=[n[0],n[1]],o=[n[2],n[3]],r=o[0]-t[0],a=o[1]-t[1],l=Math.hypot(r,a);if(lo||n>o)continue;const r=(s[0]-t[0])*c+(s[1]-t[1])*h,a=(s[2]-t[0])*c+(s[3]-t[1])*h,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],o[0],o[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,n]of d)e-p>i&&s.push([t[0]+c*p,t[1]+h*p,t[0]+c*e,t[1]+h*e]),p=Math.max(p,n);l-p>i&&s.push([t[0]+c*p,t[1]+h*p,o[0],o[1]])}return s}const Oi=864e5,Ii=576e5;function Fi(t){const e=new Set,i=t=>{if("string"!=typeof t||!t)return;const i=Li(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 Li(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 qi(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i?i:t}function Hi(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Ui(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Wi(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Bi(t,e){const i=e[2]-e[0],s=e[3]-e[1],n=i*i+s*s;if(!n)return Math.hypot(t[0]-e[0],t[1]-e[1]);let o=((t[0]-e[0])*i+(t[1]-e[1])*s)/n;return o=Math.max(0,Math.min(1,o)),Math.hypot(t[0]-(e[0]+o*i),t[1]-(e[1]+o*s))}const ji=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Vi=(t,e)=>[t[0]-e[0],t[1]-e[1]],Gi=(t,e)=>[t[0]+e[0],t[1]+e[1]],Ki=(t,e)=>t[0]*e[0]+t[1]*e[1],Yi=t=>Math.hypot(t[0],t[1]);function Xi(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function Qi(t,e,i,s){const n=s||Ji(t,e),o=(e+1)%t.length;return t.map((t,s)=>s===e||s===o?[t[0]+n[0]*i,t[1]+n[1]*i]:[...t])}function ts(t,e,i,s){const n=[],o=Vi(i,e),r=Yi(o);if(rs||h>s)continue;const d=Ki(Vi(o,e),a),p=Ki(Vi(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&n.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return n}function es(t,e,i,s,n){const o=ts(t,e,i,n);if(!o.length)return null;const r=t=>o.some(([e,i])=>Zi(t,e,i)<=n),a=t.length,l=[];for(let o=0;on||f>n)continue;const v=Ki(Vi(e,c),u),b=Ki(Vi(i,c),u),y=Math.max(0,Math.min(v,b)),w=Math.min(p,Math.max(v,b));if(!(w-y<=n)){if(y>n&&yn&&wYi(Vi(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const n=i[(s-1+i.length)%i.length],o=i[(s+1)%i.length],r=(t[0]-n[0])*(o[1]-n[1])-(t[1]-n[1])*(o[0]-n[0]),a=Yi(Vi(o,n))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function ss(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-n[0])*a[1]-(t[1]-n[1])*a[0],c=t=>(t[0]-n[0])*a[0]+(t[1]-n[1])*a[1],h=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const n=c(e);n<=h||n>=d||tMath.abs(r+(t-p)/m*(a-r));s=Math.min(s,f(_),f(g))}}return s}function os(t,e,i){if(We(t,e,i))return!0;if(Ue(t,e,i)||Ue(e,t,i))return!1;let s=0;try{const i=be([[...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+=Be(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function rs(t,e,i,s,n){const o=[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]=Qi(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[Gi(i.a,o),Gi(i.b,o)]];continue}const t=ts(e.poly,i.a,i.b,n);if(!t.length)continue;const a=es(e.poly,i.a,i.b,o,n);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[Gi(t,o),Gi(e,o)]))}for(const t of e)Zi([t.x,t.y],i.a,i.b)<=n&&(r.openings[t.id]=[t.x+o[0],t.y+o[1]]);return r}function as(t,e,i){for(const s of e)for(let e=0;ei)continue;const c=(t.x-n[0])*l[0]+(t.y-n[1])*l[1];if(c-t.length/2>=-i&&c+t.length/2<=a+i)return!0}return!1}function ls(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),n=a.polys[e];if(!ss(n))return!1;const l=Xi(s.poly),h=Xi(n);if(Math.abs(h)t.find(t=>t.id===e).poly),d=t.map(c);for(const t of ls(e,h,2*r)){const e=a.openings[t.id];if(!as(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function hs(t,e,i,s,n,o){const r=t=>[s[0]+(t[0]-s[0])*n,s[1]+(t[1]-s[1])*n],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 c=t.filter(t=>t.id!==i).map(t=>t.poly),h=hs(l,e,c,s,n,2*a),d=h.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 n=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;n.length>=2&&i(n[n.length-2],n[n.length-1],s)<=0;)n.pop();n.push(s)}return s.pop(),n.pop(),s.concat(n)}(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 ls(e,[l.poly],2*a)){const e=h.openings[t.id];if(!as(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function ps(t,e,i){const s=i/e;return Be(t)*s*s/1e4}function us(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const _s=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],gs=t=>Math.min(1,Math.max(0,t));function ms(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=_s[_s.length-1][1];for(let t=1;t<_s.length;t++){const[s,n]=_s[t-1],[o,r]=_s[t];if(e<=o){i=ki(n,r,(e-s)/(o-s));break}}return{bg:i,planDim:.1*gs((10-e)/16),warmth:e<0?1:gs(1-e/10)}}function fs(t,e,i=6){const s=t.angle*Math.PI/180,n=[Math.sin(s),-Math.cos(s)],o=s=>{const o=[t.x+n[0]*i*s,t.y+n[1]*i*s];return e.find(t=>t.poly.length>=3&&Ae(o,t.poly))||null},r=o(1),a=o(-1);return r&&a?null:r||a?r?{normal:[-n[0],-n[1]],roomId:r.id}:{normal:n,roomId:a.id}:null}function vs(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function bs(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 ys(t,e){try{const i=be([[...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 ws(t,e,i,s,n,o,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,n),l=[-a[0],-a[1]],c=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),h=[];for(const i of e){if(!(i.length>0))continue;const e=fs(i,t);if(!e||!vs(e.normal,a,s))continue;const n=t.find(t=>t.id===e.roomId);if(!n)continue;const d=o&&o[e.roomId]||n.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],g=Math.max(0,r?.[i.id]||0),m=i.x+_[0]*g/2,f=i.y+_[1]*g/2,v=Math.cos(p)*u,b=Math.sin(p)*u,y=[m-v,f-b],w=[m+v,f+b],k=c*i.length,$=ys(bs(y,w,l,k),d);if(!$.length)continue;const x=l[0]*_[0]+l[1]*_[1];h.push({openingId:i.id,roomId:e.roomId,polys:$,a:y,b:w,dir:l,len:k,normal:_,depth:k*x})}return h}function ks(t){return Math.round(10*(Number(t)||0))/10}const $s={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 xs=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Ss(t,e){const i=xs(e?.north_deg);return null!==i?i:xs(t?.north_deg)}function Ms(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function Cs(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Ds(t){const e=t?.weather_entity;return"string"==typeof e&&e.trim()?e.trim():null}function Ts(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 zs=1e3;function Ps(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,n=s>=1?e:e*s,o=s>=1?e/s:e;return{x:(e-n)/2,y:(e-o)/2,w:n,h:o}}const As=.01;function Rs(t,e=1e3){const i=Ps(t?.plan_aspect,e),s=Number(t?.plan_scale),n=Number.isFinite(s)&&s>0?Math.min(100,Math.max(As,s)):1,o=Number(t?.plan_x),r=Number(t?.plan_y);return{x:i.x+(Number.isFinite(o)?Bs(o):0)*e,y:i.y+(Number.isFinite(r)?Bs(r):0)*e,w:i.w*n,h:i.h*n}}function Es(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 Ns(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=zs,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]*zs,i[1]*e,i[2]*zs,i[3]*e],bg:t.plan_url?{href:Li(t.plan_url),...Rs(t,zs)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...Es(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*zs:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*zs:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*zs,t[1]*e]):void 0}})}}):[]}const Os=5e3,Is=Os*zs,Fs=240,Ls=zs/Fs,qs=1/Fs;function Hs(t){if(!Number.isFinite(t))return t;const e=Math.round(t*Fs/zs)*zs/Fs;return Math.abs(e-t)<=1e-9*Ls?t:e}function Us(t){return{x:Hs(t.x),y:Hs(t.y)}}function Ws(t){return Number.isFinite(t)?Math.min(Is,Math.max(-Is,t)):0}function Bs(t){return Number.isFinite(t)?Math.min(Os,Math.max(-Os,t)):0}const js=1/3,Vs=200;function Gs(t){let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t){const t=Number(o[0]),r=Number(o[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>n&&(n=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:n}}function Ks(t){return t.poly&&t.poly.length?Gs(t.poly):null==t.x||null==t.y?null:Gs([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function Ys(t,e){const i=[];for(const e of t.rooms||[]){const t=Ks(e);t&&i.push(t)}t.bg&&i.push({minX:t.bg.x,minY:t.bg.y,maxX:t.bg.x+t.bg.w,maxY:t.bg.y+t.bg.h});for(const t of e||[])if(Array.isArray(t)){const e=Gs([t]);e&&i.push(e)}else i.push(t);return i}const Xs=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},Zs=t=>{let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t)o.minXs&&(s=o.maxX),o.maxY>n&&(n=o.maxY);return e>s||i>n?null:{x:e,y:i,w:s-e,h:n-i}};function Js(t,e){let{x:i,y:s,w:n,h:o}=t;n<30&&(i=i+n/2-100,n=Vs),o<30&&(s=s+o/2-100,o=Vs);const r=Math.max(n,o)*e;return{x:i-r,y:s-r,w:n+2*r,h:o+2*r}}function Qs(t,e={}){const i=e.pad??.05,s=e.k??10,n=e.minSpread??50,o=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=Is&&Math.abs(t.maxX)<=Is&&Math.abs(t.minY)<=Is&&Math.abs(t.maxY)<=Is);if(!o.length)return{core:null,all:null,outliers:0};const r=Zs(o);if(o.length<4){const t=Js(r,i);return{core:t,all:t,outliers:0}}const a=o.map(t=>(t.minX+t.maxX)/2),l=o.map(t=>(t.minY+t.maxY)/2),c=Xs(a),h=Xs(l),d=o.map((t,e)=>Math.max(Math.abs(a[e]-c),Math.abs(l[e]-h))),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),n),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,g=_&&_<=o.length*js?o.filter((t,e)=>!u[e]):o;return{core:Js(Zs(g)||r,i),all:Js(r,i),outliers:g===o?0:_}}function tn(t,e,i=.05){const s=Qs(Ys(t,e),{pad:i});if(s.core)return s.core;const n=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,zs,zs];return{x:n[0],y:n[1],w:n[2],h:n[3]}}function en(t){const e=tn(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function sn(t){const e=[];for(const i of t.rooms||[]){const t=Ks(i);t&&e.push(t)}const i=Qs(e,{pad:0}).core;return i?Math.max(zs,Math.min(Is,Math.max(i.w,i.h))):zs}function nn(t,e,i,s=1){const n=Number(i),o=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(n)||n<=0?t*o:t*sn(e)*o/n}const on=[1,2,5,10,20,50,100,200,500,1e3];function rn(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),n=Math.min(...i);return{x:s,y:n,w:Math.max(...e)-s,h:Math.max(...i)-n}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function an(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 ln=["furniture","appliance","sanitary","other"],cn=[{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]]}],hn=new Map(cn.map(t=>[t.id,t]));function dn(t){return t&&hn.get(t)||null}function pn(t,e,i=Ls,s=1e3){const n=Number(e)>0?Number(e):5;return(Number(t)||0)/n*i/s}const un=5e-4,_n=Os;function gn(t){return Number.isFinite(t)?Math.max(un,Math.min(_n,t)):un}const mn=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function fn(t,e,i){const s=dn(t);if(!(s&&e>0&&i>0))return"";const n=t=>mn(t*e),o=t=>mn(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}H${n(e+s)}V${o(i+a)}H${n(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}L${n(s)} ${o(a)}`)}else if("e"===t[0]){const[,s,a,l,c]=t;r.push(`M${n(s-l)} ${o(a)}A${mn(l*e)} ${mn(c*i)} 0 0 1 ${n(s+l)} ${o(a)}A${mn(l*e)} ${mn(c*i)} 0 0 1 ${n(s-l)} ${o(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${n(e[0])} ${o(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,n,o=0){let r=null,a=n;for(const n of s){const[s,l,c,h]=n,d=c-s,p=h-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let g=((t-s)*d+(e-l)*p)/u;g=Math.max(0,Math.min(1,g));let m=s+g*d,f=l+g*p;const v=Math.hypot(t-m,e-f);if(!(v0){const t=Math.round(g*_/o)*o;m=s+t/_*d,f=l+t/_*p}r={cx:m+b*(i/2),cy:f+y*(i/2),angle:vn(180*Math.atan2(-b,y)/Math.PI),dist:v}}return r}function yn(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function wn(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function kn(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function $n(t,e){const i="number"==typeof t?t:parseFloat(String(t).trim().replace(",","."));if(!Number.isFinite(i)||i<=0)return null;return wn(e?2.54*i:i)}function xn(t,e,i){const s=Number(e)>0?Number(e):5;return wn(t)/s*i}function Sn(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function Mn(t,e){let i=e[0]-t[0],s=e[1]-t[1];const n=Math.hypot(i,s);return n<1e-12?[1,0]:(i/=n,s/=n,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function Cn(t,e,i){const s=Sn((t[0]+e[0])/2,i),n=Sn((t[1]+e[1])/2,i),[o,r]=Mn(t,e);let a=Math.atan2(r,o);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,c=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(c)},${n.toFixed(c)}@${l.toFixed(4)}`}function Dn(t,e,i,s){return 1===s?Cn(t,e,i):Cn([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function Tn(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[n,o]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[n,o,r].every(Number.isFinite)&&s.push({w:e,x:n*i,y:o*i,ang:r})}return s}function zn(t,e){const[i,s]=Mn(t,e);let n=Math.atan2(s,i);return n<0&&(n+=Math.PI),n}function Pn(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function An(t,e,i,s,n=1){if(!t?.length)return null;const o=Dn(e,i,s,n),r=t.find(t=>t.key===o);if(r)return r;const a=n>0?n:1,l=(e[0]+i[0])/2,c=(e[1]+i[1])/2,h=zn(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of Tn(t,a))if(Pn(e.ang,h)&&Math.hypot(e.x-l,e.y-c)<=d)return e.w;return null}function Rn(t,e,i,s,n=1){const o=An(t,e,i,s,n);return o&&o.cm>0?wn(o.cm):0}function En(t,e,i,s=1,n=[]){if(!t?.length)return[];const o=new Set;for(const t of Ce(e))o.add(Dn([t[0],t[1]],[t[2],t[3]],i,s));const r=e||[],a=Math.max(i*s*.02,1e-9);for(let t=0;to.has(t.key)&&t.cm>=1&&t.cm<=100)}function Nn(t,e,i,s=8){const[n,o]=Mn(t,e);let r=Math.atan2(o,n);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 On(t,e,i,s,n,o=1){const r=Dn(e,i,n,o),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,{key:r,cm:wn(s)}]}function In(t,e,i,s,n,o=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,n=1){const o=Gn(t,e,i,s,n);if(!o)return[];const r=[];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),n=Bn(i,t);if(s&&n)return`${jn(s)} ${jn(Vn(n))}`}}let s="";for(let i=0;i.05)&&Jn(r,a,t[0],t[1],t[2],t[3])<=o)return!0}return!1}function qn(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function Hn(t,e){const i=t[e],s=t[(e+1)%t.length],n=s[0]-i[0],o=s[1]-i[1],r=Math.hypot(n,o)||1;let a=-o/r,l=n/r;const c=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,n=e.length-1;st[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r+0)+o&&(i=!i)}return i}([c[0]+.001*a,c[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function Un(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 Wn(t,e,i,s){const n=e[0]*s[1]-e[1]*s[0];if(Math.abs(n)<1e-12)return null;const o=[i[0]-t[0],i[1]-t[1]],r=(o[0]*s[1]-o[1]*s[0])/n;return[t[0]+r*e[0],t[1]+r*e[1]]}function Bn(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 n=0;n0||d>0)){s.push([t[n][0],t[n][1]]);continue}if(Un(y,w)){const e=t[n],i=[e[0]+p*h,e[1]+u*h],o=[e[0]+_*d,e[1]+g*d];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=Wn(k,y,$,w),S=Math.max(h,d,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}h>0&&s.push([t[n][0]+p*h,t[n][1]+u*h]),d>0&&s.push([t[n][0]+_*d,t[n][1]+g*d]),h>0||d>0||s.push([t[n][0],t[n][1]])}return s.length>=3?s:null}function jn(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),r=Se(o);if(!r||r.length<3)return null;const a=qn(s,n),l=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Se(i);if(t)for(const e of Ri(r,t,a))l.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])l.push([t[0],t[1]],[t[2],t[3]]);const c=[],h=[];for(let t=0;ta)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=n||r>=1-n||(o.some(t=>Math.abs(t-r)*s<=2*a)||o.push(r))}o.sort((t,e)=>t-e);for(const s of o)c.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),h.push(t)}return{poly:c,parent:h,orig:r}}function Kn(t,e,i,s){const n=e.poly.length,o=new Array(n).fill(0);if(!t?.length)return o;const r=new Set,a=[];for(let l=0;l0?(o[l]=wn(c.cm),r.add(c.key)):a.push(l)}if(!a.length)return o;const l=s>0?s:1,c=Math.max(.5*i,1e-9)*l,h=Tn(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],n=e.orig[(t+1)%e.orig.length],a=zn(s,n),l=(s[0]+n[0])/2,d=(s[1]+n[1])/2;let p=null;for(const t of h){if(r.has(t.w.key))continue;if(!Pn(t.ang,a))continue;if(Jn(t.x,t.y,s[0],s[1],n[0],n[1])>c)continue;const e=Math.hypot(t.x-l,t.y-d);(!p||et?.id===e),n=Se(s);if(!n)return[];const o=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Se(s);if(t)for(const e of Ri(n,t,i))o.push(e)}return o}(t,e,qn(n,a)),h=function(t,e,i,s,n){const o=qn(s,n),r=[];for(let a=0;aJn(h,d,t[0],t[1],t[2],t[3])<=o);r.push(p?"shared":"outer")}return r}(l.poly,c,s,n,a),d=Kn(i,l,n,a),p=d.map((t,e)=>h[e]&&t>0?xn(t,o,r)/2:0);return{...l,kinds:h,cms:d,offsets:p}}function Xn(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Yn(t,l.id,e,i,s,n,o,r);if(c)for(let t=0;t4*l||(!p||tt?.id===e),c=Se(l);if(!c||c.length<3)return null;if(!i?.length)return c.map(t=>[t[0],t[1]]);const h=Yn(t,e,i,s,n,o,r,a);return h&&h.offsets.some(t=>t>0)&&Bn(h.poly,h.offsets)||c.map(t=>[t[0],t[1]])}function to(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=[],n,o,r,a=1){if(!e?.length)return null;const l=[],c=[];let h=0;for(const s of t||[]){if(!s?.id)continue;const d=Yn(t,s.id,e,i,n,o,r,a);if(!d||d.poly.length<3||!d.offsets.some(t=>t>0))continue;for(const t of d.offsets)t>0&&(h=Math.max(h,2*t));const p=io(d.poly,d.offsets),u=Bn(d.poly,d.offsets);p&&l.push(p),u&&c.push(u)}if(!l.length)return null;const d=function(t,e,i,s,n,o,r){if(!e?.length||!i?.length)return[];const a=4*qn(s,r),l=new Map;for(const a of Xn(t,e,i,s,n,o,r))!a.open&&a.half>0&&!l.has(a.key)&&l.set(a.key,a);const c=[...l.values()];if(c.length<2)return[];const h=[];for(const t of i)for(const e of[[t[0],t[1]],[t[2],t[3]]])h.some(t=>Math.hypot(t[0]-e[0],t[1]-e[1])<=a)||h.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 n=Math.hypot(i,s);return n>a?[i/n,s/n]:null};for(const t of h){const e=c.map(e=>({iv:e,u:p(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(r>0?[t.slice(),h,u,p]:[t.slice(),p,u,h])}}return d}(t,e,i,n,o,r,a);try{let t=to(l[0]);for(let e=1;e0))continue;const i=t.angle*Math.PI/180,s=Math.cos(i),o=Math.sin(i),r=-o,l=s,c=t.length/2,d=1.25*Math.max(h,n*a),p=[[t.x-s*c-r*d,t.y-o*c-l*d],[t.x+s*c-r*d,t.y+o*c-l*d],[t.x+s*c+r*d,t.y+o*c+l*d],[t.x-s*c+r*d,t.y-o*c+l*d]];e=ye(e,to(p))}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?" ":"")+jn(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:h}:null}catch{const s=function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Yn(t,l.id,e,i,s,n,o,r);if(!c||c.poly.length<3||!c.offsets.some(t=>t>0))continue;const h=io(c.poly,c.offsets),d=Bn(c.poly,c.offsets);if(!h||!d)continue;const p=`${jn(h)} ${jn(Vn(d))}`;let u="",_="outer",g=0,m=0;for(let t=0;t0){u=Dn(c.poly[t],c.poly[(t+1)%c.poly.length],s,r),_=c.kinds[t]||"outer",g=c.cms[t],m=xn(g,n,o);break}a.push({d:p,key:u,kind:_,cm:g,depthUnits:m})}return a}(t,e,i,n,o,r,a);return s.length?{d:s.map(t=>t.d).join(" "),depthUnits:h}: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]]);Vn(t),e.slice().reverse();const s=[];for(let n=0;n0||a>0)){s.push([t[n][0],t[n][1]]);continue}if(Un(y,w)){const e=t[n],i=[e[0]-l*r,e[1]-c*r],o=[e[0]-h*a,e[1]-d*a];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=Wn(k,y,$,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[n][0]-l*r,t[n][1]-c*r]),a>0&&s.push([t[n][0]-h*a,t[n][1]-d*a])}return s.length>=3?s:null}function so(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){const c=Se(l);if(c&&c.length>=3){const h=Yn(t,l.id,e,i,s,n,o,r),d=h&&h.offsets.some(t=>t>0)?io(h.poly,h.offsets):null,p=d||c;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 no(t,e,i,s,n,o,r=1){let a=null;for(const i of t||[]){const t=Se(i);if(t)for(let s=0;ss*r)return{ox:0,oy:0,cm:0};const l=Rn(i,a.a,a.b,s,r);if(!(l>0))return{ox:0,oy:0,cm:0};const c=xn(l,n,o),h=Se(a.room),[d,p]=Hn(h,a.edge),u=e.flip_v?-1:1,_=c/2;return{ox:d*_*u,oy:p*_*u,cm:l}}function oo(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function ro(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function ao(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function lo(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!ao(t.a)||!ao(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],n=[Number(t.b[0]),Number(t.b[1])];Math.hypot(n[0]-s[0],n[1]-s[1])<.001||e.push({a:s,b:n})}return e}function co(t,e){const i=e[0],s=e[1],n=e[2]-i,o=e[3]-s,r=n*n+o*o;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*n+(t[1]-s)*o)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*n,s+a*o];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function ho(t,e){return co(t,e).q}function po(t,e,i,s,n){const o=co(t,e).q;let r=o,a=1/0;for(const t of i){const e=Math.hypot(o[0]-t[0],o[1]-t[1]);e<=n&&e0?s:1,g=Math.round(u/_)*_,m=Math.max(0,Math.min(p,g))/p;return[l+h*m,c+d*m]}function uo(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt?.id);let o=null;for(let e=0;et.seg)}(e,s);let o=null;for(const r of e||[]){if(!r?.id)continue;const e=Se(r);if(e)for(let a=0;ai)continue;let p=!1;for(const t of n)if(Bi([(l[0]+c[0])/2,(l[1]+c[1])/2],t)<2*s){p=!0;break}p||(!o||dt?.id),r=lo(e);if(r.length)return r.map(t=>ro(t,i));if(!n)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;too([t[0],t[1]],[t[2],t[3]],e))}function vo(t,e,i){let s=null;for(const n of e){const e=Bi(t,n);e<=i&&(!s||e.05)continue;const d=Rn(t,[e[0],e[1]],[e[2],e[3]],s,n);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-h[0],p[1]-h[1]);u0?l:o}(t,e,i,s,n,o);return On(t,[e[0],e[1]],[e[2],e[3]],r,s,n)}function yo(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],n=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const o of e)if(!(Bi(n,o)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Bi([o[0],o[1]],t)>i))for(const e of[[o[0],o[1]],[o[2],o[3]]])Bi(e,t)<=2*i&&s.push(e);return s}function wo(t,e,i,s){const n=lo(t);if(!n.length)return[];if(e.length!==i.length)return n;const o=[];for(const t of n){const n=ro(t,s),r=[n[0],n[1]],a=[n[2],n[3]];let l=r,c=a;for(let t=0;t{const e=co(t,[s[0],s[1],n[0],n[1]]);if(e.d>.001)return t;const i=h[0]-o[0],r=h[1]-o[1];return[o[0]+i*e.t,o[1]+r*e.t]};if(co([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],n[0],n[1]]).d<.01){l=d(r),c=d(a);break}}o.push(oo(l,c,s))}return o}class ko{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=Li(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],n=s?this.now()-s.at:1/0;return n{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 n=0;for(const o of i){const i=t?.urls?.[o];"string"==typeof i&&i?(s[o]={url:i,at:e},this.retry.delete(o),n++):this.backOff(o)}n&&(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,n]of Object.entries(this.signed))e.has(t)&&i-n.at{const e=Number(t);return Number.isFinite(e)?e:null};function Mo(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=So(e.x)&&null!=So(e.y)?{x:So(e.x),y:So(e.y),a:So(e.a??e.angle??e.theta)}:null;let s=null;const n=t.path?.points??t.path;if(Array.isArray(n)&&n.length){s=[];for(const t of n){const e=So(Array.isArray(t)?t[0]:t?.x),i=So(Array.isArray(t)?t[1]:t?.y);null!=e&&null!=i&&s.push([e,i])}s.length||(s=null)}const o=[],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=So(e.cx??e.center?.x),n=So(e.cy??e.center?.y);if(null==s||null==n){const t=So(e.x0),i=So(e.y0),o=So(e.x1),r=So(e.y1);null!=t&&null!=i&&null!=o&&null!=r&&(s=(t+o)/2,n=(i+r)/2)}if(null!=s&&null!=n||(s=So(e.x),n=So(e.y)),i&&null!=s&&null!=n){const r={id:t,name:i,cx:s,cy:n},a=So(e.x0),l=So(e.y0),c=So(e.x1),h=So(e.y1);null!=a&&null!=l&&null!=c&&null!=h&&(r.x0=Math.min(a,c),r.y0=Math.min(l,h),r.x1=Math.max(a,c),r.y1=Math.max(l,h)),o.push(r)}}const l=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||o.length||s?{pos:i,path:s,rooms:o,mapId:l}:null}function Co(t){const e=t?.attributes;return!(!e||!e.vacuum_position&&!e.robot_position)}const Do=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function To(t,e){const i=new Map(e.map(t=>[Do(t.name),t])),s=[],n=[];for(const e of t){const t=i.get(Do(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),n.push(e.name))}if(s.length<3)return null;const o=function(t){if(t.length<3)return null;let e=0,i=0,s=0,n=0,o=0,r=0,a=0,l=0,c=0,h=0,d=0,p=0;for(const[[u,_],[g,m]]of t){if(![u,_,g,m].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,n+=_*_,o+=_,r+=1,a+=u*g,l+=_*g,c+=g,h+=u*m,d+=_*m,p+=m}const u=[e,i,s,i,n,o,s,o,r],_=t=>{const[e,i,s,n,o,r,a,l,c]=u,h=e*(o*c-r*l)-i*(n*c-r*a)+s*(n*l-o*a);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-r*l)/h,(s*l-i*c)/h,(i*r-s*o)/h,(r*a-n*c)/h,(e*c-s*a)/h,(s*n-e*r)/h,(n*l-o*a)/h,(i*a-e*l)/h,(e*o-i*n)/h];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]]},g=_([a,l,c]),m=_([h,d,p]);if(!g||!m)return null;const f=[g[0],g[1],g[2],m[0],m[1],m[2]];return f.every(Number.isFinite)?f:null}(s);return o?{matrix:o,matched:n,residual:xo(o,s)}:null}function zo(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 n=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[n,o]=s.pop(),[r,a]=t[n],[l,c]=t[o],h=l-r,d=c-a,p=Math.hypot(h,d)||1e-9;let u=0,_=-1;for(let e=n+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([n,_],[_,o]))}const n=[];for(let e=0;e600&&(n=n.filter((t,e)=>e%2==0||e===n.length-1)),n}function Po(t){return"cleaning"===t||"returning"===t||"on"===t}const Ao={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function Ro(t){const[e,i]=Ao[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 Eo(t,e,i,s){const[n,o]=$o(Ro(e),i,s),r=Ro({...t,ox:0,oy:0}),[a,l]=$o(r,i,s);return{...t,ox:n-a,oy:o-l}}function No(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}function Oo(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 Io(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 Fo(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)}function Lo(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),n=[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 n){const i=e.find(e=>Fo(t,e.eid));if(i)return i.eid}for(const t of n)for(const e of ft){const i=t.find(t=>t.eid.split(".")[0]===e);if(i)return i.eid}for(const t of n)if(t.length)return t[0].eid}function qo(t,e){const i=!0===e.marker?.is_light?[...e.marker?.controls||[],...e.entities]:e.entities.filter(t=>t.startsWith("light."));return i.find(e=>"on"===t.states[e]?.state)||null}function Ho(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const n=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===n){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const o=e.attributes?.linkquality??e.attributes?.lqi;if(null!=o){const t=parseFloat(o);isNaN(t)||i.push(t)}}return Ye(i)}function Uo(t,e){for(const i of e){if(!Fo(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 Wo(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 Bo(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 jo(t,e){for(const i of e){if(!Bo(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 Vo(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden)continue;let n=null;if("group"===s.platform)n=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if("Group"!==e?.model)continue;n=e.area_id||s.area_id||null}}if(!n)continue;const o=t.states[e];i.push({eid:e,name:s.name||o?.attributes?.friendly_name||e,area:n})}return i}function Go(t,e,i,s,n){const o=mt(e,i,n);if(o!==gt)return o;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=_t[e];if(t)return t}return null}(r)??gt}function Ko(t,e){t.marker=e,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 Yo(t){const{hass:e,areaToSpace:i,markers:s,settings:n,excluded:o,showAll:r,firstSpaceId:a,loc:l,iconRules:c}=t,h=!1!==n.group_lights,d=Vo(e,h),p=new Set(d.map(t=>t.area)),u=Oo(e),_=new Set;for(const t of s){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||_.add(t.binding)}const g=(t,e)=>s.find(i=>i.binding===t+":"+e),m={},f=[];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(_.has("device:"+t.id))continue;const a=g("device",t.id);if(a&&a.hidden&&!n.filter_seeded)continue;const d=u[t.id]||[],v=Io(e,t,d),b=!n.filter_seeded;if(b&&!r){if(o.has(v))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===v&&t.via_device_id)continue}const y=(t.name_by_user||t.name||l("device.unnamed")).trim(),w=y+"|"+s;let k=Go(e,y,t.model,d,c);if(d.some(t=>t.startsWith("lock."))&&(k="mdi:lock"),b&&!r&&h&&"mdi:lightbulb"===k&&p.has(s))continue;m[w]=(m[w]||0)+1;const $=m[w]>1?y+" "+m[w]:y,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=Lo(e,d,k),"mdi:thermometer"!==k&&"mdi:air-filter"!==k||(x.temp=Uo(e,d)),x.primary&&Bo(e,x.primary)&&(x.hum=jo(e,d)),f.push(x)}for(const t of d)i[t.area]&&(_.has("entity:"+t.eid)||f.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.hidden&&!n.filter_seeded)continue;const[s,o]=t.binding.split(":");if("device"===s){const s=e.devices[o],n=t.area||s?.area_id||"",r=n&&i[n]||t.space||a,h=s&&u[s.id]||[];let d=s?Go(e,s.name_by_user||s.name||"",s.model,h,c):"mdi:help-circle";h.some(t=>t.startsWith("lock."))&&(d="mdi:lock");const p={id:t.id,name:s?.name_by_user||s?.name||l("device.fallback"),model:s?.model||"",area:n,space:r,icon:d,entities:h,bindingKind:"device",bindingRef:o};p.primary=Lo(e,h,d),"mdi:thermometer"!==d&&"mdi:air-filter"!==d||(p.temp=Uo(e,h)),p.primary&&Bo(e,p.primary)&&(p.hum=jo(e,h)),p.primary&&Bo(e,p.primary)&&(p.hum=jo(e,h)),Ko(p,t),f.push(p)}else if("entity"===s){const s=e.entities[o],n=t.area||s?.area_id||s?.device_id&&e.devices[s.device_id]?.area_id||"",r=n&&i[n]||t.space||a,l=e.states[o],h=s?.name||l?.attributes?.friendly_name||o;let d=Go(e,h,"",[o],c);o.startsWith("lock.")&&(d="mdi:lock");const p={id:t.id,name:h,model:"",area:n,space:r,icon:d,entities:[o],primary:o,bindingKind:"entity",bindingRef:o};"mdi:thermometer"!==d&&"mdi:air-filter"!==d||(p.temp=Uo(e,[o])),Bo(e,o)&&(p.hum=jo(e,[o])),Ko(p,t),f.push(p)}else{const e=t.area||"",s=t.space||e&&i[e]||a,n={id:t.id,name:t.name||l("device.virtual"),model:t.model||"",area:e,space:s,icon:t.icon||"mdi:map-marker",entities:[],bindingKind:"virtual",virtual:!0};Ko(n,t),f.push(n)}}return f}function Xo(t,e,i){let s=!1;for(const n of e)if(n.area===i&&!n.hidden)for(const e of n.entities)if(e.startsWith("light.")&&(s=!0,"on"===t.states[e]?.state))return"on";return s?"off":"none"}function Zo(t,e,i){if(!e)return null;const s=e.indexOf(":");if(s<0)return null;const n=e.slice(0,s),o=e.slice(s+1);if(!o)return null;if("entity"===n){const e=parseFloat(t.states[o]?.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===o).map(([t])=>t);return"temp"===i?Uo(t,e):jo(t,e)}return null}const Jo=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 Qo={"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.remove":"Remove","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":"Add a room: connect grid dots with lines until the outline closes","title.markup_merge":"Merge rooms: 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: click a virtual wall to make it solid; a shared solid wall merges the two rooms; an outer wall or inside a room deletes the room (with confirm).","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":"Add","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","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} · Esc/Ctrl+Z — undo a dot · close the outline by clicking the first one","markup.hint_start":"click a grid dot to start the outline","tip.lqi":"average zigbee signal:","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 badge","display.ripple":"Ripple only","display.icon_ripple":"Icon + ripple","marker.ripple_size":"Ripple 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":'Remove "{name}" from the plan?',"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.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 removed 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","editor.aspect_ratio":"Aspect ratio (e.g. 16:9 or auto)","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.color":"Color","decor.width":"Line width","decor.w_thin":"Thin","decor.w_mid":"Medium","decor.w_thick":"Thick","decor.fill":"Fill","decor.text_title":"Text label","decor.text_label":"Text","decor.text_hint":"A new line here is a new line on the plan; the block is centred. Drag a corner to resize it, the handle above it to turn it.","decor.text_hint_live":"{} is where the value lands. Without it the value is added at the end. A new line here is a new line on the plan.","decor.live_group":"Live value","decor.live_hint":"The label may show one entity's value — as Home Assistant reports it, with no rounding. A dead entity shows a dash.","decor.live_entity":"Entity","decor.live_entity_ph":"none — a plain label","decor.live_attr":"Attribute","decor.live_attr_ph":"the state","decor.live_unit":"Unit","decor.live_preview":"Preview","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag the picture to move it, pull a corner to resize it. Shift — off the grid.","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)","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 light sources","marker.controls_hint":"With tap action “Toggle”, a click flips all bound lights at once (any on → all off). The icon mirrors their state.","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":"Wall thickness","title.markup_wallthick":"Wall 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 for new room walls. Empty or zero — thin walls. 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","markup.openwall":"Open boundary","title.markup_openwall":"Open boundary — click a point on a shared wall, then a second point on the same wall (within the nearest corners) to make that stretch virtual. Click a dashed stretch to close it. 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.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 device from plan","marker.hide_tip":'The device is not drawn on the plan but still counts toward the room signal. To see it: the "Show hidden" button in the device editor.',"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":"Grid","gs.grid_hint":"Everything you draw or drag now lands on the grid. Hold Shift to place something between the nodes on purpose.","gs.align_all":"Align everything to the grid","gs.align_title":"Align everything to the grid","gs.align_none":"Everything is already on the grid — nothing to move.","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_warn":"This rewrites the plan and the device positions in one go. There is no undo: the only way back is to move things by hand. If some elements are deliberately between the nodes, cancel.","gs.align_run":"Align","gs.align_done":"Aligned to the grid: {n} elements moved","decor.furniture":"Furniture","furn.title":"Furniture library","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 tr={en:Qo,ru:{"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.remove":"Убрать","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":"Добавить комнату: соединяйте точки сетки линиями до замкнутого контура","title.markup_merge":"Объединить комнаты: клик по одной, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить: виртуальная стена → обычная; общая глухая → слияние комнат; внешняя или клик внутрь → удалить комнату (с подтверждением)","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":"Проём","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} · Esc/Ctrl+Z — убрать точку · замкните контур кликом по первой","markup.hint_start":"кликните точку сетки, чтобы начать контур","tip.lqi":"средний сигнал zigbee:","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.ripple":"Только пульсация","display.icon_ripple":"Значок + пульсация","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.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.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":"Путь дашборда (куда вести)","editor.aspect_ratio":"Соотношение сторон (напр. 16:9 или auto)","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.color":"Цвет","decor.width":"Толщина линии","decor.w_thin":"Тонкая","decor.w_mid":"Средняя","decor.w_thick":"Толстая","decor.fill":"Залить","decor.text_title":"Надпись","decor.text_label":"Текст","decor.text_hint":"Перевод строки здесь — перевод строки на плане; блок выравнивается по центру. Тяните угол — меняет размер, ручка сверху — поворот.","decor.text_hint_live":"{} — место, куда встанет значение. Без него значение добавится в конец. Перевод строки здесь — перевод строки на плане.","decor.live_group":"Живое значение","decor.live_hint":"Надпись может показывать значение одной сущности — так, как его отдаёт Home Assistant, без округления. Мёртвая сущность — прочерк.","decor.live_entity":"Сущность","decor.live_entity_ph":"нет — обычная надпись","decor.live_attr":"Атрибут","decor.live_attr_ph":"состояние","decor.live_unit":"Единица","decor.live_preview":"Предпросмотр","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — двигается, тяните угол — меняет размер. Shift — мимо сетки.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","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":"Толщина стен","title.markup_wallthick":"Толщина стен — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина стен новой комнаты. Пустое или ноль — без толщины. Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","markup.openwall":"Открытая граница","title.markup_openwall":"Открытая граница — клик по точке на общей стене, затем второй клик на той же стене (не дальше ближайших углов) открывает отрезок. Клик по пунктиру закрывает. Курсор — крестик.","toast.openwall_pick":"Кликните по стене, разделяющей две комнаты","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.openwall_opened_span":"Виртуальный отрезок открыт","toast.openwall_closed_span":"Виртуальный отрезок закрыт","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":"Устройство не отображается на плане, но участвует в расчёте сигнала комнаты. Показать: кнопка «Показать скрытые» в редакторе устройств.","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":"Всё, что вы рисуете и перетаскиваете, ложится строго на сетку. Shift — временно поставить элемент между узлами.","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_warn":"Действие разом перезапишет план и позиции устройств. Отмены нет: вернуть можно только руками. Если какие-то элементы стоят между узлами намеренно — отмените.","gs.align_run":"Выровнять","gs.align_done":"Выровнено по сетке: сдвинуто элементов — {n}","decor.furniture":"Мебель","furn.title":"Библиотека мебели","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 er(t,e){if(e&&e in tr)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function ir(t,e,i){return _i(tr[t][e]??Qo[e]??e,i)}class sr 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 er(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:ir(e,"editor.lang_auto")},{value:"en",label:ir(e,"editor.lang_en")},{value:"ru",label:ir(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 V;this._loadSpaces();const t=this._lang,e={title:ir(t,"editor.title"),default_floor:ir(t,"editor.default_floor"),language:ir(t,"editor.language"),icon_size:ir(t,"editor.icon_size"),show_temperature:ir(t,"editor.show_temperature"),live_states:ir(t,"editor.live_states"),show_signal:ir(t,"editor.show_signal"),kiosk:ir(t,"editor.kiosk"),cycle:ir(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)}}ir.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",ir);const sr=o` + >`}_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)}}sr.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",sr);const nr=o` :host { --hp-bg: var(--card-background-color, #16212e); --hp-line: var(--divider-color, #2b3d4f); @@ -2508,7 +2508,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow z-index: 120; max-width: 90vw; } - `;function nr(t){const e=Ns(t.cfg),i=e.find(e=>e.id===t.spaceId);if(!i)return null;const s=fi(t.cfg.spaces.find(e=>e.id===t.spaceId)),n=t.iconSize??2.5,o=n>8?2.5:n,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):ht,l=pt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:dt),c=Ko({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=>er(t.lang,e),iconRules:l}),h=c.filter(e=>e.space===t.spaceId),d=h.filter(t=>!t.hidden),p=function(t,e,i){const s={},n=i/100*sn(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 o=rn(i),r=.1*Math.min(o.w,o.h),a=o.w-2*r,l=o.h-2*r,c=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),h=a/c,d=l/Math.max(Math.ceil(e.length/c),1),p=e.map((t,e)=>({x:o.x+r+h*(e%c+.5),y:o.y+r+d*(Math.floor(e/c)+.5)}));Ze(p,o,n,.5*r),e.forEach((t,e)=>s[t.id]=Us(p[e]))}return s}(h,i,o),u=[];for(const e of d){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*zs,e=i.y*zs;u.push({minX:t,minY:e,maxX:t,maxY:e})}}const _=tn(i,u),g=[_.x,_.y,_.w,_.h],m=i.rooms.filter(t=>t.area||s.showBorders).map(e=>{let n="room "+(i.bg?"overlay":"yard"),o="";const r=Hi(s.fill,e);if(s.showBorders||"none"!==r){n+=" styled";const i=[`--room-stroke:${s.color}`,`--room-stroke-op:${s.showBorders?s.opacity:0}`],a=e.area?$i(r,"lqi"===r?function(t,e,i){const s=[];for(const n of e){if(n.area!==i||n.virtual)continue;const e=Ho(t,n.entities);null!=e&&s.push(e)}return Ye(s)}(t.hass,h,e.area):null,"light"===r?Yo(t.hass,h,e.area):"none","temp"===r?function(t,e,i){const s=[];for(const n of e){if(n.area!==i)continue;if("mdi:thermometer"!==n.icon&&"mdi:air-filter"!==n.icon)continue;const e=qo(t,n.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,h,e.area):null,s.tempMin,s.tempMax,wi(t.cfg?.settings)):null;a?(n+=" filled",i.push(`--room-fill:${a.c}`,`--room-fill-op:${a.a.toFixed(3)}`)):i.push("--room-fill:transparent","--room-fill-op:0"),o=i.join(";")}const a=!i.bg&&!s.showNames,l=an(e),c=e.id||V,d=e.area||V,p=e.poly?B`e.id===t.spaceId);if(!i)return null;const s=fi(t.cfg.spaces.find(e=>e.id===t.spaceId)),n=t.iconSize??2.5,o=n>8?2.5:n,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):ht,l=pt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:dt),c=Yo({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=>ir(t.lang,e),iconRules:l}),h=c.filter(e=>e.space===t.spaceId),d=h.filter(t=>!t.hidden),p=function(t,e,i){const s={},n=i/100*sn(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 o=rn(i),r=.1*Math.min(o.w,o.h),a=o.w-2*r,l=o.h-2*r,c=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),h=a/c,d=l/Math.max(Math.ceil(e.length/c),1),p=e.map((t,e)=>({x:o.x+r+h*(e%c+.5),y:o.y+r+d*(Math.floor(e/c)+.5)}));Ze(p,o,n,.5*r),e.forEach((t,e)=>s[t.id]=Us(p[e]))}return s}(h,i,o),u=[];for(const e of d){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*zs,e=i.y*zs;u.push({minX:t,minY:e,maxX:t,maxY:e})}}const _=tn(i,u),g=[_.x,_.y,_.w,_.h],m=i.rooms.filter(t=>t.area||s.showBorders).map(e=>{let n="room "+(i.bg?"overlay":"yard"),o="";const r=qi(s.fill,e);if(s.showBorders||"none"!==r){n+=" styled";const i=[`--room-stroke:${s.color}`,`--room-stroke-op:${s.showBorders?s.opacity:0}`],a=e.area?$i(r,"lqi"===r?function(t,e,i){const s=[];for(const n of e){if(n.area!==i||n.virtual)continue;const e=Ho(t,n.entities);null!=e&&s.push(e)}return Ye(s)}(t.hass,h,e.area):null,"light"===r?Xo(t.hass,h,e.area):"none","temp"===r?function(t,e,i){const s=[];for(const n of e){if(n.area!==i)continue;if("mdi:thermometer"!==n.icon&&"mdi:air-filter"!==n.icon)continue;const e=Uo(t,n.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,h,e.area):null,s.tempMin,s.tempMax,wi(t.cfg?.settings)):null;a?(n+=" filled",i.push(`--room-fill:${a.c}`,`--room-fill-op:${a.a.toFixed(3)}`)):i.push("--room-fill:transparent","--room-fill-op:0"),o=i.join(";")}const a=!i.bg&&!s.showNames,l=an(e),c=e.id||V,d=e.area||V,p=e.poly?B``:B``;return B`${p}${a?B`${e.name}`:V}`}),f=d.map(e=>{const s=function(t,e,i,s,n){const o=e[t.id];return o&&o.s===t.space?{x:o.x*zs,y:o.y*zs}:s[t.id]?s[t.id]:Us(en(n))}(e,t.layout,t.cfg,p,i),n=(s.x-g[0])/g[2]*100,o=(s.y-g[1])/g[3]*100,r=Number(e.marker?.size)>0?Number(e.marker.size):1,a=Number(e.marker?.angle)||0,l=[`left:${n}%`,`top:${o}%`];return 1!==r&&l.push(`--dev-scale:${r}`),W`
`}),v=s.showNames?i.rooms.filter(t=>t.name).map(e=>{const n=function(t,e,i){const s=i["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*zs,y:s.y*zs};const n=an(t);return Us({x:n[0],y:n[1]})}(e,i.id,t.layout,t.cfg),o=(n.x-g[0])/g[2]*100,r=(n.y-g[1])/g[3]*100,a=Math.min(1,s.opacity+.25);return W`
${e.name}
`}):[],b=i.bg?t.displayUrl?t.displayUrl(i.bg.href):i.bg.href:"",y=t.cfg.spaces.find(e=>e.id===t.spaceId)?.settings||{};let w="";if("daynight"===Ms(t.cfg?.settings,y)&&null!==Ss(t.cfg?.settings,y)){const e=Ts(t.hass);e&&(w=ms(e.elevation).bg)}const k=w||vi(t.cfg?.settings,s),$=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},x=Array.isArray($.walls)?$.walls:[],S=Number($.cell_cm)>0?Number($.cell_cm):5,M=x.length?so(i.rooms,x,[],Hs,S,Ls,zs):Me(i.rooms),C=wi(t.cfg.settings),D=x.length&&s.showBorders?eo(i.rooms,x,[],[],Hs,S,Ls,zs):null,T=t.stageWidth&&g[2]?t.stageWidth/g[2]:1,z=!!D&&yn(D.depthUnits,T);return W` + style="left:${o}%;top:${r}%;color:${s.color};opacity:${a}">${e.name}`}):[],b=i.bg?t.displayUrl?t.displayUrl(i.bg.href):i.bg.href:"",y=t.cfg.spaces.find(e=>e.id===t.spaceId)?.settings||{};let w="";if("daynight"===Ms(t.cfg?.settings,y)&&null!==Ss(t.cfg?.settings,y)){const e=Ts(t.hass);e&&(w=ms(e.elevation).bg)}const k=w||vi(t.cfg?.settings,s),$=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},x=Array.isArray($.walls)?$.walls:[],S=Number($.cell_cm)>0?Number($.cell_cm):5,M=x.length?so(i.rooms,x,[],qs,S,Ls,zs):Me(i.rooms),C=wi(t.cfg.settings),D=x.length&&s.showBorders?eo(i.rooms,x,[],[],qs,S,Ls,zs):null,T=t.stageWidth&&g[2]?t.stageWidth/g[2]:1,z=!!D&&yn(D.depthUnits,T);return W`
${D?B` @@ -2538,13 +2538,13 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${""}
${f}${v}
- `}let or=null,rr=null,ar=!1;const lr=new Set;function cr(){if(or)return or;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 hr(t){return or?Promise.resolve(or):rr||(rr=async function(t){const[e,i]=await Promise.all([t.callWS({type:"houseplan/config/get"}),t.callWS({type:"houseplan/layout/get"})]);if(or={config:e?.config??null,rev:e?.rev??0,layout:i?.layout??{}},!ar&&t.connection?.subscribeEvents){ar=!0;const e=()=>{or=null,lr.forEach(t=>t())};try{await t.connection.subscribeEvents(e,"houseplan_config_updated"),await t.connection.subscribeEvents(e,"houseplan_layout_updated")}catch{ar=!1}}return or}(t).finally(()=>{rr=null}),rr)}class dr 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 tr(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:"aspect_ratio",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}}]}render(){if(!this.hass||!this._config)return V;this._loadSpaces();const t=this._lang,e={space:er(t,"editor.space"),title:er(t,"editor.title"),show_button:er(t,"editor.show_button"),button_label:er(t,"editor.button_label"),button_target:er(t,"editor.button_target"),aspect_ratio:er(t,"editor.aspect_ratio"),icon_size:er(t,"editor.icon_size")};return W`{rr=null,cr.forEach(t=>t())};try{await t.connection.subscribeEvents(e,"houseplan_config_updated"),await t.connection.subscribeEvents(e,"houseplan_layout_updated")}catch{lr=!1}}return rr}(t).finally(()=>{ar=null}),ar)}class pr 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 er(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:"aspect_ratio",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}}]}render(){if(!this.hass||!this._config)return V;this._loadSpaces();const t=this._lang,e={space:ir(t,"editor.space"),title:ir(t,"editor.title"),show_button:ir(t,"editor.show_button"),button_label:ir(t,"editor.button_label"),button_target:ir(t,"editor.button_target"),aspect_ratio:ir(t,"editor.aspect_ratio"),icon_size:ir(t,"editor.icon_size")};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)}}dr.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",dr);const pr=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 ur extends lt{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._stageWidth=0,this._loadedOnce=!1,this._signer=new wo(()=>this.requestUpdate()),this._goToSpace=()=>{const t=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");pr(`${t}#space=${encodeURIComponent(this._config.space)}`)}}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(t){const e=cr();return{type:"custom:houseplan-space-card",space:Ns(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||cr()}connectedCallback(){var t;super.connectedCallback(),this._unsub=(t=()=>{this._loading=!1,this._snap=null,this.requestUpdate()},lr.add(t),()=>lr.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 hr(this.hass);this._snap=t,this._loadedOnce=!0}catch{}finally{this._loading=!1,this.requestUpdate()}}}get _lang(){return tr(this.hass,this._config?.language)}getCardSize(){const t=Ns(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 Fi(this._snap?.config)}render(){if(!this._config)return V;const t=this._snap?.config;if(!t)return W`
${er(this._lang,"space_card.loading")}
`;const e=this._config.space,i=nr({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(er(this._lang,"space_card.not_found",{id:e}));const s=Ns(t).find(t=>t.id===e),n=void 0!==this._config.title?this._config.title:s?.title||"",o=!1!==this._config.show_button,r=this._config.button_label||er(this._lang,"space_card.button");return W` + >
`}_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)}}pr.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",pr);const ur=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 _r extends lt{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._stageWidth=0,this._loadedOnce=!1,this._signer=new ko(()=>this.requestUpdate()),this._goToSpace=()=>{const t=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");ur(`${t}#space=${encodeURIComponent(this._config.space)}`)}}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(t){const e=hr();return{type:"custom:houseplan-space-card",space:Ns(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||hr()}connectedCallback(){var t;super.connectedCallback(),this._unsub=(t=()=>{this._loading=!1,this._snap=null,this.requestUpdate()},cr.add(t),()=>cr.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 dr(this.hass);this._snap=t,this._loadedOnce=!0}catch{}finally{this._loading=!1,this.requestUpdate()}}}get _lang(){return er(this.hass,this._config?.language)}getCardSize(){const t=Ns(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 Fi(this._snap?.config)}render(){if(!this._config)return V;const t=this._snap?.config;if(!t)return W`
${ir(this._lang,"space_card.loading")}
`;const e=this._config.space,i=or({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(ir(this._lang,"space_card.not_found",{id:e}));const s=Ns(t).find(t=>t.id===e),n=void 0!==this._config.title?this._config.title:s?.title||"",o=!1!==this._config.show_button,r=this._config.button_label||ir(this._lang,"space_card.button");return W` ${n?W`
${n}
`:V} ${i} @@ -2552,7 +2552,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow `:V}
- `}}ur.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0}},ur.styles=[sr,o` + `}}_r.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0}},_r.styles=[nr,o` .hp-static-title { font-weight: 700; padding: 10px 14px 6px; @@ -2627,7 +2627,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",ur),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 _r=4.166666666666667e-9;function gr(t){if(!Number.isFinite(t))return t;const e=Math.round(t/Hs)*Hs;return Math.abs(e-t)<=_r?t:e}const mr=(t,e,i,s)=>Math.hypot(i-t,s-e),fr=(t,e,i,s,n,o,r,a)=>Math.hypot(Math.max(Math.abs(n-t),Math.abs(n+r-(t+i))),Math.max(Math.abs(o-e),Math.abs(o+a-(e+s)))),vr=(t,e,i,s,n,o,r)=>{const a=Math.max(Number(s)||0,0)/2,l=Math.PI/180,c=Math.cos(i*l)*a,h=Math.sin(i*l)*a,d=Math.cos(r*l)*a,p=Math.sin(r*l)*a,u=Math.max(mr(t+c,e+h,n+d,o+p),mr(t-c,e-h,n-d,o-p)),_=Math.max(mr(t+c,e+h,n-d,o-p),mr(t-c,e-h,n+d,o+p));return Math.min(u,_)},br=t=>{const e=Number(t?.cell_cm);return e>0?e:5};const yr="1.59.0-beta.8",wr=1500,kr=new Map;let $r=0;const xr=t=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(t??{})}`;let Sr=1e4;const Mr="houseplan_card_layout_v1",Cr="houseplan_card_cfg_v1",Dr="houseplan_card_zoom_v1",Tr="houseplan_card_nav_v1",zr="houseplan_card_kiosk_v1",Pr=1e3,Ar=(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}},Rr=(t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)},Er=(t,e)=>{let i,s=null;const n=(...n)=>{clearTimeout(i),s=n,i=window.setTimeout(()=>{i=void 0;const e=s;s=null,e&&t(...e)},e)};return n.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const e=s;s=null,e&&t(...e)},n.pending=()=>void 0!==i,n},Nr=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}};class Or 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._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={color:"#607d8b",width:3,fill:!1},this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorTextDialog=null,this._furnPalette=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._tool="draw",this._wallDialog=null,this._drawWallField=null,this._rszSel=null,this._rszDrag=null,this._rszUndo=[],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._opShift=!1,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._warmRevivePending=!1,this._warmGen=++$r,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._senseRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._vacVisHandler=()=>{"visible"===document.visibilityState&&(this._vacJumpOnce=!0,this._skyElev=null,this.requestUpdate())},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._space=t,this._selId=null,this._restoreZoom(),this.requestUpdate())},this._drag=null,this._rlResize=null,this._holdFired=!1,this._cfgEpoch=0,this._modelCache=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 wo(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=Er(()=>{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(Mr,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._saveConfigDebounced=Er(()=>{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._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:vi(this._settings,{bgColor:null})||null,northDeg:Ss(this._settings,{}),bgMode:Ms(this._settings,{}),sunRays:Cs(this._settings,{}),weatherEntity:Ds(this._settings)||"",busy:!1}},this._openAlignDialog=()=>{if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[],e=function(t,e){const i=JSON.parse(JSON.stringify(t||[])),s=JSON.parse(JSON.stringify(e||{}));let n=0,o=0,r=0,a=0,l="",c=0;const h={};let d=5;for(const t of i){const e=br(t);null!=t?.id&&(h[String(t.id)]=e),e>d&&(d=e)}const p=(t,e,i,s=!1)=>{if(!(t>_r||s))return;n++,t>r&&(r=t);const o=t*Fs*e;o>a&&(a=o,l=i)};for(const t of i){const e=br(t),i=null!=t?.id?String(t.id):"";for(const s of t.rooms||[]){o++;let t=0;if(s.poly?.length)s.poly=s.poly.map(e=>{const i=[gr(e[0]),gr(e[1])];return t=Math.max(t,mr(e[0],e[1],i[0],i[1])),i});else if(null!=s.x&&null!=s.y){const e=s.x,i=s.y,n=s.w||0,o=s.h||0,r=gr(e+n),a=gr(i+o),l=gr(e),c=gr(i),h=Math.max(Hs,r-l),d=Math.max(Hs,a-c);t=fr(e,i,n,o,l,c,h,d),s.x=l,s.y=c,s.w=h,s.h=d}p(t,e,i)}for(const s of t.decor||[]){o++;let t=0;if("line"===s.kind){const e=[gr(s.x1),gr(s.y1)],i=[gr(s.x2),gr(s.y2)];t=Math.max(mr(s.x1,s.y1,e[0],e[1]),mr(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=gr(s.x),i=gr(s.y);if(null!=s.w&&null!=s.h){const n=gr(s.x+s.w),o=gr(s.y+s.h),r=Math.max(Hs,n-e),a=Math.max(Hs,o-i);t=fr(s.x,s.y,s.w,s.h,e,i,r,a),s.w=r,s.h=a}else t=mr(s.x,s.y,e,i);s.x=e,s.y=i}p(t,e,i)}for(const s of t.openings||[]){o++;const n=De([s.x,s.y],t.rooms||[],.025,{step:Hs,length:Number(s.length)||0});if(!n)continue;const r=Number(s.angle),a=!(Number.isFinite(r)&&r===n.angle),l=vr(s.x,s.y,Number.isFinite(r)?r:n.angle,Number(s.length)||0,n.x,n.y,n.angle);s.x=n.x,s.y=n.y,s.angle=n.angle,a&&c++,p(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;o++;const n=gr(i.x),r=gr(i.y),a=mr(i.x,i.y,n,r);s[t]={...i,x:n,y:r};const l="string"==typeof i.s?i.s:"";p(a,h[l]??d,l)}return{spaces:i,layout:s,report:{moved:n,total:o,maxShift:r,maxShiftCm:a,maxSpace:l,rotated:c},changed:n>0}}(t,this._layout||{}),i=Math.ceil(10*e.report.maxShiftCm)/10,s=t.find(t=>null!=t?.id&&String(t.id)===e.report.maxSpace),n=t.length>1&&s?String(s.title||s.id):"";this._alignDialog={report:e.report,spaces:e.spaces,layout:e.layout,cm:i,where:n,busy:!1}},this._openRulesDialog=()=>{if(!this._norm)return;const t=this._settings.icon_rules,e=(t&&t.length?t:dt).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)}_slideTo(t,e){if(t===this._space)return;const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;this._space=t,this._selId=null,this._restoreZoom(),i||(this._slide=e,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slide="",this.requestUpdate()},260),this.requestUpdate())}_cycleTick(){if(this._kiosk&&Number(this._config?.cycle)>0&&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,()=>Fi(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},wr)),!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))}disconnectedCallback(){document.removeEventListener("visibilitychange",this._vacVisHandler),this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._skySnapRaf&&(cancelAnimationFrame(this._skySnapRaf),this._skySnapRaf=0);for(const t of this._senseRt.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._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),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),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._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._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0)}if("decor"===this._mode)return"Delete"!==t.key&&"Backspace"!==t.key||!this._decorSel||t.target?.closest?.("input, textarea, select")?void("Escape"===t.key&&(t.preventDefault(),this._decorDraft?this._decorDraft=null: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)return;const e="Escape"===t.key||(t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase();if(e){if(this._roomDialog)return t.preventDefault(),void this._roomDialogCancel();if("draw"===this._tool&&this._path.length)return t.preventDefault(),void this._undoPoint();if(e)return"resize"===this._tool?(t.preventDefault(),this._rszDrag?void this._rszCancelDrag():(t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase()?void this._rszUndoPop():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?(t.preventDefault(),void(this._openWallAnchor?this._openWallAnchor=null:this._tool="draw")):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool||(t.preventDefault(),this._tool="draw"))}}_undoPoint(){this._path.length&&(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 kr.values())for(const e of t)clearTimeout(e.evict);kr.clear(),Sr=t&&t>0?t:1e4}static _warmBootStats(){let t=0,e=0;const i=[];for(const s of kr.values())for(const n of s)t++,n.dlg&&(e++,i.push(n.dlg.kind));return{keys:kr.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(Dr)||"{}")||{}}catch{this._zoomBySpace={}}try{const t=JSON.parse(localStorage.getItem(zr)||"null");this._kioskScale={icon:qi(t?.icon),font:qi(t?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(Cr)||"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()}_warmAdopt(){if(this._config?.kiosk)return;const t=xr(this._config);if(this._warmKey===t&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const e=this.parentNode,i=this._warmIdx(e),s=kr.get(t);if(!s||!s.length)return;const n=s.find(t=>t.owner===this._warmGen);if(n)return clearTimeout(n.evict),n.evict=0,n.freed=0,n.live=!0,this._warmSlot=n,void(this._warmKey=t);const{slot:o,sure:r}=((t,e,i,s)=>{const n=t=>{const e=!!i&&t.place?.deref()===i;return e&&t.idx===s?4:t.live?0:e?3:2};let o=null,r=0,a=0,l=null;for(const i of t){if(i.owner===e)continue;l=i;const t=n(i);t<=0||(t>r?(o=i,r=t,a=1):t===r&&a++)}return!o||a>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(s,this._warmGen,e,i);o&&(this._booting=!1,this._bootFading=!1,this._hdrH=o.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},wr)),this._warmKey=t,r?(clearTimeout(o.evict),o.evict=0,o.owner=this._warmGen,o.place=e?new WeakRef(e):null,o.idx=i,o.live=!0,this._warmSlot=o,this._warmVp=o.vp,this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,place:e?new WeakRef(e):null,idx:i,live:!0,hdrH:o.hdrH,stageH:o.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 n=kr.get(e);if(!t.live&&n&&n.length>1){const e=n.indexOf(t);e>=0&&n.splice(e,1)}},Sr+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=xr(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=kr.get(i)||[];for(s.push(this._warmSlot),kr.set(i,s),this._warmTrim(s);kr.size>8;){const t=kr.keys().next().value;if(void 0===t||t===i)break;for(const e of kr.get(t)||[])clearTimeout(e.evict);kr.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._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._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>Sr)&&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"decorText":this._decorTextDialog={...e.data};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(Cr,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.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 Ns(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:n,excluded:o,iconRules:r}=t,a=!1!==n.group_lights,l=jo(e,a),c=new Set(l.map(t=>t.area)),h=No(e),d=new Set(s.map(t=>t.binding)),p=[];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(d.has("device:"+t.id))continue;const n=h[t.id]||[],l=Oo(e,t,n);let u=o.has(l)||"Group"===t.model||/scene/i.test(t.model||"")||/bridge/i.test((t.model||"")+(t.name||""))||"myheat"===l&&!!t.via_device_id;!u&&a&&c.has(s)&&"mdi:lightbulb"===Vo(e,(t.name_by_user||t.name||"").trim(),t.model,n,r)&&(u=!0),u&&p.push("device:"+t.id)}return p}({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=pt(t)),this._rulesCompiled}get _fillColors(){return wi(this._settings)}get _excluded(){const t=this._settings.exclude_integrations;return t?new Set(t):ht}willUpdate(t){t?.has?.("hass")&&(this._vacTick(),this._senseTick()),this._skyPlan(),t.has("hass")&&this.hass&&(this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices())}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),n=Math.round(i+s);n>=0&&Math.abs(n-this._hdrH)>1&&(this._hdrH=n),n>=0&&!this._booting&&!this._config?.kiosk&&t.clientHeight>0&&this._warmPatch({hdrH:n,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._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null);const i=t?.config;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=>{(t?.data?.rev??-1)!==this._cfgRev&&this._reloadConfigOnly()},"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(),n=this._savedNav();!this._hashApplied&&s&&this._model.find(t=>t.id===s)?(this._space=s,this._hashApplied=!0):n?.space&&!this._navApplied&&!this._hashApplied&&this._model.find(t=>t.id===n.space)?(this._space=n.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(Mr)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1}this._regSignature="",this.requestUpdate()}async _reloadConfigOnly(t=!1){if(!t&&(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting))return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(),400));try{const t=await this.hass.callWS({type:"houseplan/config/get"}),e=t?.config;this._serverCfg=e&&Array.isArray(e.spaces)?e:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,"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,Fi(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._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")+":"+tr(t,this._config?.language);e===this._regSignature&&this._devices.length||(this._regSignature=e,this._devices=Ko({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())}_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:n}=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:n,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=Ho(this.hass,i.entities);null!=s&&e.push(s)}return Ye(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),n=Math.min(...i);return{x:s,y:n,w:Math.max(...e)-s,h:Math.max(...i)-n}}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*sn(i)*1.3;for(const e of i.rooms){if(!e.area)continue;const n=this._devices.filter(t=>t.area===e.area&&t.space===i.id);if(!n.length)continue;const o=this._roomBounds(e),r=.1*Math.min(o.w,o.h),a=o.w-2*r,l=o.h-2*r,c=Math.max(1,Math.round(Math.sqrt(n.length*a/Math.max(l,1)))),h=Math.ceil(n.length/c),d=a/c,p=l/Math.max(h,1),u=n.map((t,e)=>({x:o.x+r+d*(e%c+.5),y:o.y+r+p*(Math.floor(e/c)+.5)}));Ze(u,o,s,.5*r),n.forEach((e,i)=>t[e.id]=Us(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*Pr,y:i.y*Pr}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[t.id]?this._defPos[t.id]:Us(en(e))}_savePos(t,e,i,s=!1){if(this._norm){const n=this._gridPitch,o=s?e:Math.round(e/n)*n,r=s?i:Math.round(i/n)*n,a=this._layout[t.id]?.k;this._layout={...this._layout,[t.id]:{s:t.space,x:Bs(o/Pr),y:Bs(r/Pr),...a?{k:a}:{}}}}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?ri(t.entities):null}_actEntity(t){return this._coverIndicator(t)||t.primary}_stateClass(t){if(!this._config?.live_states)return"";const e=this._coverIndicator(t);if(e){const t=this.hass.states[e];return t?"unavailable"===t.state?"unavail":ai(t.state)?"covermove":"":""}const i=(t.marker?.controls||[]).filter(Ai);if(i.length)return i.some(t=>"on"===this.hass.states[t]?.state)?"on":"";if(Lo(this.hass,t))return"on";const s=this._actEntity(t),n=s?this.hass.states[s]:void 0;if(!n)return"";if("unavailable"===n.state)return"unavail";const o=s.split(".")[0];if(["light","switch","fan","humidifier"].includes(o))return"on"===n.state?"on":"";if("climate"===o){const t=n.attributes?.hvac_action;return null!=t?["heating","cooling","drying","fan"].includes(t)?"on":"":["off","unknown"].includes(n.state)?"":"on"}if("cover"===o)return ai(n.state)?"covermove":"";if("valve"===o)return["open","opening"].includes(n.state)?"open":"";if("lock"===o)return["unlocked","open"].includes(n.state)?"open":"";if("binary_sensor"===o){const e=n.attributes?.device_class;if(["door","window","garage_door","opening","gas","smoke","moisture","problem"].includes(e))return"on"===n.state?"open":"";if("motion"===e){const e=this._senseRt.get(t.id);return e&&e.flashTs&&Date.now()-e.flashTs<3300?e.gen%2==0?"senseflash sf2":"senseflash":""}if(["occupancy","presence"].includes(e))return"on"===n.state?"sensehold":""}return"media_player"===o?["playing","on"].includes(n.state)?"on":"":"vacuum"===o&&["cleaning","returning"].includes(n.state)?"on":""}_liveTemp(t){if(!this._config?.show_temperature)return null;if(!0===t.marker?.use_climate_temp){const e=Uo(this.hass,t.entities);if(null!=e)return e}return"mdi:thermometer"!==t.icon&&"mdi:air-filter"!==t.icon?null:qo(this.hass,t.entities)}_bindingHasClimate(t){if(t.startsWith("entity:"))return t.slice(7).startsWith("climate.");if(t.startsWith("device:")){const e=t.slice(7);for(const[t,i]of Object.entries(this.hass?.entities||{}))if(i?.device_id===e&&t.startsWith("climate."))return!0}return!1}_bindingCoverEntity(t){if(t.startsWith("entity:"))return ri([t.slice(7)]);if(t.startsWith("device:")){const e=t.slice(7);return ri(Object.entries(this.hass?.entities||{}).filter(([,t])=>t?.device_id===e).map(([t])=>t))}return null}_bindingCoverTap(t){const e=this._bindingCoverEntity(t);if(!e)return!1;const i=String(this.hass?.states?.[e]?.attributes?.device_class||"");return!oi.has(i)}_liveHum(t){return this._config?.show_temperature&&t.primary&&Wo(this.hass,t.primary)?Bo(this.hass,t.entities):null}_openMoreInfo(t){t?Rr(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),n=s?s.split(".")[0]:null,o=(t,i)=>{e.marker?.tap_confirm?this._tapConfirm={text:t,exec:i}:i()},r=(e.marker?.controls||[]).filter(Ai);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 o(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 n=t||e||("light"===i?"toggle":"info");return"more-info"===n?"more-info":"run"===n?"run"===t?"run":"info":"cover"===n?"cover"!==t||"cover"!==i||oi.has(String(s||""))?"info":"cover":"toggle"!==n||!i||ni.has(i)?"info":"toggle"===t?"toggle":si.has(i)?"cover"===i&&oi.has(String(s||""))?"info":"toggle":"info"}(e.tapAction,void 0,n,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 n=s.attributes?.friendly_name||t;return void o(this._t("confirm.tap_run",{name:n}),()=>{this.hass.callService(i.domain,i.service,{entity_id:t}).then(()=>this._showToast(this._t("toast.run_started",{name:n}))).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 o(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?o(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 er(tr(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,n=Math.sin(i)*t.rlen/2,o=Gs([[t.rx-s,t.ry-n],[t.rx+s,t.ry+n]]);o&&e.push(o)}const t=this._decorH;for(const i of this._decorList){const s=Gs("line"===i.kind?[[i.x1*Pr,i.y1*t],[i.x2*Pr,i.y2*t]]:[[i.x*Pr,i.y*t],[(i.x+(i.w||0))*Pr,(i.y+(i.h||0))*t]]);s&&e.push(s)}}return Ys(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=Qs(this._contentItems(t));let n=s.all||tn(t),o=this._showFar?n:s.core||tn(t);return e&&e.id===t.id&&i&&e.grow&&(o=Ar(e.rect,o),n=Ar(e.all,n)),this._frame={id:t.id,model:t,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:n,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._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?V:W`
+ `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",_r),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 gr=4.166666666666667e-9;function mr(t){if(!Number.isFinite(t))return t;const e=Math.round(t/qs)*qs;return Math.abs(e-t)<=gr?t:e}const fr=(t,e,i,s)=>Math.hypot(i-t,s-e),vr=(t,e,i,s,n,o,r,a)=>Math.hypot(Math.max(Math.abs(n-t),Math.abs(n+r-(t+i))),Math.max(Math.abs(o-e),Math.abs(o+a-(e+s)))),br=(t,e,i,s,n,o,r)=>{const a=Math.max(Number(s)||0,0)/2,l=Math.PI/180,c=Math.cos(i*l)*a,h=Math.sin(i*l)*a,d=Math.cos(r*l)*a,p=Math.sin(r*l)*a,u=Math.max(fr(t+c,e+h,n+d,o+p),fr(t-c,e-h,n-d,o-p)),_=Math.max(fr(t+c,e+h,n-d,o-p),fr(t-c,e-h,n+d,o+p));return Math.min(u,_)},yr=t=>{const e=Number(t?.cell_cm);return e>0?e:5};const wr="1.59.0-beta.9",kr=1500,$r=new Map;let xr=0;const Sr=t=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(t??{})}`;let Mr=1e4;const Cr="houseplan_card_layout_v1",Dr="houseplan_card_cfg_v1",Tr="houseplan_card_zoom_v1",zr="houseplan_card_nav_v1",Pr="houseplan_card_kiosk_v1",Ar=1e3,Rr=(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}},Er=(t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)},Nr=(t,e)=>{let i,s=null;const n=(...n)=>{clearTimeout(i),s=n,i=window.setTimeout(()=>{i=void 0;const e=s;s=null,e&&t(...e)},e)};return n.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const e=s;s=null,e&&t(...e)},n.pending=()=>void 0!==i,n},Or=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}};class Ir 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._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={color:"#607d8b",width:3,fill:!1},this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorTextDialog=null,this._furnPalette=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._tool="draw",this._wallDialog=null,this._drawWallField=null,this._rszSel=null,this._rszDrag=null,this._rszUndo=[],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._opShift=!1,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._warmRevivePending=!1,this._warmGen=++xr,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._senseRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._vacVisHandler=()=>{"visible"===document.visibilityState&&(this._vacJumpOnce=!0,this._skyElev=null,this.requestUpdate())},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._space=t,this._selId=null,this._restoreZoom(),this.requestUpdate())},this._drag=null,this._rlResize=null,this._holdFired=!1,this._cfgEpoch=0,this._modelCache=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 ko(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=Nr(()=>{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(Cr,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._saveConfigDebounced=Nr(()=>{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._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:vi(this._settings,{bgColor:null})||null,northDeg:Ss(this._settings,{}),bgMode:Ms(this._settings,{}),sunRays:Cs(this._settings,{}),weatherEntity:Ds(this._settings)||"",busy:!1}},this._openAlignDialog=()=>{if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[],e=function(t,e){const i=JSON.parse(JSON.stringify(t||[])),s=JSON.parse(JSON.stringify(e||{}));let n=0,o=0,r=0,a=0,l="",c=0;const h={};let d=5;for(const t of i){const e=yr(t);null!=t?.id&&(h[String(t.id)]=e),e>d&&(d=e)}const p=(t,e,i,s=!1)=>{if(!(t>gr||s))return;n++,t>r&&(r=t);const o=t*Fs*e;o>a&&(a=o,l=i)};for(const t of i){const e=yr(t),i=null!=t?.id?String(t.id):"";for(const s of t.rooms||[]){o++;let t=0;if(s.poly?.length)s.poly=s.poly.map(e=>{const i=[mr(e[0]),mr(e[1])];return t=Math.max(t,fr(e[0],e[1],i[0],i[1])),i});else if(null!=s.x&&null!=s.y){const e=s.x,i=s.y,n=s.w||0,o=s.h||0,r=mr(e+n),a=mr(i+o),l=mr(e),c=mr(i),h=Math.max(qs,r-l),d=Math.max(qs,a-c);t=vr(e,i,n,o,l,c,h,d),s.x=l,s.y=c,s.w=h,s.h=d}p(t,e,i)}for(const s of t.decor||[]){o++;let t=0;if("line"===s.kind){const e=[mr(s.x1),mr(s.y1)],i=[mr(s.x2),mr(s.y2)];t=Math.max(fr(s.x1,s.y1,e[0],e[1]),fr(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=mr(s.x),i=mr(s.y);if(null!=s.w&&null!=s.h){const n=mr(s.x+s.w),o=mr(s.y+s.h),r=Math.max(qs,n-e),a=Math.max(qs,o-i);t=vr(s.x,s.y,s.w,s.h,e,i,r,a),s.w=r,s.h=a}else t=fr(s.x,s.y,e,i);s.x=e,s.y=i}p(t,e,i)}for(const s of t.openings||[]){o++;const n=De([s.x,s.y],t.rooms||[],.025,{step:qs,length:Number(s.length)||0});if(!n)continue;const r=Number(s.angle),a=!(Number.isFinite(r)&&r===n.angle),l=br(s.x,s.y,Number.isFinite(r)?r:n.angle,Number(s.length)||0,n.x,n.y,n.angle);s.x=n.x,s.y=n.y,s.angle=n.angle,a&&c++,p(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;o++;const n=mr(i.x),r=mr(i.y),a=fr(i.x,i.y,n,r);s[t]={...i,x:n,y:r};const l="string"==typeof i.s?i.s:"";p(a,h[l]??d,l)}return{spaces:i,layout:s,report:{moved:n,total:o,maxShift:r,maxShiftCm:a,maxSpace:l,rotated:c},changed:n>0}}(t,this._layout||{}),i=Math.ceil(10*e.report.maxShiftCm)/10,s=t.find(t=>null!=t?.id&&String(t.id)===e.report.maxSpace),n=t.length>1&&s?String(s.title||s.id):"";this._alignDialog={report:e.report,spaces:e.spaces,layout:e.layout,cm:i,where:n,busy:!1}},this._openRulesDialog=()=>{if(!this._norm)return;const t=this._settings.icon_rules,e=(t&&t.length?t:dt).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)}_slideTo(t,e){if(t===this._space)return;const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;this._space=t,this._selId=null,this._restoreZoom(),i||(this._slide=e,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slide="",this.requestUpdate()},260),this.requestUpdate())}_cycleTick(){if(this._kiosk&&Number(this._config?.cycle)>0&&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,()=>Fi(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},kr)),!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))}disconnectedCallback(){document.removeEventListener("visibilitychange",this._vacVisHandler),this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._skySnapRaf&&(cancelAnimationFrame(this._skySnapRaf),this._skySnapRaf=0);for(const t of this._senseRt.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._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),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),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._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._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0)}if("decor"===this._mode)return"Delete"!==t.key&&"Backspace"!==t.key||!this._decorSel||t.target?.closest?.("input, textarea, select")?void("Escape"===t.key&&(t.preventDefault(),this._decorDraft?this._decorDraft=null: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)return;const e="Escape"===t.key||(t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase();if(e){if(this._roomDialog)return t.preventDefault(),void this._roomDialogCancel();if("draw"===this._tool&&this._path.length)return t.preventDefault(),void this._undoPoint();if(e)return"resize"===this._tool?(t.preventDefault(),this._rszDrag?void this._rszCancelDrag():(t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase()?void this._rszUndoPop():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?(t.preventDefault(),void(this._openWallAnchor?this._openWallAnchor=null:this._tool="draw")):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool||(t.preventDefault(),this._tool="draw"))}}_undoPoint(){this._path.length&&(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 $r.values())for(const e of t)clearTimeout(e.evict);$r.clear(),Mr=t&&t>0?t:1e4}static _warmBootStats(){let t=0,e=0;const i=[];for(const s of $r.values())for(const n of s)t++,n.dlg&&(e++,i.push(n.dlg.kind));return{keys:$r.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(Tr)||"{}")||{}}catch{this._zoomBySpace={}}try{const t=JSON.parse(localStorage.getItem(Pr)||"null");this._kioskScale={icon:Hi(t?.icon),font:Hi(t?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(Dr)||"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()}_warmAdopt(){if(this._config?.kiosk)return;const t=Sr(this._config);if(this._warmKey===t&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const e=this.parentNode,i=this._warmIdx(e),s=$r.get(t);if(!s||!s.length)return;const n=s.find(t=>t.owner===this._warmGen);if(n)return clearTimeout(n.evict),n.evict=0,n.freed=0,n.live=!0,this._warmSlot=n,void(this._warmKey=t);const{slot:o,sure:r}=((t,e,i,s)=>{const n=t=>{const e=!!i&&t.place?.deref()===i;return e&&t.idx===s?4:t.live?0:e?3:2};let o=null,r=0,a=0,l=null;for(const i of t){if(i.owner===e)continue;l=i;const t=n(i);t<=0||(t>r?(o=i,r=t,a=1):t===r&&a++)}return!o||a>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(s,this._warmGen,e,i);o&&(this._booting=!1,this._bootFading=!1,this._hdrH=o.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},kr)),this._warmKey=t,r?(clearTimeout(o.evict),o.evict=0,o.owner=this._warmGen,o.place=e?new WeakRef(e):null,o.idx=i,o.live=!0,this._warmSlot=o,this._warmVp=o.vp,this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,place:e?new WeakRef(e):null,idx:i,live:!0,hdrH:o.hdrH,stageH:o.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 n=$r.get(e);if(!t.live&&n&&n.length>1){const e=n.indexOf(t);e>=0&&n.splice(e,1)}},Mr+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=Sr(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=$r.get(i)||[];for(s.push(this._warmSlot),$r.set(i,s),this._warmTrim(s);$r.size>8;){const t=$r.keys().next().value;if(void 0===t||t===i)break;for(const e of $r.get(t)||[])clearTimeout(e.evict);$r.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._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._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>Mr)&&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"decorText":this._decorTextDialog={...e.data};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(Dr,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.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 Ns(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:n,excluded:o,iconRules:r}=t,a=!1!==n.group_lights,l=Vo(e,a),c=new Set(l.map(t=>t.area)),h=Oo(e),d=new Set(s.map(t=>t.binding)),p=[];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(d.has("device:"+t.id))continue;const n=h[t.id]||[],l=Io(e,t,n);let u=o.has(l)||"Group"===t.model||/scene/i.test(t.model||"")||/bridge/i.test((t.model||"")+(t.name||""))||"myheat"===l&&!!t.via_device_id;!u&&a&&c.has(s)&&"mdi:lightbulb"===Go(e,(t.name_by_user||t.name||"").trim(),t.model,n,r)&&(u=!0),u&&p.push("device:"+t.id)}return p}({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=pt(t)),this._rulesCompiled}get _fillColors(){return wi(this._settings)}get _excluded(){const t=this._settings.exclude_integrations;return t?new Set(t):ht}willUpdate(t){t?.has?.("hass")&&(this._vacTick(),this._senseTick()),this._skyPlan(),t.has("hass")&&this.hass&&(this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices())}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),n=Math.round(i+s);n>=0&&Math.abs(n-this._hdrH)>1&&(this._hdrH=n),n>=0&&!this._booting&&!this._config?.kiosk&&t.clientHeight>0&&this._warmPatch({hdrH:n,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._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null);const i=t?.config;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=>{(t?.data?.rev??-1)!==this._cfgRev&&this._reloadConfigOnly()},"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(),n=this._savedNav();!this._hashApplied&&s&&this._model.find(t=>t.id===s)?(this._space=s,this._hashApplied=!0):n?.space&&!this._navApplied&&!this._hashApplied&&this._model.find(t=>t.id===n.space)?(this._space=n.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(Cr)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1}this._regSignature="",this.requestUpdate()}async _reloadConfigOnly(t=!1){if(!t&&(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting))return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(),400));try{const t=await this.hass.callWS({type:"houseplan/config/get"}),e=t?.config;this._serverCfg=e&&Array.isArray(e.spaces)?e:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,"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,Fi(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._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")+":"+er(t,this._config?.language);e===this._regSignature&&this._devices.length||(this._regSignature=e,this._devices=Yo({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())}_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:n}=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:n,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=Ho(this.hass,i.entities);null!=s&&e.push(s)}return Ye(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),n=Math.min(...i);return{x:s,y:n,w:Math.max(...e)-s,h:Math.max(...i)-n}}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*sn(i)*1.3;for(const e of i.rooms){if(!e.area)continue;const n=this._devices.filter(t=>t.area===e.area&&t.space===i.id);if(!n.length)continue;const o=this._roomBounds(e),r=.1*Math.min(o.w,o.h),a=o.w-2*r,l=o.h-2*r,c=Math.max(1,Math.round(Math.sqrt(n.length*a/Math.max(l,1)))),h=Math.ceil(n.length/c),d=a/c,p=l/Math.max(h,1),u=n.map((t,e)=>({x:o.x+r+d*(e%c+.5),y:o.y+r+p*(Math.floor(e/c)+.5)}));Ze(u,o,s,.5*r),n.forEach((e,i)=>t[e.id]=Us(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*Ar,y:i.y*Ar}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[t.id]?this._defPos[t.id]:Us(en(e))}_savePos(t,e,i,s=!1){if(this._norm){const n=this._gridPitch,o=s?e:Math.round(e/n)*n,r=s?i:Math.round(i/n)*n,a=this._layout[t.id]?.k;this._layout={...this._layout,[t.id]:{s:t.space,x:Bs(o/Ar),y:Bs(r/Ar),...a?{k:a}:{}}}}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?ri(t.entities):null}_actEntity(t){return this._coverIndicator(t)||t.primary}_stateClass(t){if(!this._config?.live_states)return"";const e=this._coverIndicator(t);if(e){const t=this.hass.states[e];return t?"unavailable"===t.state?"unavail":ai(t.state)?"covermove":"":""}const i=(t.marker?.controls||[]).filter(Ai);if(i.length)return i.some(t=>"on"===this.hass.states[t]?.state)?"on":"";if(qo(this.hass,t))return"on";const s=this._actEntity(t),n=s?this.hass.states[s]:void 0;if(!n)return"";if("unavailable"===n.state)return"unavail";const o=s.split(".")[0];if(["light","switch","fan","humidifier"].includes(o))return"on"===n.state?"on":"";if("climate"===o){const t=n.attributes?.hvac_action;return null!=t?["heating","cooling","drying","fan"].includes(t)?"on":"":["off","unknown"].includes(n.state)?"":"on"}if("cover"===o)return ai(n.state)?"covermove":"";if("valve"===o)return["open","opening"].includes(n.state)?"open":"";if("lock"===o)return["unlocked","open"].includes(n.state)?"open":"";if("binary_sensor"===o){const e=n.attributes?.device_class;if(["door","window","garage_door","opening","gas","smoke","moisture","problem"].includes(e))return"on"===n.state?"open":"";if("motion"===e){const e=this._senseRt.get(t.id);return e&&e.flashTs&&Date.now()-e.flashTs<3300?e.gen%2==0?"senseflash sf2":"senseflash":""}if(["occupancy","presence"].includes(e))return"on"===n.state?"sensehold":""}return"media_player"===o?["playing","on"].includes(n.state)?"on":"":"vacuum"===o&&["cleaning","returning"].includes(n.state)?"on":""}_liveTemp(t){if(!this._config?.show_temperature)return null;if(!0===t.marker?.use_climate_temp){const e=Wo(this.hass,t.entities);if(null!=e)return e}return"mdi:thermometer"!==t.icon&&"mdi:air-filter"!==t.icon?null:Uo(this.hass,t.entities)}_bindingHasClimate(t){if(t.startsWith("entity:"))return t.slice(7).startsWith("climate.");if(t.startsWith("device:")){const e=t.slice(7);for(const[t,i]of Object.entries(this.hass?.entities||{}))if(i?.device_id===e&&t.startsWith("climate."))return!0}return!1}_bindingCoverEntity(t){if(t.startsWith("entity:"))return ri([t.slice(7)]);if(t.startsWith("device:")){const e=t.slice(7);return ri(Object.entries(this.hass?.entities||{}).filter(([,t])=>t?.device_id===e).map(([t])=>t))}return null}_bindingCoverTap(t){const e=this._bindingCoverEntity(t);if(!e)return!1;const i=String(this.hass?.states?.[e]?.attributes?.device_class||"");return!oi.has(i)}_liveHum(t){return this._config?.show_temperature&&t.primary&&Bo(this.hass,t.primary)?jo(this.hass,t.entities):null}_openMoreInfo(t){t?Er(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),n=s?s.split(".")[0]:null,o=(t,i)=>{e.marker?.tap_confirm?this._tapConfirm={text:t,exec:i}:i()},r=(e.marker?.controls||[]).filter(Ai);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 o(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 n=t||e||("light"===i?"toggle":"info");return"more-info"===n?"more-info":"run"===n?"run"===t?"run":"info":"cover"===n?"cover"!==t||"cover"!==i||oi.has(String(s||""))?"info":"cover":"toggle"!==n||!i||ni.has(i)?"info":"toggle"===t?"toggle":si.has(i)?"cover"===i&&oi.has(String(s||""))?"info":"toggle":"info"}(e.tapAction,void 0,n,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 n=s.attributes?.friendly_name||t;return void o(this._t("confirm.tap_run",{name:n}),()=>{this.hass.callService(i.domain,i.service,{entity_id:t}).then(()=>this._showToast(this._t("toast.run_started",{name:n}))).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 o(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?o(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 ir(er(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,n=Math.sin(i)*t.rlen/2,o=Gs([[t.rx-s,t.ry-n],[t.rx+s,t.ry+n]]);o&&e.push(o)}const t=this._decorH;for(const i of this._decorList){const s=Gs("line"===i.kind?[[i.x1*Ar,i.y1*t],[i.x2*Ar,i.y2*t]]:[[i.x*Ar,i.y*t],[(i.x+(i.w||0))*Ar,(i.y+(i.h||0))*t]]);s&&e.push(s)}}return Ys(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=Qs(this._contentItems(t));let n=s.all||tn(t),o=this._showFar?n:s.core||tn(t);return e&&e.id===t.id&&i&&e.grow&&(o=Rr(e.rect,o),n=Rr(e.all,n)),this._frame={id:t.id,model:t,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:n,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._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?V:W`
${this._t("canvas.far_objects",{n:this._outliers})} @@ -2635,7 +2635,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow style="left:${s.toFixed(1)}%;top:${n.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:Xe(t,this._stageAspect())}_screenToVb(t,e){const i=this._stageEl,s=this._viewOr(this._baseVb()),n=i?.clientWidth||1,o=i?.clientHeight||1;return[s.x+t/n*s.w,s.y+e/o*s.h]}_clampView(t,e){const i=(t,e,i,s)=>{const n=1*Math.max(e,s),o=i-n,r=i+s-e+n;return Math.max(Math.min(o,r),Math.min(Math.max(o,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(),n=Xe(s,this._stageAspect()),o=Math.min(Or.ZOOM_MAX,Math.max(Or.ZOOM_MIN,t)),r=n.w/o,a=n.h/o,l=this._viewOr(s),c=e??l.x+l.w/2,h=i??l.y+l.h/2;this._zoom=o,this._view=this._clampView({x:c-r/2,y:h-a/2,w:r,h:a},n)}_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},wr)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_refitView(){if(!this._stageEl)return;const t=this._view;this._applyView(this._zoom,t?t.x+t.w/2:void 0,t?t.y+t.h/2:void 0),this.requestUpdate()}_zoomAt(t,e,i){const s=this._stageEl;if(!s)return;const n=Xe(this._baseVb(),this._stageAspect()),o=Math.min(Or.ZOOM_MAX,Math.max(Or.ZOOM_MIN,i)),r=s.clientWidth,a=s.clientHeight,l=this._screenToVb(t,e),c=n.w/o,h=n.h/o;this._zoom=o,this._view=this._clampView({x:l[0]-t/r*c,y:l[1]-e/a*h,w:c,h:h},n)}_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=Xe(t,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(Dr,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._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId)if(this._decorDraft?.pid!==t.pointerId)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,n=(t[0].y+t[1].y)/2-i.top;this._zoomAt(s,n,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(),n=this._viewOr(t),o=Xe(t,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-e/(s.clientWidth||1)*n.w,y:this._panStart.vy-i/(s.clientHeight||1)*n.h,w:n.w,h:n.h},o)}}}else this._markupMove(t);else this._decorMoveUpdate(t);else this._decorDraft={...this._decorDraft,b:this._snap(this._svgPoint(t),t)};else this._bdMove(t);else this._dtMove(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 n="pan"===this._panLock?null:function(t,e,i,s,n,o=60){if(i>1.001||s.length<2)return null;if(Math.abs(t)t.id),this._space);n&&(this._slideTo(n,i<0?"left":"right"),this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}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._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()}_clickRoom(t){var e;!this._suppressClick&&t.area&&(e="/config/areas/area/"+t.area,history.pushState(null,"",e),Rr(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},Nr(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(),n=i.getBoundingClientRect(),o=this._viewOr(s),r=(t.clientX-this._drag.sx)/n.width*o.w,a=(t.clientY-this._drag.sy)/n.height*o.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=Ws(this._drag.ox+r),c=Ws(this._drag.oy+a);this._savePos(e,l,c,t.shiftKey)}_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 Or._noHoverMq||Or._touchSeen}_notePointer(t){"touch"!==t.pointerType&&"pen"!==t.pointerType||(Or._touchSeen=!0,this._tip&&(this._tip=null))}_showTip(t,e,i,s,n){this._noHover||this._drag||(this._tip={x:t.clientX,y:t.clientY,title:e,meta:i,lqi:s,temp:n})}get _gridPitch(){return Ls}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 $e(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,Pr}get _segments(){const t=this._curSpaceCfg,e=this._spaceH;return Ce(t?.rooms||[]).map(t=>[t[0]*Pr,t[1]*e,t[2]*Pr,t[3]*e])}_savedNav(){try{return JSON.parse(localStorage.getItem(Tr)||"null")}catch{return null}}_saveNav(){try{localStorage.setItem(Tr,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._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(this._mode=t,e&&(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._rszSel=null,this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._rszUndo=[],this._tip=null,this._decorDraft=null,this._decorSel=null,this._decorTool="decor"===t&&this._curSpaceCfg?.plan_url?"backdrop":"select",this._bdDrag=null,this._dtDrag=null,this._dtBox=null,"plan"===t&&this._primeDrawWallField(),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=kn(15,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?kn(15,this._imperial):this._drawWallField}get _drawWallCm(){return $n(this._drawWallFieldValue,this._imperial)}_svgPoint(t){const e=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t,e=!1){const i="boolean"==typeof e?e:!!e?.shiftKey,s=this._gridPitch;return i?[Ws(t[0]),Ws(t[1])]:[Ws(ke(t[0],s)),Ws(ke(t[1],s))]}_samePt(t,e){return Pe(t,e)}_dropLegacySegments(){for(const t of this._serverCfg?.spaces||[])if(delete t.segments,Array.isArray(t.walls)){const e=lo(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=En(t.walls,t.rooms||[],Hs,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()}_roomAt(t){return this._spaceModel().rooms.find(e=>{const i=Se(e);return!!i&&Oe(t,i)})}_overlapRoom(t){return this._spaceModel().rooms.find(e=>{const i=Se(e);return!!i&&We(t,i)})}_pointInRoom(t,e){return e.poly?Ae(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}_markupClick(t){if(this._vacFit)return;if(!this._markup)return;if(this._suppressClick)return;if(this._drag||this._rlResize)return;const e=t.composedPath?.()||[];if(e.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;const i=this._svgPoint(t);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._deleteWallClick(i);if("opening"===this._tool)return void this._openingClick(i,t.shiftKey);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("split"===this._tool)return void this._splitClick(i,t.shiftKey);const s=this._snap(i,t),n=this._path.length>=3&&this._samePt(s,this._path[0]);if(!this._path.length)return void(this._path=[s]);const o=this._path[this._path.length-1];if(!this._samePt(s,o)){if(n){const t=this._overlapRoom(this._path);return t?void this._showToast(this._t("toast.room_overlap",{name:t.name||""})):(this._path=[...this._path,s],this._cursorPt=null,this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),void(this._roomDialog=!0))}this._path=[...this._path,s]}}_rszRooms(){const t=[];for(const e of this._spaceModel().rooms){const i=e.id?Se(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(){const t=this._curSpaceCfg;return JSON.stringify({rooms:t?.rooms||[],openings:t?.openings||[],walls:t?.walls||[],open_spans:t?.open_spans||[]})}_rszRestore(t){const e=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!e)return;const i=JSON.parse(t);e.rooms=i.rooms,e.openings=i.openings,Array.isArray(i.walls)&&i.walls.length?e.walls=i.walls:delete e.walls,Array.isArray(i.open_spans)&&i.open_spans.length?e.open_spans=i.open_spans:delete e.open_spans,this._cfgEpoch++,this.requestUpdate()}_rszApplyPreview(t,e){const i=this._rszDrag,s=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!i||!s)return;const n=JSON.parse(i.snap),o={...s,rooms:n.rooms,openings:n.openings,walls:n.walls},r=this._spaceH;for(const[e,i]of Object.entries(t)){const t=o.rooms.find(t=>t.id===e);t&&(t.poly=i.map(t=>[t[0]/Pr,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=(o.openings||[]).find(e=>e.id===t);e&&(e.x=i[0]/Pr,e.y=i[1]/r)}this._rszPreview={space:this._space,sp:o},this._cfgEpoch++}_rszEdgeDown(t,e,i){if("resize"!==this._tool||this._rszDrag)return;t.stopPropagation(),t.preventDefault(),Nr(t);const s=this._rszRooms(),n=function(t,e,i){const s=t.find(t=>t.id===e);if(!s||!s.poly||s.poly.length<3)return null;if(i<0||i>=s.poly.length)return null;const n=[...s.poly[i]],o=[...s.poly[(i+1)%s.poly.length]];return{roomId:e,edge:i,a:n,b:o,n:Ji(s.poly,i)}}(s,e,i);n&&(this._rszDrag={kind:"edge",pid:t.pointerId,roomId:e,plan:n,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(),Nr(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 s=e.plan,n=(i[0]-s.a[0])*s.n[0]+(i[1]-s.a[1])*s.n[1],o=this._snap([s.a[0]+s.n[0]*n,s.a[1]+s.n[1]*n],t);let r=(o[0]-s.a[0])*s.n[0]+(o[1]-s.a[1])*s.n[1];if(r=function(t,e,i,s,n,o){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(n,1e-6);for(let s=0;s<4096&&a>1e-9;s++,a-=l){const s=r*a;if(cs(t,e,i,s,o))return s}return 0}(e.rooms,e.openings,s,r,this._gridPitch,this._rszOpts()),r===e.d&&e.moved)return;e.d=r,e.moved=!0;const a=rs(e.rooms,e.openings,s,r,this._rszOpts().eps);e.changed=Object.keys(a.polys),this._rszApplyPreview(a.polys,a.openings),this._rszLive=this._rszEdgeLabels(a,s)}else{const s=e.fixed,n=this._snap(i,t);let o=Math.hypot(n[0]-s[0],n[1]-s[1])/(e.span0||1);if(o=Math.max(.05,Math.min(20,o)),o=function(t,e,i,s,n,o){if(!Number.isFinite(n)||n<=0)return 1;if(ds(t,e,i,s,n,o))return n;let r=1,a=n;for(let n=0;n<28;n++){const n=(r+a)/2;ds(t,e,i,s,n,o)?r=n:a=n}return r}(e.rooms,e.openings,e.roomId,s,o,this._rszOpts()),o===e.k&&e.moved)return;e.k=o,e.moved=!0;const r=e.rooms.find(t=>t.id===e.roomId),a=e.rooms.filter(t=>t.id!==e.roomId).map(t=>t.poly),l=hs(r,e.openings,a,s,o,2*this._rszOpts().eps);e.changed=[e.roomId],this._rszApplyPreview({[e.roomId]:l.poly},l.openings),this._rszLive=this._rszScaleLabels(l.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=this._curSpaceCfg;if(s){s.rooms=i.sp.rooms,s.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?s.walls=i.sp.walls:delete s.walls);for(const t of e.changed){const e=s.rooms.find(e=>e.id===t);e?.poly&&(e.poly=is(e.poly,1e-9))}const t=this._spaceH,n=[],o=[];for(const i of e.changed){const r=e.rooms.find(t=>t.id===i),a=s.rooms.find(t=>t.id===i);if(!r||!a?.poly)continue;const l=a.poly.map(e=>[e[0]*Pr,e[1]*t]);if(r.poly.length===l.length)for(let t=0;t30&&this._rszUndo.shift(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),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())}_rszUndoPop(){for(let t=this._rszUndo.length-1;t>=0;t--){if(this._rszUndo[t].space!==this._space)continue;const[e]=this._rszUndo.splice(t,1);return this._rszRestore(e.snap),void this._saveConfig()}}_rszEdgeLabels(t,e){const i=this._rszDrag,s=[],n=t.polys[e.roomId]||i.rooms.find(t=>t.id===e.roomId).poly,o=n.length,r=e.edge,a=(r+1)%o;for(const[t,e]of[[n[(r-1+o)%o],n[r]],[n[r],n[a]],[n[a],n[(a+1)%o]]])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,c=Object.keys(t.polys).length?Object.keys(t.polys):[e.roomId],h=this._spaceWalls,d=this._openPairs().flatMap(t=>t.segs);for(const e of c){const n=t.polys[e]||i.rooms.find(t=>t.id===e).poly,o=h.length&&Qn(Object.entries(t.polys).map(([t,e])=>({id:t,poly:e})),e,h,d,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)||n,r=Le(o);s.push({x:r[0],y:r[1],text:us(ps(o,this._gridPitch,this._cellCm),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]),n=Math.max(...i)-Math.min(...i),o=Math.max(...s)-Math.min(...s),r=this._spaceWalls,a=r.length&&this._rszSel&&Qn([{id:this._rszSel,poly:t}],this._rszSel,r,[],this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)||t,l=Le(a);return[{x:Math.min(...i),y:Math.min(...s),text:`${this._fmtLen([0,0],[n,0])} × ${this._fmtLen([0,0],[o,0])}`},{x:l[0],y:l[1],text:us(ps(a,this._gridPitch,this._cellCm),e),area:!0}]}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),i=e/2,s=t=>t.toFixed(1),n=`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)}`,o=[],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:Xe(t,this._stageAspect())}_screenToVb(t,e){const i=this._stageEl,s=this._viewOr(this._baseVb()),n=i?.clientWidth||1,o=i?.clientHeight||1;return[s.x+t/n*s.w,s.y+e/o*s.h]}_clampView(t,e){const i=(t,e,i,s)=>{const n=1*Math.max(e,s),o=i-n,r=i+s-e+n;return Math.max(Math.min(o,r),Math.min(Math.max(o,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(),n=Xe(s,this._stageAspect()),o=Math.min(Ir.ZOOM_MAX,Math.max(Ir.ZOOM_MIN,t)),r=n.w/o,a=n.h/o,l=this._viewOr(s),c=e??l.x+l.w/2,h=i??l.y+l.h/2;this._zoom=o,this._view=this._clampView({x:c-r/2,y:h-a/2,w:r,h:a},n)}_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},kr)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_refitView(){if(!this._stageEl)return;const t=this._view;this._applyView(this._zoom,t?t.x+t.w/2:void 0,t?t.y+t.h/2:void 0),this.requestUpdate()}_zoomAt(t,e,i){const s=this._stageEl;if(!s)return;const n=Xe(this._baseVb(),this._stageAspect()),o=Math.min(Ir.ZOOM_MAX,Math.max(Ir.ZOOM_MIN,i)),r=s.clientWidth,a=s.clientHeight,l=this._screenToVb(t,e),c=n.w/o,h=n.h/o;this._zoom=o,this._view=this._clampView({x:l[0]-t/r*c,y:l[1]-e/a*h,w:c,h:h},n)}_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=Xe(t,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(Tr,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._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId)if(this._decorDraft?.pid!==t.pointerId)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,n=(t[0].y+t[1].y)/2-i.top;this._zoomAt(s,n,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(),n=this._viewOr(t),o=Xe(t,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-e/(s.clientWidth||1)*n.w,y:this._panStart.vy-i/(s.clientHeight||1)*n.h,w:n.w,h:n.h},o)}}}else this._markupMove(t);else this._decorMoveUpdate(t);else this._decorDraft={...this._decorDraft,b:this._snap(this._svgPoint(t),t)};else this._bdMove(t);else this._dtMove(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 n="pan"===this._panLock?null:function(t,e,i,s,n,o=60){if(i>1.001||s.length<2)return null;if(Math.abs(t)t.id),this._space);n&&(this._slideTo(n,i<0?"left":"right"),this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}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._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()}_clickRoom(t){var e;!this._suppressClick&&t.area&&(e="/config/areas/area/"+t.area,history.pushState(null,"",e),Er(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},Or(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(),n=i.getBoundingClientRect(),o=this._viewOr(s),r=(t.clientX-this._drag.sx)/n.width*o.w,a=(t.clientY-this._drag.sy)/n.height*o.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=Ws(this._drag.ox+r),c=Ws(this._drag.oy+a);this._savePos(e,l,c,t.shiftKey)}_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 Ir._noHoverMq||Ir._touchSeen}_notePointer(t){"touch"!==t.pointerType&&"pen"!==t.pointerType||(Ir._touchSeen=!0,this._tip&&(this._tip=null))}_showTip(t,e,i,s,n){this._noHover||this._drag||(this._tip={x:t.clientX,y:t.clientY,title:e,meta:i,lqi:s,temp:n})}get _gridPitch(){return Ls}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 $e(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,Ar}get _segments(){const t=this._curSpaceCfg,e=this._spaceH;return Ce(t?.rooms||[]).map(t=>[t[0]*Ar,t[1]*e,t[2]*Ar,t[3]*e])}_savedNav(){try{return JSON.parse(localStorage.getItem(zr)||"null")}catch{return null}}_saveNav(){try{localStorage.setItem(zr,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._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(this._mode=t,e&&(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._rszSel=null,this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._rszUndo=[],this._tip=null,this._decorDraft=null,this._decorSel=null,this._decorTool="decor"===t&&this._curSpaceCfg?.plan_url?"backdrop":"select",this._bdDrag=null,this._dtDrag=null,this._dtBox=null,"plan"===t&&this._primeDrawWallField(),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=kn(15,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?kn(15,this._imperial):this._drawWallField}get _drawWallCm(){return $n(this._drawWallFieldValue,this._imperial)}_svgPoint(t){const e=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t,e=!1){const i="boolean"==typeof e?e:!!e?.shiftKey,s=this._gridPitch;return i?[Ws(t[0]),Ws(t[1])]:[Ws(ke(t[0],s)),Ws(ke(t[1],s))]}_samePt(t,e){return Pe(t,e)}_dropLegacySegments(){for(const t of this._serverCfg?.spaces||[])if(delete t.segments,Array.isArray(t.walls)){const e=lo(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=En(t.walls,t.rooms||[],qs,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()}_roomAt(t){return this._spaceModel().rooms.find(e=>{const i=Se(e);return!!i&&Oe(t,i)})}_overlapRoom(t){return this._spaceModel().rooms.find(e=>{const i=Se(e);return!!i&&We(t,i)})}_pointInRoom(t,e){return e.poly?Ae(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}_markupClick(t){if(this._vacFit)return;if(!this._markup)return;if(this._suppressClick)return;if(this._drag||this._rlResize)return;const e=t.composedPath?.()||[];if(e.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;const i=this._svgPoint(t);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._deleteWallClick(i);if("opening"===this._tool)return void this._openingClick(i,t.shiftKey);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("split"===this._tool)return void this._splitClick(i,t.shiftKey);const s=this._snap(i,t),n=this._path.length>=3&&this._samePt(s,this._path[0]);if(!this._path.length)return void(this._path=[s]);const o=this._path[this._path.length-1];if(!this._samePt(s,o)){if(n){const t=this._overlapRoom(this._path);return t?void this._showToast(this._t("toast.room_overlap",{name:t.name||""})):(this._path=[...this._path,s],this._cursorPt=null,this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),void(this._roomDialog=!0))}this._path=[...this._path,s]}}_rszRooms(){const t=[];for(const e of this._spaceModel().rooms){const i=e.id?Se(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(){const t=this._curSpaceCfg;return JSON.stringify({rooms:t?.rooms||[],openings:t?.openings||[],walls:t?.walls||[],open_spans:t?.open_spans||[]})}_rszRestore(t){const e=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!e)return;const i=JSON.parse(t);e.rooms=i.rooms,e.openings=i.openings,Array.isArray(i.walls)&&i.walls.length?e.walls=i.walls:delete e.walls,Array.isArray(i.open_spans)&&i.open_spans.length?e.open_spans=i.open_spans:delete e.open_spans,this._cfgEpoch++,this.requestUpdate()}_rszApplyPreview(t,e){const i=this._rszDrag,s=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!i||!s)return;const n=JSON.parse(i.snap),o={...s,rooms:n.rooms,openings:n.openings,walls:n.walls,open_spans:n.open_spans};Array.isArray(n.open_spans)&&n.open_spans.length||delete o.open_spans;const r=this._spaceH;for(const[e,i]of Object.entries(t)){const t=o.rooms.find(t=>t.id===e);t&&(t.poly=i.map(t=>[t[0]/Ar,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=(o.openings||[]).find(e=>e.id===t);e&&(e.x=i[0]/Ar,e.y=i[1]/r)}const a=[],l=[];for(const t of i.changed){const e=i.rooms.find(e=>e.id===t),s=o.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const n=s.poly.map(t=>[t[0]*Ar,t[1]*r]);if(e.poly.length===n.length)for(let t=0;t0?n:1,a=Math.max(.5*s,1e-9)*r,l=new Set,c=[];for(const n of t){let t=o.get(n.key)||"";if(!t){const o=Tn([n],r)[0];if(o)for(let n=0;n1.000001)continue;if(Jn(o.x,o.y,l[0],l[1],c[0],c[1])>a)continue;const m=h[0]+(d[0]-h[0])*Math.max(0,Math.min(1,g)),f=h[1]+(d[1]-h[1])*Math.max(0,Math.min(1,g)),[v,b]=Mn(h,d),y=Math.max(s*r,1e-6);t=Dn([m-v*y,f-b*y],[m+v*y,f+b*y],s,r);break}}t||(t=n.key),l.has(t)||(l.add(t),c.push({key:t,cm:n.cm}))}return c}(o.walls,a,l,this._wallKeyPitch,Ar))}this._rszPreview={space:this._space,sp:o},this._cfgEpoch++}_rszEdgeDown(t,e,i){if("resize"!==this._tool||this._rszDrag)return;t.stopPropagation(),t.preventDefault(),Or(t);const s=this._rszRooms(),n=function(t,e,i){const s=t.find(t=>t.id===e);if(!s||!s.poly||s.poly.length<3)return null;if(i<0||i>=s.poly.length)return null;const n=[...s.poly[i]],o=[...s.poly[(i+1)%s.poly.length]];return{roomId:e,edge:i,a:n,b:o,n:Ji(s.poly,i)}}(s,e,i);n&&(this._rszDrag={kind:"edge",pid:t.pointerId,roomId:e,plan:n,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(),Or(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 s=e.plan,n=(i[0]-s.a[0])*s.n[0]+(i[1]-s.a[1])*s.n[1],o=this._snap([s.a[0]+s.n[0]*n,s.a[1]+s.n[1]*n],t);let r=(o[0]-s.a[0])*s.n[0]+(o[1]-s.a[1])*s.n[1];if(r=function(t,e,i,s,n,o){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(n,1e-6);for(let s=0;s<4096&&a>1e-9;s++,a-=l){const s=r*a;if(cs(t,e,i,s,o))return s}return 0}(e.rooms,e.openings,s,r,this._gridPitch,this._rszOpts()),r===e.d&&e.moved)return;e.d=r,e.moved=!0;const a=rs(e.rooms,e.openings,s,r,this._rszOpts().eps);e.changed=Object.keys(a.polys),this._rszApplyPreview(a.polys,a.openings),this._rszLive=this._rszEdgeLabels(a,s)}else{const s=e.fixed,n=this._snap(i,t);let o=Math.hypot(n[0]-s[0],n[1]-s[1])/(e.span0||1);if(o=Math.max(.05,Math.min(20,o)),o=function(t,e,i,s,n,o){if(!Number.isFinite(n)||n<=0)return 1;if(ds(t,e,i,s,n,o))return n;let r=1,a=n;for(let n=0;n<28;n++){const n=(r+a)/2;ds(t,e,i,s,n,o)?r=n:a=n}return r}(e.rooms,e.openings,e.roomId,s,o,this._rszOpts()),o===e.k&&e.moved)return;e.k=o,e.moved=!0;const r=e.rooms.find(t=>t.id===e.roomId),a=e.rooms.filter(t=>t.id!==e.roomId).map(t=>t.poly),l=hs(r,e.openings,a,s,o,2*this._rszOpts().eps);e.changed=[e.roomId],this._rszApplyPreview({[e.roomId]:l.poly},l.openings),this._rszLive=this._rszScaleLabels(l.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=this._curSpaceCfg;if(s){s.rooms=i.sp.rooms,s.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?s.walls=i.sp.walls:delete s.walls),Array.isArray(i.sp.open_spans)&&i.sp.open_spans.length?s.open_spans=i.sp.open_spans:delete s.open_spans;for(const t of e.changed){const e=s.rooms.find(e=>e.id===t);e?.poly&&(e.poly=is(e.poly,1e-9))}this._commitOpenSpans(),Array.isArray(s.walls)&&s.walls.length&&(s.walls=En(s.walls,s.rooms||[],qs,1,this._cfgOpenCuts()),s.walls.length||delete s.walls)}this._rszUndo.push({space:this._space,snap:e.snap}),this._rszUndo.length>30&&this._rszUndo.shift(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),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())}_rszUndoPop(){for(let t=this._rszUndo.length-1;t>=0;t--){if(this._rszUndo[t].space!==this._space)continue;const[e]=this._rszUndo.splice(t,1);return this._rszRestore(e.snap),void this._saveConfig()}}_rszEdgeLabels(t,e){const i=this._rszDrag,s=[],n=t.polys[e.roomId]||i.rooms.find(t=>t.id===e.roomId).poly,o=n.length,r=e.edge,a=(r+1)%o;for(const[t,e]of[[n[(r-1+o)%o],n[r]],[n[r],n[a]],[n[a],n[(a+1)%o]]])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,c=Object.keys(t.polys).length?Object.keys(t.polys):[e.roomId],h=this._spaceWalls,d=this._openPairs().flatMap(t=>t.segs);for(const e of c){const n=t.polys[e]||i.rooms.find(t=>t.id===e).poly,o=h.length&&Qn(Object.entries(t.polys).map(([t,e])=>({id:t,poly:e})),e,h,d,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)||n,r=Le(o);s.push({x:r[0],y:r[1],text:us(ps(o,this._gridPitch,this._cellCm),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]),n=Math.max(...i)-Math.min(...i),o=Math.max(...s)-Math.min(...s),r=this._spaceWalls,a=r.length&&this._rszSel&&Qn([{id:this._rszSel,poly:t}],this._rszSel,r,[],this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)||t,l=Le(a);return[{x:Math.min(...i),y:Math.min(...s),text:`${this._fmtLen([0,0],[n,0])} × ${this._fmtLen([0,0],[o,0])}`},{x:l[0],y:l[1],text:us(ps(a,this._gridPitch,this._cellCm),e),area:!0}]}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),i=e/2,s=t=>t.toFixed(1),n=`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)}`,o=[],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)} @@ -2645,7 +2645,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)}>`),o.push(B``)}return B`${o}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH;return(t?.openings||[]).map(t=>({...t,rx:t.x*Pr,ry:t.y*e,rlen:t.length*Pr}))}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return Pr}_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._snap(this._svgPoint(t),t);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},Nr(t),!0}if("text"===e){const e=this._snap(this._svgPoint(t),t);return this._decorTextDialog={x:Bs(e[0]/Pr),y:Bs(e[1]/this._decorH),text:"",color:this._decorStyle.color},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t),!0);if(this._decorSel=null,this._bdMovable){const e=this._bdRect,i=this._svgPoint(t);if(i[0]>=e.x&&i[0]<=e.x+e.w&&i[1]>=e.y&&i[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(Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])t.id!==e.id),this._decorSel===e.id&&(this._decorSel=null),this._saveConfig(),void this.requestUpdate()}this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1},Nr(t)}}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,n=("line"===s.kind?s.x1:s.x)*Pr,o=("line"===s.kind?s.y1:s.y)*this._decorH,r=this._snap([n+(i[0]-e.start[0]),o+(i[1]-e.start[1])],t);let a=(r[0]-n)/Pr,l=(r[1]-o)/this._decorH;const c=e.orig,h="line"===c.kind?Math.min(c.x1,c.x2):c.x,d="line"===c.kind?Math.min(c.y1,c.y2):c.y,p="line"===c.kind?Math.abs(c.x2-c.x1):c.w||0,u="line"===c.kind?Math.abs(c.y2-c.y1):c.h||0,_=Os;a=Math.max(-_-h,Math.min(_-h-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){"decor"===this._mode&&"select"===this._decorTool&&"text"===t.kind&&this._decorOpenText(t)}_decorOpenText(t){this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:t.text,color:t.color,entity:t.entity||"",attr:t.attr||"",unit:t.unit||""}}_decorTextLink(t){const e={},i=(t.entity||"").trim();if(!i)return e;e.entity=i;const s=(t.attr||"").trim();s&&(e.attr=s);const n=(t.unit||"").trim();return n&&(e.unit=n),e}_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._curSpaceCfg,s=this._decorTextLink(t);if(t.id)i.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;const{entity:n,attr:o,unit:r,...a}=i;return{...a,text:e,color:t.color,...s}});else{const n="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);i.decor=[...this._decorList,{id:n,kind:"text",x:t.x,y:t.y,text:e,color:t.color,...s}],this._decorSel=n}this._decorTextDialog=null,this._saveConfig(),this.requestUpdate()}get _dtSel(){if("decor"!==this._mode||"select"!==this._decorTool||!this._decorSel)return null;const t=this._decorList.find(t=>t.id===this._decorSel);return!t||"text"!==t.kind&&"furniture"!==t.kind?null:t}_dtPivot(t){return"furniture"===t.kind?[(t.x+t.w/2)*Pr,(t.y+t.h/2)*this._decorH]:[t.x*Pr,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{size:s,...n}=i,o={...n};return void 0!==e.scale?o.scale=Number(e.scale.toFixed(4)):void 0===i.scale&&void 0!==s&&(o.scale=pi(i)),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){const s=this._dtSel;if(!s)return;t.stopPropagation(),t.preventDefault();const[n,o]=this._dtPivot(s),r=this._svgPoint(t),a="furniture"===s.kind;this._dtDrag={id:s.id,kind:e,pid:t.pointerId,ax:n,ay:o,r0:Math.hypot(r[0]-n,r[1]-o),a0:180*Math.atan2(r[1]-o,r[0]-n)/Math.PI,scale0:a?1:pi(s),angle0:Number(s.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:a?{x:s.x*Pr,y:s.y*this._decorH,w:s.w*Pr,h:s.h*this._decorH,angle:Number(s.angle)||0}:void 0,moved:!1},Nr(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if("scale"===e.kind&&e.orig){const s=t.shiftKey?0:this._gridPitch,n=s>0?s:.25*this._gridPitch,o=function(t,e,i,s,n,o=0,r=1e-6){const a=(Number(t.angle)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=-Math.sin(a),d=Math.cos(a),p=t.x+t.w/2,u=t.y+t.h/2,_=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,m=p+_*l+g*h,f=u+_*c+g*d,v=s-m,b=n-f;let y=(v*l+b*c)*(e>0?1:-1),w=(v*h+b*d)*(i>0?1:-1);return o>0&&(y=Math.round(y/o)*o,w=Math.round(w/o)*o),y=Math.max(r,y),w=Math.max(r,w),{x:m+(e>0?y/2:-y/2)*l+(i>0?w/2:-w/2)*h-y/2,y:f+(e>0?y/2:-y/2)*c+(i>0?w/2:-w/2)*d-w/2,w:y,h:w}}(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],s,n);return(Math.abs(o.w-e.orig.w)>1e-6||Math.abs(o.h-e.orig.h)>1e-6)&&(e.moved=!0),void this._furnApplyBox(e.id,o)}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.min(20,Math.max(.15,e.scale0*(t/e.r0)));return Math.abs(s-e.scale0)>1e-6&&(e.moved=!0),void this._dtApply(e.id,{scale:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let n=e.angle0+(s-e.a0);t.shiftKey||(n=5*Math.round(n/5)),n=(n%360+360)%360,n>180&&(n-=360),Math.abs(n-e.angle0)>1e-6&&(e.moved=!0),this._dtApply(e.id,{angle:n})}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&this._saveConfig(),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("furniture"===t.kind)e={id:t.id,x:t.x*Pr,y:t.y*this._decorH,w:t.w*Pr,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())}_decorDeleteSel(){if(!this._decorSel)return;this._curSpaceCfg.decor=this._decorList.filter(t=>t.id!==this._decorSel),this._decorSel=null,this._saveConfig(),this.requestUpdate()}get _furnWalls(){return this._segments}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=dn(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){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const n=Pr,o=this._decorH,r=gn(pn(i.w,this._cellCm,this._gridPitch,n)),a=gn(pn(i.h,this._cellCm,this._gridPitch,n)),l=this._snap(t,e);let c=l[0],h=l[1],d=0;if(!e.shiftKey){const t=bn(c,h,a*o,this._furnWalls,this._furnWallReach,this._gridPitch);t&&(c=t.cx,h=t.cy,d=t.angle)}const p="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),u={id:p,kind:"furniture",symbol:i.symbol,x:Bs(c/n-r/2),y:Bs(h/o-a/2),w:r,h:a,color:this._decorStyle.color,width:this._decorStyle.width};d&&(u.angle=Number(d.toFixed(2))),s.decor=[...this._decorList,u],this._decorSel=p,this._decorTool="select",this._furnPalette=null,this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(t){const e=this._decorMove,i=e.orig,s=this._curSpaceCfg;if(!s)return;const n=Pr,o=this._decorH,r=this._svgPoint(t),a=(i.x+i.w/2)*n+(r[0]-e.start[0]),l=(i.y+i.h/2)*o+(r[1]-e.start[1]);let c,h,d=Number(i.angle)||0;const p=t.shiftKey?null:bn(a,l,i.h*o,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)c=p.cx/n-i.w/2,h=p.cy/o-i.h/2,d=p.angle;else{const e=this._snap([a-i.w/2*n,l-i.h/2*o],t);c=e[0]/n,h=e[1]/o}c=Bs(c),h=Bs(h),(Math.abs(c-i.x)>1e-9||Math.abs(h-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:c,y:h};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_furnApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=Pr,n=this._decorH;i.decor=this._decorList.map(i=>i.id===t?{...i,x:Bs(e.x/s),y:Bs(e.y/n),w:gn(e.w/s),h:gn(e.h/n)}:i),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=Pr,s=this._decorH,n=e.w*i,o=e.h*s,r=function(t,e,i,s,n){const o=t+i/2,r=e+s/2,a=(Number(n)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=(t,e)=>{const i=t-o,s=e-r;return[o+i*l-s*c,r+i*c+s*l]};return[h(t,e),h(t+i,e),h(t+i,e+s),h(t,e+s)]}(e.x*i,e.y*s,n,o,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],l=a(r[0],r[1]),c=a(r[0],r[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[n,0])},{x:c[0],y:c[1],text:this._fmtLen([0,0],[0,o])}]}_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)}>`),o.push(B``)}return B`${o}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH;return(t?.openings||[]).map(t=>({...t,rx:t.x*Ar,ry:t.y*e,rlen:t.length*Ar}))}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return Ar}_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._snap(this._svgPoint(t),t);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},Or(t),!0}if("text"===e){const e=this._snap(this._svgPoint(t),t);return this._decorTextDialog={x:Bs(e[0]/Ar),y:Bs(e[1]/this._decorH),text:"",color:this._decorStyle.color},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t),!0);if(this._decorSel=null,this._bdMovable){const e=this._bdRect,i=this._svgPoint(t);if(i[0]>=e.x&&i[0]<=e.x+e.w&&i[1]>=e.y&&i[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(Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])t.id!==e.id),this._decorSel===e.id&&(this._decorSel=null),this._saveConfig(),void this.requestUpdate()}this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1},Or(t)}}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,n=("line"===s.kind?s.x1:s.x)*Ar,o=("line"===s.kind?s.y1:s.y)*this._decorH,r=this._snap([n+(i[0]-e.start[0]),o+(i[1]-e.start[1])],t);let a=(r[0]-n)/Ar,l=(r[1]-o)/this._decorH;const c=e.orig,h="line"===c.kind?Math.min(c.x1,c.x2):c.x,d="line"===c.kind?Math.min(c.y1,c.y2):c.y,p="line"===c.kind?Math.abs(c.x2-c.x1):c.w||0,u="line"===c.kind?Math.abs(c.y2-c.y1):c.h||0,_=Os;a=Math.max(-_-h,Math.min(_-h-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){"decor"===this._mode&&"select"===this._decorTool&&"text"===t.kind&&this._decorOpenText(t)}_decorOpenText(t){this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:t.text,color:t.color,entity:t.entity||"",attr:t.attr||"",unit:t.unit||""}}_decorTextLink(t){const e={},i=(t.entity||"").trim();if(!i)return e;e.entity=i;const s=(t.attr||"").trim();s&&(e.attr=s);const n=(t.unit||"").trim();return n&&(e.unit=n),e}_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._curSpaceCfg,s=this._decorTextLink(t);if(t.id)i.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;const{entity:n,attr:o,unit:r,...a}=i;return{...a,text:e,color:t.color,...s}});else{const n="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);i.decor=[...this._decorList,{id:n,kind:"text",x:t.x,y:t.y,text:e,color:t.color,...s}],this._decorSel=n}this._decorTextDialog=null,this._saveConfig(),this.requestUpdate()}get _dtSel(){if("decor"!==this._mode||"select"!==this._decorTool||!this._decorSel)return null;const t=this._decorList.find(t=>t.id===this._decorSel);return!t||"text"!==t.kind&&"furniture"!==t.kind?null:t}_dtPivot(t){return"furniture"===t.kind?[(t.x+t.w/2)*Ar,(t.y+t.h/2)*this._decorH]:[t.x*Ar,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{size:s,...n}=i,o={...n};return void 0!==e.scale?o.scale=Number(e.scale.toFixed(4)):void 0===i.scale&&void 0!==s&&(o.scale=pi(i)),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){const s=this._dtSel;if(!s)return;t.stopPropagation(),t.preventDefault();const[n,o]=this._dtPivot(s),r=this._svgPoint(t),a="furniture"===s.kind;this._dtDrag={id:s.id,kind:e,pid:t.pointerId,ax:n,ay:o,r0:Math.hypot(r[0]-n,r[1]-o),a0:180*Math.atan2(r[1]-o,r[0]-n)/Math.PI,scale0:a?1:pi(s),angle0:Number(s.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:a?{x:s.x*Ar,y:s.y*this._decorH,w:s.w*Ar,h:s.h*this._decorH,angle:Number(s.angle)||0}:void 0,moved:!1},Or(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if("scale"===e.kind&&e.orig){const s=t.shiftKey?0:this._gridPitch,n=s>0?s:.25*this._gridPitch,o=function(t,e,i,s,n,o=0,r=1e-6){const a=(Number(t.angle)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=-Math.sin(a),d=Math.cos(a),p=t.x+t.w/2,u=t.y+t.h/2,_=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,m=p+_*l+g*h,f=u+_*c+g*d,v=s-m,b=n-f;let y=(v*l+b*c)*(e>0?1:-1),w=(v*h+b*d)*(i>0?1:-1);return o>0&&(y=Math.round(y/o)*o,w=Math.round(w/o)*o),y=Math.max(r,y),w=Math.max(r,w),{x:m+(e>0?y/2:-y/2)*l+(i>0?w/2:-w/2)*h-y/2,y:f+(e>0?y/2:-y/2)*c+(i>0?w/2:-w/2)*d-w/2,w:y,h:w}}(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],s,n);return(Math.abs(o.w-e.orig.w)>1e-6||Math.abs(o.h-e.orig.h)>1e-6)&&(e.moved=!0),void this._furnApplyBox(e.id,o)}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.min(20,Math.max(.15,e.scale0*(t/e.r0)));return Math.abs(s-e.scale0)>1e-6&&(e.moved=!0),void this._dtApply(e.id,{scale:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let n=e.angle0+(s-e.a0);t.shiftKey||(n=5*Math.round(n/5)),n=(n%360+360)%360,n>180&&(n-=360),Math.abs(n-e.angle0)>1e-6&&(e.moved=!0),this._dtApply(e.id,{angle:n})}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&this._saveConfig(),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("furniture"===t.kind)e={id:t.id,x:t.x*Ar,y:t.y*this._decorH,w:t.w*Ar,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())}_decorDeleteSel(){if(!this._decorSel)return;this._curSpaceCfg.decor=this._decorList.filter(t=>t.id!==this._decorSel),this._decorSel=null,this._saveConfig(),this.requestUpdate()}get _furnWalls(){return this._segments}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=dn(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){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const n=Ar,o=this._decorH,r=gn(pn(i.w,this._cellCm,this._gridPitch,n)),a=gn(pn(i.h,this._cellCm,this._gridPitch,n)),l=this._snap(t,e);let c=l[0],h=l[1],d=0;if(!e.shiftKey){const t=bn(c,h,a*o,this._furnWalls,this._furnWallReach,this._gridPitch);t&&(c=t.cx,h=t.cy,d=t.angle)}const p="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),u={id:p,kind:"furniture",symbol:i.symbol,x:Bs(c/n-r/2),y:Bs(h/o-a/2),w:r,h:a,color:this._decorStyle.color,width:this._decorStyle.width};d&&(u.angle=Number(d.toFixed(2))),s.decor=[...this._decorList,u],this._decorSel=p,this._decorTool="select",this._furnPalette=null,this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(t){const e=this._decorMove,i=e.orig,s=this._curSpaceCfg;if(!s)return;const n=Ar,o=this._decorH,r=this._svgPoint(t),a=(i.x+i.w/2)*n+(r[0]-e.start[0]),l=(i.y+i.h/2)*o+(r[1]-e.start[1]);let c,h,d=Number(i.angle)||0;const p=t.shiftKey?null:bn(a,l,i.h*o,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)c=p.cx/n-i.w/2,h=p.cy/o-i.h/2,d=p.angle;else{const e=this._snap([a-i.w/2*n,l-i.h/2*o],t);c=e[0]/n,h=e[1]/o}c=Bs(c),h=Bs(h),(Math.abs(c-i.x)>1e-9||Math.abs(h-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:c,y:h};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_furnApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=Ar,n=this._decorH;i.decor=this._decorList.map(i=>i.id===t?{...i,x:Bs(e.x/s),y:Bs(e.y/n),w:gn(e.w/s),h:gn(e.h/n)}:i),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=Ar,s=this._decorH,n=e.w*i,o=e.h*s,r=function(t,e,i,s,n){const o=t+i/2,r=e+s/2,a=(Number(n)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=(t,e)=>{const i=t-o,s=e-r;return[o+i*l-s*c,r+i*c+s*l]};return[h(t,e),h(t+i,e),h(t+i,e+s),h(t,e+s)]}(e.x*i,e.y*s,n,o,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],l=a(r[0],r[1]),c=a(r[0],r[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[n,0])},{x:c[0],y:c[1],text:this._fmtLen([0,0],[0,o])}]}_renderFurnPalette(){const t=this._furnPalette,e=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return W`
t.stopPropagation()}>
${this._t("furn.title")} @@ -2680,7 +2680,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?{...Ps(t.plan_aspect,Pr)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?Rs(t,Pr):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale);return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,k:Number.isFinite(s)&&s>0?s:1}}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&("select"===this._decorTool||"backdrop"===this._decorTool)}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(t,e,i){const s=this._curSpaceCfg;s&&(s.plan_x=Number(Bs(t).toFixed(6)),s.plan_y=Number(Bs(e).toFixed(6)),s.plan_scale=Number(Math.min(100,Math.max(As,i)).toFixed(6)),this._cfgEpoch++,this.requestUpdate())}_bdStart(t,e){const i=this._bdBase,s=this._bdRect;if(!i||!s)return!1;const n=this._svgPoint(t),o=e?e[0]:0,r=e?e[1]:0,a=o>0?s.x:s.x+s.w,l=r>0?s.y:s.y+s.h;return this._bdDrag={kind:e?"scale":"move",pid:t.pointerId,sx:n[0],sy:n[1],base:i,p0:this._bdParams,fx:a,fy:l,sgx:o,sgy:r,moved:!1},Nr(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const n=s.x+e.p0.dx*Pr,o=s.y+e.p0.dy*Pr,r=this._snap([n+(i[0]-e.sx),o+(i[1]-e.sy)],t);return(Math.abs(r[0]-n)>1e-9||Math.abs(r[1]-o)>1e-9)&&(e.moved=!0),void this._bdApply((r[0]-s.x)/Pr,(r[1]-s.y)/Pr,e.p0.k)}const n=s.w||1,o=s.h||1;let r=Math.max(Math.abs(i[0]-e.fx)/n,Math.abs(i[1]-e.fy)/o);if(!t.shiftKey){const t=n>=o,i=ke(t?e.fx+e.sgx*r*n:e.fy+e.sgy*r*o,this._gridPitch),s=Math.abs(i-(t?e.fx:e.fy))/(t?n:o);s>0&&(r=s)}r=Math.min(100,Math.max(As,r)),Math.abs(r-e.p0.k)>1e-9&&(e.moved=!0);const a=e.sgx>0?e.fx:e.fx-r*n,l=e.sgy>0?e.fy:e.fy-r*o;this._bdApply((a-s.x)/Pr,(l-s.y)/Pr,r)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.k}_bdReset(){const t=this._curSpaceCfg;t&&(delete t.plan_x,delete t.plan_y,delete t.plan_scale,this._bdDrag=null,this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate())}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&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 V;const i=.02*Math.max(t.w,t.h),s=i/4;return B` +
`}get _bdBase(){const t=this._curSpaceCfg;return t?.plan_url?{...Ps(t.plan_aspect,Ar)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?Rs(t,Ar):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale);return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,k:Number.isFinite(s)&&s>0?s:1}}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&("select"===this._decorTool||"backdrop"===this._decorTool)}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(t,e,i){const s=this._curSpaceCfg;s&&(s.plan_x=Number(Bs(t).toFixed(6)),s.plan_y=Number(Bs(e).toFixed(6)),s.plan_scale=Number(Math.min(100,Math.max(As,i)).toFixed(6)),this._cfgEpoch++,this.requestUpdate())}_bdStart(t,e){const i=this._bdBase,s=this._bdRect;if(!i||!s)return!1;const n=this._svgPoint(t),o=e?e[0]:0,r=e?e[1]:0,a=o>0?s.x:s.x+s.w,l=r>0?s.y:s.y+s.h;return this._bdDrag={kind:e?"scale":"move",pid:t.pointerId,sx:n[0],sy:n[1],base:i,p0:this._bdParams,fx:a,fy:l,sgx:o,sgy:r,moved:!1},Or(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const n=s.x+e.p0.dx*Ar,o=s.y+e.p0.dy*Ar,r=this._snap([n+(i[0]-e.sx),o+(i[1]-e.sy)],t);return(Math.abs(r[0]-n)>1e-9||Math.abs(r[1]-o)>1e-9)&&(e.moved=!0),void this._bdApply((r[0]-s.x)/Ar,(r[1]-s.y)/Ar,e.p0.k)}const n=s.w||1,o=s.h||1;let r=Math.max(Math.abs(i[0]-e.fx)/n,Math.abs(i[1]-e.fy)/o);if(!t.shiftKey){const t=n>=o,i=ke(t?e.fx+e.sgx*r*n:e.fy+e.sgy*r*o,this._gridPitch),s=Math.abs(i-(t?e.fx:e.fy))/(t?n:o);s>0&&(r=s)}r=Math.min(100,Math.max(As,r)),Math.abs(r-e.p0.k)>1e-9&&(e.moved=!0);const a=e.sgx>0?e.fx:e.fx-r*n,l=e.sgy>0?e.fy:e.fy-r*o;this._bdApply((a-s.x)/Ar,(l-s.y)/Ar,r)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.k}_bdReset(){const t=this._curSpaceCfg;t&&(delete t.plan_x,delete t.plan_y,delete t.plan_scale,this._bdDrag=null,this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate())}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&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 V;const i=.02*Math.max(t.w,t.h),s=i/4;return B` ${[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]].map(([t,n,o])=>{const r=t<0?e.x:e.x+e.w,a=n<0?e.y:e.y+e.h;return B`this._dtStart(i,"scale",[t,e])}>`)} - `}_renderDecorLayer(){const t=Pr,e=this._decorH,i="decor"===this._mode,s=this._decorList.map(s=>{const n="dshape"+(i&&this._decorSel===s.id?" dsel":""),o=t=>this._decorShapeDown(t,s),r=()=>this._decorShapeDbl(s);if("line"===s.kind)return B``}_renderDecorLayer(){const t=Ar,e=this._decorH,i="decor"===this._mode,s=this._decorList.map(s=>{const n="dshape"+(i&&this._decorSel===s.id?" dsel":""),o=t=>this._decorShapeDown(t,s),r=()=>this._decorShapeDbl(s);if("line"===s.kind)return B``;if("rect"===s.kind)return B``)} ${i?i.segs.map(t=>B``):V} - `}_openCuts(){const t=this._curSpaceCfg;return mo(this._spaceModel().rooms,t?.open_spans,Pr,.02*this._gridPitch)}_openPairs(){const t=this._openCuts();if(!t.length)return[];const e=this._spaceModel().rooms.filter(t=>t.id),i=.02*this._gridPitch,s=[];for(let n=0;n{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return l.some(t=>Bi(e,t)<4*i)});c.length&&s.push({a:e[n],b:e[o],segs:c})}return s}_commitOpenSpans(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch;this._cfgEpoch++;const s=this._spaceModel().rooms;let n=lo(e.open_spans);n.length&&t?.old.length?n=function(t,e,i,s){const n=lo(t);if(!n.length)return[];if(e.length!==i.length)return n;const o=[];for(const t of n){const n=ro(t,s),r=[n[0],n[1]],a=[n[2],n[3]];let l=r,c=a;for(let t=0;t{const e=co(t,[s[0],s[1],n[0],n[1]]);if(e.d>.001)return t;const i=h[0]-o[0],r=h[1]-o[1];return[o[0]+i*e.t,o[1]+r*e.t]};if(co([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],n[0],n[1]]).d<.01){l=d(r),c=d(a);break}}o.push(oo(l,c,s))}return o}(n,t.old,t.next,Pr):n.length||(n=fo(mo(s,null,Pr,i),Pr)),n=function(t,e,i,s){const n=lo(t);if(!n.length)return[];const o=uo(e,s);if(!o.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of n){const e=ro(t,i),n=e[0],l=e[1],c=e[2]-n,h=e[3]-l,d=Math.hypot(c,h);if(d4*s||o>4*s)continue;const r=(t[0]-n)*p+(t[1]-l)*u,c=(t[2]-n)*p+(t[3]-l)*u,h=Math.max(0,Math.min(r,c)),g=Math.min(d,Math.max(r,c));if(g-ht.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t){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 t of e){const e=[n+p*t.lo,l+u*t.lo],s=[n+p*t.hi,l+u*t.hi];Math.hypot(s[0]-e[0],s[1]-e[1])ro(t,Pr)))}_persistOpenCuts(t){const e=this._curSpaceCfg;if(!e)return;const i=fo(t,Pr);i.length?e.open_spans=i:delete e.open_spans,function(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const n=(e||[]).filter(t=>t?.id);new Map(n.map(t=>[t.id,t]));const o=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=o.get(t),s=o.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;tBi(i,t)<4*s)){r(n[t].id,n[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}(e.rooms||[],this._spaceModel().rooms,t,.02*this._gridPitch)}_closeOpenSpan(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch,s=function(t,e,i){const s=[(e[0]+e[2])/2,(e[1]+e[3])/2];return t.filter(t=>{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return Math.hypot(e[0]-s[0],e[1]-s[1])>4*i})}(this._openCuts(),t,i);let n=this._normalizeWalls(e.walls,s);if(!(Zn(this._spaceModel().rooms,n,s,t,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)>0)){const e=[];for(const t of Xn(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr))t.open||e.push([t.a[0],t.a[1],t.b[0],t.b[1]]);n=bo(n,t,e,this._wallKeyPitch,Pr,15),n=this._normalizeWalls(n,s)}n.length?e.walls=n:delete e.walls,this._persistOpenCuts(s),this._showToast(this._t("toast.openwall_closed_span")),this._saveConfig(),this.requestUpdate()}_openWallClick(t){const e=6*this._gridPitch,i=.02*this._gridPitch,s=this._openCuts();if(this._openWallAnchor){const{p:n,edge:o}=this._openWallAnchor,r=ho(po(t,o,yo(o,s,i),this._gridPitch,1.5*this._gridPitch),o),a=Math.hypot(r[0]-n[0],r[1]-n[1]);if(this._openWallAnchor=null,a<.5*this._gridPitch)return void this._showToast(this._t("toast.openwall_short"));if(Bi(t,o)>e)return void this._showToast(this._t("toast.openwall_pick"));const l=[n[0],n[1],r[0],r[1]],c=[...s,l],h=this._curSpaceCfg;if(!h)return;const d=this._normalizeWalls(h.walls,c);d.length?h.walls=d:delete h.walls;const p=(h.openings||[]).length;return h.openings=function(t,e,i,s){return t?.length?t.filter(t=>Bi([Number(t.x)*i,Number(t.y)*i],e)>s||!Nn([e[0],e[1]],[e[2],e[3]],Number(t.angle)||0)):t?t.slice():[]}(h.openings,l,Pr,e),(h.openings||[]).lengththis._pointInRoom(t,e));if(!a)return;if(!confirm(this._t("confirm.delete_room",{name:a.name})))return;const l=this._curSpaceCfg;l&&(l.rooms=l.rooms.filter(t=>t.id!==a.id),this._commitOpenSpans(),this._saveConfig(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate())}get _wallKeyPitch(){return Hs}get _spaceWalls(){const t=this._curSpaceCfg?.walls;return Array.isArray(t)?t:[]}_cfgOpenCuts(){return lo(this._curSpaceCfg?.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_intervalCm(t){return Zn(this._spaceModel().rooms,this._spaceWalls,this._openCuts(),t,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)}_normalizeWalls(t,e){const i=function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=[],l=new Set;for(const c of Xn(t,e,i,s,n,o,r))!c.open&&c.cm>0&&!l.has(c.key)&&(l.add(c.key),a.push({key:c.key,cm:c.cm}));return a}(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr);return En(i,this._curSpaceCfg?.rooms||[],Hs,1,e.map(t=>[t[0]/Pr,t[1]/Pr,t[2]/Pr,t[3]/Pr]))}_paperShapes(t){const e=this._spaceWalls;if(!e.length)return Me(t);const i=this._openPairs().flatMap(t=>t.segs);return so(t,e,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)}_thickWallCuts(){const t=this._spaceWalls;if(!t.length)return[];const e=this._openPairs().flatMap(t=>t.segs);return function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=new Set,l=[];for(const c of t||[]){if(!c?.id)continue;const h=Yn(t,c.id,e,i,s,n,o,r);if(!h)continue;const d=h.poly;for(let t=0;t0))continue;const u=Dn(e,i,s,r);if(a.has(u))continue;a.add(u);const _=xn(p,n,o),[g,m]=qn(d,t),f=-g,v=-m,b=_/2,y=[[e[0]+f*b,e[1]+v*b],[i[0]+f*b,i[1]+v*b],[i[0]+g*b,i[1]+m*b],[e[0]+g*b,e[1]+m*b]];l.push({key:u,kind:c,cm:p,quad:y,a:[e[0],e[1]],b:[i[0],i[1]],depthUnits:_})}}return l}(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_wallThickHit(t){const e=6*this._gridPitch,i=this._openCuts();let s=null;for(const n of Xn(this._spaceModel().rooms,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)){const i=Bi(t,[n.a[0],n.a[1],n.b[0],n.b[1]]);i<=e&&(!s||i0?xn(e,this._cellCm,this._gridPitch):3*this._gridPitch,s=Math.max(i/2,1.25*this._gridPitch);let n="";for(const e of t.segs)n+=(n?" ":"")+Fn([[e[0],e[1]],[e[2],e[3]]],s,!1);return{segs:t.segs,open:t.open,d:n}}_wallThickClick(t){const e=this._wallThickHit(t);if(!e)return void this._showToast(this._t("toast.wallthick_pick"));if(e.open)return void this._showToast(this._t("toast.wallthick_open"));const i=e.cm,s=this._viewOr(this._baseVb()),n=(e.a[0]+e.b[0])/2,o=(e.a[1]+e.b[1])/2;this._wallDialog={a:e.a,b:e.b,value:kn(i,this._imperial),roomId:e.roomId,sx:(n-s.x)/s.w*100,sy:(o-s.y)/s.h*100}}_wallThickApply(t){const e=this._wallDialog;if(!e)return;const i=this._curSpaceCfg;if(!i)return;const s=$n(e.value,this._imperial),n=this._openCuts();let o;o=t&&e.roomId?In(i.walls,this._spaceModel().rooms,e.roomId,s,this._wallKeyPitch,n,Pr):On(i.walls,e.a,e.b,s,this._wallKeyPitch,Pr),o=this._normalizeWalls(o,n),o.length?i.walls=o:delete i.walls,this._wallDialog=null,this._showToast(this._t(null==s?"toast.wallthick_cleared":"toast.wallthick_set")),this._saveConfig(),this.requestUpdate()}_wallHatchDefs(t){if(!this._spaceWalls.length&&!this._markup)return B``;const e=Math.max(.4,1/Math.max(this._zoom,.4)),i=t||"var(--hp-muted, #607d8b)";return B` + `}_openCuts(){const t=this._curSpaceCfg;return mo(this._spaceModel().rooms,t?.open_spans,Ar,.02*this._gridPitch)}_openPairs(){const t=this._openCuts();if(!t.length)return[];const e=this._spaceModel().rooms.filter(t=>t.id),i=.02*this._gridPitch,s=[];for(let n=0;n{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return l.some(t=>Bi(e,t)<4*i)});c.length&&s.push({a:e[n],b:e[o],segs:c})}return s}_commitOpenSpans(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch;this._cfgEpoch++;const s=this._spaceModel().rooms;let n=lo(e.open_spans);n.length&&t?.old.length?n=wo(n,t.old,t.next,Ar):n.length||(n=fo(mo(s,null,Ar,i),Ar)),n=function(t,e,i,s){const n=lo(t);if(!n.length)return[];const o=uo(e,s);if(!o.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of n){const e=ro(t,i),n=e[0],l=e[1],c=e[2]-n,h=e[3]-l,d=Math.hypot(c,h);if(d4*s||o>4*s)continue;const r=(t[0]-n)*p+(t[1]-l)*u,c=(t[2]-n)*p+(t[3]-l)*u,h=Math.max(0,Math.min(r,c)),g=Math.min(d,Math.max(r,c));if(g-ht.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t){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 t of e){const e=[n+p*t.lo,l+u*t.lo],s=[n+p*t.hi,l+u*t.hi];Math.hypot(s[0]-e[0],s[1]-e[1])ro(t,Ar)))}_persistOpenCuts(t){const e=this._curSpaceCfg;if(!e)return;const i=fo(t,Ar);i.length?e.open_spans=i:delete e.open_spans,function(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const n=(e||[]).filter(t=>t?.id);new Map(n.map(t=>[t.id,t]));const o=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=o.get(t),s=o.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;tBi(i,t)<4*s)){r(n[t].id,n[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}(e.rooms||[],this._spaceModel().rooms,t,.02*this._gridPitch)}_closeOpenSpan(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch,s=function(t,e,i){const s=[(e[0]+e[2])/2,(e[1]+e[3])/2];return t.filter(t=>{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return Math.hypot(e[0]-s[0],e[1]-s[1])>4*i})}(this._openCuts(),t,i);let n=this._normalizeWalls(e.walls,s);if(!(Zn(this._spaceModel().rooms,n,s,t,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)>0)){const e=[];for(const t of Xn(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar))t.open||e.push([t.a[0],t.a[1],t.b[0],t.b[1]]);n=bo(n,t,e,this._wallKeyPitch,Ar,15),n=this._normalizeWalls(n,s)}n.length?e.walls=n:delete e.walls,this._persistOpenCuts(s),this._showToast(this._t("toast.openwall_closed_span")),this._saveConfig(),this.requestUpdate()}_openWallClick(t){const e=6*this._gridPitch,i=.02*this._gridPitch,s=this._openCuts();if(this._openWallAnchor){const{p:n,edge:o}=this._openWallAnchor,r=ho(po(t,o,yo(o,s,i),this._gridPitch,1.5*this._gridPitch),o),a=Math.hypot(r[0]-n[0],r[1]-n[1]);if(this._openWallAnchor=null,a<.5*this._gridPitch)return void this._showToast(this._t("toast.openwall_short"));if(Bi(t,o)>e)return void this._showToast(this._t("toast.openwall_pick"));const l=[n[0],n[1],r[0],r[1]],c=[...s,l],h=this._curSpaceCfg;if(!h)return;const d=this._normalizeWalls(h.walls,c);d.length?h.walls=d:delete h.walls;const p=(h.openings||[]).length;return h.openings=function(t,e,i,s){return t?.length?t.filter(t=>Bi([Number(t.x)*i,Number(t.y)*i],e)>s||!Nn([e[0],e[1]],[e[2],e[3]],Number(t.angle)||0)):t?t.slice():[]}(h.openings,l,Ar,e),(h.openings||[]).lengththis._pointInRoom(t,e));if(!a)return;if(!confirm(this._t("confirm.delete_room",{name:a.name})))return;const l=this._curSpaceCfg;l&&(l.rooms=l.rooms.filter(t=>t.id!==a.id),this._commitOpenSpans(),this._saveConfig(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate())}get _wallKeyPitch(){return qs}get _spaceWalls(){const t=this._curSpaceCfg?.walls;return Array.isArray(t)?t:[]}_cfgOpenCuts(){return lo(this._curSpaceCfg?.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_intervalCm(t){return Zn(this._spaceModel().rooms,this._spaceWalls,this._openCuts(),t,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)}_normalizeWalls(t,e){const i=function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=[],l=new Set;for(const c of Xn(t,e,i,s,n,o,r))!c.open&&c.cm>0&&!l.has(c.key)&&(l.add(c.key),a.push({key:c.key,cm:c.cm}));return a}(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar);return En(i,this._curSpaceCfg?.rooms||[],qs,1,e.map(t=>[t[0]/Ar,t[1]/Ar,t[2]/Ar,t[3]/Ar]))}_paperShapes(t){const e=this._spaceWalls;if(!e.length)return Me(t);const i=this._openPairs().flatMap(t=>t.segs);return so(t,e,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)}_thickWallCuts(){const t=this._spaceWalls;if(!t.length)return[];const e=this._openPairs().flatMap(t=>t.segs);return function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=new Set,l=[];for(const c of t||[]){if(!c?.id)continue;const h=Yn(t,c.id,e,i,s,n,o,r);if(!h)continue;const d=h.poly;for(let t=0;t0))continue;const u=Dn(e,i,s,r);if(a.has(u))continue;a.add(u);const _=xn(p,n,o),[g,m]=Hn(d,t),f=-g,v=-m,b=_/2,y=[[e[0]+f*b,e[1]+v*b],[i[0]+f*b,i[1]+v*b],[i[0]+g*b,i[1]+m*b],[e[0]+g*b,e[1]+m*b]];l.push({key:u,kind:c,cm:p,quad:y,a:[e[0],e[1]],b:[i[0],i[1]],depthUnits:_})}}return l}(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_wallThickHit(t){const e=6*this._gridPitch,i=this._openCuts();let s=null;for(const n of Xn(this._spaceModel().rooms,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)){const i=Bi(t,[n.a[0],n.a[1],n.b[0],n.b[1]]);i<=e&&(!s||i0?xn(e,this._cellCm,this._gridPitch):3*this._gridPitch,s=Math.max(i/2,1.25*this._gridPitch);let n="";for(const e of t.segs)n+=(n?" ":"")+Fn([[e[0],e[1]],[e[2],e[3]]],s,!1);return{segs:t.segs,open:t.open,d:n}}_wallThickClick(t){const e=this._wallThickHit(t);if(!e)return void this._showToast(this._t("toast.wallthick_pick"));if(e.open)return void this._showToast(this._t("toast.wallthick_open"));const i=e.cm,s=this._viewOr(this._baseVb()),n=(e.a[0]+e.b[0])/2,o=(e.a[1]+e.b[1])/2;this._wallDialog={a:e.a,b:e.b,value:kn(i,this._imperial),roomId:e.roomId,sx:(n-s.x)/s.w*100,sy:(o-s.y)/s.h*100}}_wallThickApply(t){const e=this._wallDialog;if(!e)return;const i=this._curSpaceCfg;if(!i)return;const s=$n(e.value,this._imperial),n=this._openCuts();let o;o=t&&e.roomId?In(i.walls,this._spaceModel().rooms,e.roomId,s,this._wallKeyPitch,n,Ar):On(i.walls,e.a,e.b,s,this._wallKeyPitch,Ar),o=this._normalizeWalls(o,n),o.length?i.walls=o:delete i.walls,this._wallDialog=null,this._showToast(this._t(null==s?"toast.wallthick_cleared":"toast.wallthick_set")),this._saveConfig(),this.requestUpdate()}_wallHatchDefs(t){if(!this._spaceWalls.length&&!this._markup)return B``;const e=Math.max(.4,1/Math.max(this._zoom,.4)),i=t||"var(--hp-muted, #607d8b)";return B` - `}_renderWallBodies(t){if(t&&!t.showBorders&&!this._markup)return B``;const e=this._spaceWalls;if(!e.length)return B``;const i=this._openPairs().flatMap(t=>t.segs),s=(this._curSpaceCfg?.openings||[]).map(t=>({x:Number(t.x)*Pr,y:Number(t.y)*Pr,angle:Number(t.angle)||0,length:(Number(t.length)>0?Number(t.length):.9)*Pr})),n=eo(this._spaceModel().rooms,e,i,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr);if(!n)return B``;const o=this._stageEl,r=this._viewOr(this._baseVb()),a=o&&o.clientWidth&&r.w?o.clientWidth/r.w:1,l=t?.color||"var(--hp-muted)",c=yn(n.depthUnits,a),h=this._fillColors.wall_fill;return B` + `}_renderWallBodies(t){if(t&&!t.showBorders&&!this._markup)return B``;const e=this._spaceWalls;if(!e.length)return B``;const i=this._openPairs().flatMap(t=>t.segs),s=(this._curSpaceCfg?.openings||[]).map(t=>({x:Number(t.x)*Ar,y:Number(t.y)*Ar,angle:Number(t.angle)||0,length:(Number(t.length)>0?Number(t.length):.9)*Ar})),n=eo(this._spaceModel().rooms,e,i,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar);if(!n)return B``;const o=this._stageEl,r=this._viewOr(this._baseVb()),a=o&&o.clientWidth&&r.w?o.clientWidth/r.w:1,l=t?.color||"var(--hp-muted)",c=yn(n.depthUnits,a),h=this._fillColors.wall_fill;return B`
-
`:W``}_openingClick(t,e=!1){const i=1.5*this._gridPitch,s=this._openingsR.find(e=>Math.hypot(t[0]-e.rx,t[1]-e.ry)<=Math.max(e.rlen/2,i));if(s)return void this._editOpening(s);const n=De(t,this._spaceModel().rooms,i);if(!n)return void this._showToast(this._t("toast.opening_no_wall"));if(function(t,e,i,s,n){for(const o of s)if(!(Bi([t,e],o)>n)&&Nn([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(n.x,n.y,n.angle,this._openCuts(),i))return void this._showToast(this._t("toast.opening_on_virtual"));const o=this._opRuler(n,this._cmToUnits(90),e);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{Nr(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1}}}_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=De(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,n=s?.openings?.find(t=>t.id===e.id);if(!n)return;const o=this._opRuler(i,n.length*Pr,t.shiftKey);this._opMeasure=o.measure;const r=o.x/Pr,a=o.y/this._spaceH;n.x===r&&n.y===a&&n.angle===i.angle||(this._opDrag.dirty=!0),n.x=r,n.y=a,n.angle=i.angle,this.requestUpdate()}_opRuler(t,e,i){const s=this._spaceModel().rooms,n=this._gridPitch/2;let o=t.x,r=t.y,a=Te([o,r],t.angle,e,s,n);if(!a||!a.centered||i||o===a.wallCenter[0]&&r===a.wallCenter[1]){if(a&&!i){const[i,l]=a.wallA,[c,h]=a.wallB,d=c-i,p=h-l,u=Math.hypot(d,p);if(u>0){const c=this._gridPitch,h=Math.min(e/2,u/2);let _=Math.round(((o-i)*d+(r-l)*p)/u/c)*c;_=Math.max(h,Math.min(u-h,_)),o=i+_/u*d,r=l+_/u*p,a=Te([o,r],t.angle,e,s,n)||a}}}else[o,r]=a.wallCenter,a=Te([o,r],t.angle,e,s,n);if(!a)return{x:o,y:r,angle:t.angle,measure:null};const l="mi"===this.hass?.config?.unit_system?.length,c=(t,e)=>({x:e[0],y:e[1],text:$e(t/this._gridPitch*this._cellCm,l)});return{x:o,y:r,angle:t.angle,measure:{labels:[c(a.sideA,a.midA),c(a.sideB,a.midB)],guide:a.centered&&!i?{x:a.wallCenter[0],y:a.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;const i=this._opDrag.moved;this._opMeasure=null,i&&this._opDrag.dirty&&this._saveConfig(),i?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._spaceH,s={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Pr,y:t.y/i,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/Pr,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===s.id);n>=0?e.openings[n]=s:e.openings.push(s),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;t?.id&&e?.openings&&(e.openings=e.openings.filter(e=>e.id!==t.id),this._saveConfig(),this._openingDialog=null,this.requestUpdate())}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&t.push([e,s?.attributes?.friendly_name||e,n?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_mergeClick(t){const e=this._spaceModel().rooms,i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const s=i.id;if(!this._mergeSel||this._mergeSel===s)return void(this._mergeSel=this._mergeSel===s?null:s);const n=e.find(t=>t.id===this._mergeSel),o=n?Se(n):null,r=Se(i),a=o&&r?Ve(o,r):null;if(!a)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:s,poly:a,pick:"a"},this._mergeSel=null}_commitMerge(){const t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._spaceH,s="a"===t.pick?t.aId:t.bId,n="a"===t.pick?t.bId:t.aId,o=e.rooms.find(t=>t.id===s);o?(o.poly=t.poly.map(t=>[t[0]/Pr,t[1]/i]),delete o.x,delete o.y,delete o.w,delete o.h,e.rooms=e.rooms.filter(t=>t.id!==n),this._commitOpenSpans(),this._saveConfig(),this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.rooms_merged",{name:o.name||""}))):this._mergeDialog=null}_splitClick(t,e=!1){const i=this._spaceModel().rooms;if(!this._splitSel){const e=[...i].reverse().find(e=>this._pointInRoom(t,e));if(!e?.id)return;return void(this._splitSel={roomId:e.id,pts:[]})}const s=i.find(t=>t.id===this._splitSel.roomId),n=s?Se(s):null;if(!s||!n)return void(this._splitSel=null);const o=.02*this._gridPitch,r=6*this._gridPitch,a=Ee(t,n),l=a&&!e&&function(t,e,i){let s=null,n=1/0;for(let o=0;o=n)continue;n=_;const g=Math.sqrt(p),m=(i>0?Math.max(0,Math.min(g,Math.round(u*g/i)*i)):u*g)/g;s=[r+m*h,a+m*d]}return s}(a,n,this._gridPitch)||a,c=a&&l&&Math.hypot(a[0]-t[0],a[1]-t[1])<=r?l:null,h=!!c&&Ne(c,n,o),d=this._splitSel.pts;if(!d.length)return h?void(this._splitSel={...this._splitSel,pts:[c]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const i=this._snap(t,e);return Oe(i,n,o)?void(this._splitSel={...this._splitSel,pts:[...d,i]}):void this._showToast(this._t("toast.split_pick_inside"))}const p=function(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(Pe(s,n,i))return null;const o=Ge(t,s,i),r=Ge(t,n,i);if(o<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!Oe(e,t,i))return null;for(let i=0;i{const r=[e];let a=(s+1)%t.length;for(let e=0;e<=t.length&&(r.push(t[a]),a!==o);e++)a=(a+1)%t.length;return r.push(n),Ke(r,i)};let c,h;if(o===r){const r=Ke([...e],i);if(r.length<3||Be(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}c=Ke(a,i),h=r}else c=Ke([...l(s,o,n,r),...[...a].reverse()],i),h=Ke([...l(n,r,s,o),...a],i);return c.length<3||h.length<3||Be(c)<=i||Be(h)<=i||Math.abs(Be(c)+Be(h)-Be(t))>Math.max(i,1e-6*Be(t))?null:[c,h]}(n,[...d,c],o);if(!p)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,g=Be(u)>=Be(_)?u:_,m=g===u?_:u;this._pendingSplit={roomId:s.id,mainPoly:g,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("opening"===this._tool||"openwall"===this._tool||"wallthick"===this._tool)return this._opShift=!!t.shiftKey,void(this._cursorPt=this._svgPoint(t));const e="draw"===this._tool&&this._path.length&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;(e||i)&&(this._cursorPt=this._snap(this._svgPoint(t),t))}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),n=De(t,this._spaceModel().rooms,e);if(!n)return null;const o=this._opRuler(n,s,this._opShift);return{x:o.x,y:o.y,angle:o.angle,rlen:s,measure:o.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._spaceH,i=!!this._pendingSplit;let s;if(this._pendingSplit){const i=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!i)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);i.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Pr,t[1]/e]),delete i.x,delete i.y,delete i.w,delete i.h,s=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;s=this._path.slice(0,-1)}const n=this._areaSel?this.hass.areas[this._areaSel]?.name:"",o={id:"r"+Date.now().toString(36),name:this._nameSel||n||this._t("room.default_name"),area:this._areaSel||null,poly:s.map(t=>[t[0]/Pr,t[1]/e]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(o),i&&this._commitOpenSpans(),!i){const e=this._drawWallCm;if(null!=e){this._cfgEpoch++;const i=this._openCuts();let s=function(t,e,i,s,n,o=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=Gn(e,i,o,n,r);if(!a)return t?t.slice():[];const l=Kn(t,a,n,r);let c=t?t.slice():[];for(let t=0;t0||(c=On(c,e,i,s,n,r))}return c}(t.walls,this._spaceModel().rooms,o.id,e,this._wallKeyPitch,i,Pr);s=this._normalizeWalls(s,i),s.length?t.walls=s:delete t.walls}}this._saveConfig(),this._path=[],this._pendingSplit=null,this._splitSel=null;const r=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let a=0;if(r){const t=Pr,e={...this._layout};for(const i of this._devices){if(i.area!==r||i.space!==this._space)continue;if(a++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/Pr,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const l=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(r?this._t("toast.room_saved",{n:l,added:a}):this._t("toast.room_saved_no_area",{n:l}))}_cancelPath(){this._path=[],this._cursorPt=null,this._roomDialog=!1,this._pendingSplit=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._openWallAnchor=null}_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: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:[...t.marker?.controls||[]],controlsFilter:"",isLight:!0===t.marker?.is_light,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 li)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=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&e.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&e.add("entity:"+t.bindingRef));const i=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&i.add(t.name.trim()+"|"+(t.area||""));const s=[];for(const n of Object.values(t.devices)){if("service"===n.entry_type)continue;const t="device:"+n.id;if(e.has(t))continue;const o=(n.name_by_user||n.name||n.id).trim();t!==this._markerDialog?.binding&&i.has(o+"|"+(n.area_id||""))||s.push({value:t,label:o,sub:(n.model||this._t("marker.sub_device"))+("Group"===n.model?this._t("marker.sub_z2m_group"):"")})}const n=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[i,o]of Object.entries(t.entities)){const r="entity:"+i;if(e.has(r))continue;const a=n.has(o.platform),l="group"===o.platform;if(!a&&!l)continue;if(o.hidden)continue;const c=t.states[i];s.push({value:r,label:o.name||c?.attributes?.friendly_name||i,sub:i.split(".")[0]+" · "+("group"===o.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const i=new Set(s.map(t=>t.value));for(const[n,o]of Object.entries(t.entities)){const r="entity:"+n;if(e.has(r)||i.has(r)||o.hidden)continue;const a=t.states[n],l=o.name||a?.attributes?.friendly_name||n,c=o.device_id?t.devices[o.device_id]:null,h=c&&(c.name_by_user||c.name)||"";s.push({value:r,label:l,sub:n.split(".")[0]+" · "+this._t("marker.sub_entity")+(h?" · "+h:"")})}}const o=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),r=o?s.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(o)):s;return r.sort((t,e)=>t.label.localeCompare(e.label)),r.slice(0,200)}_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",n=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const t={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||t.name,url:o.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}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 n=s?.space||null,o=s?.area||null;const r=s?.roomId||null;"virtual"!==t.binding||n||(n=this._space),i=function(t,e,i){const[s,n]=t.split(":");return"device"===s?n:"entity"===s?"lg_"+n:e&&e.startsWith("v_")?e:i()}(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const a=t.devId,l=e.markers.find(t=>t.id===i||t.id===a)?.vacuum||null,c={id:i,vacuum:l,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"badge"!==t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"badge"!==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:t.controls.length?t.controls:null,is_light:!!t.isLight||null,use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=parseFloat(t.glowRadius);return!Number.isFinite(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)&&(c.space=n,c.area=o,c.room_id=r);const h=a?this._devices.find(t=>t.id===a):null,d=h?.marker?.room_id??null,p=!!t.room&&null!=h&&(h.space!==n||h.area!==o||d!==r);let u=!1;const _=t.uploadId||a;if(_&&_!==i&&c.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:_,to_id:i}),e=t?.mapping||{};c.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const n="/files/"+e+"/",o="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(n))return t;const e=t.url.split(n)[1]||"",[i,r]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(n+i)[0]+o+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(n).join(o)}})}(c.pdfs,_,i,e),u=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),e.markers.push(c);let g=null;const m=n||h?.space||this._space,f=a?this._layout[a]:null,v=f?{s:f.s||h?.space||this._space,x:f.x,y:f.y}:a&&h&&this._defPos[a]?this._normPos(h.space,this._defPos[a].x,this._defPos[a].y):null;if(v&&v.s===m)i===a&&this._layout[i]&&!p||(g={s:v.s,x:v.x,y:v.y},this._layout={...this._layout,[i]:g});else if(!this._layout[i]||p){const t=this._spaceModel(n||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):o?t.rooms.find(t=>t.area===o):void 0;a&&([e,s]=this._roomCenter(a)),g=this._normPos(n||this._space,e,s),this._layout={...this._layout,[i]:g}}await this._saveConfigNow(),g&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:i,pos:g})),a&&a!==i&&(delete this._layout[a],await this.hass.callWS({type:"houseplan/layout/delete",device_id:a}).then(t=>this._noteLayoutRev(t)).catch(()=>{})),u&&_&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:_}).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)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null,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||[],e&&"virtual"===e.bindingKind?s.markers=s.markers.filter(t=>t.id!==e.id):e&&e.marker?(s.markers=s.markers.filter(t=>t.id!==e.id),"device"===e.bindingKind&&e.bindingRef?s.markers.push({id:e.id,binding:"device:"+e.bindingRef,hidden:!0}):"entity"===e.bindingKind&&e.bindingRef&&s.markers.push({id:e.id,binding:"entity:"+e.bindingRef,hidden:!0})):e&&"device"===e.bindingKind&&e.bindingRef?s.markers.push({id:e.id,binding:"device:"+e.bindingRef,hidden:!0}):e&&"entity"===e.bindingKind&&e.bindingRef&&s.markers.push({id:e.id,binding:"entity:"+e.bindingRef,hidden:!0});try{await this._saveConfigNow(),e&&"virtual"===e.bindingKind&&this._layout[e.id]&&(delete this._layout[e.id],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e.id}).then(t=>this._noteLayoutRev(t)).catch(()=>{})),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Pr,y:i/Pr}}_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=fi(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:Ss({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:gi,roomOpacity:mi,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 n=new Uint8Array(await i.arrayBuffer());let o="";for(let t=0;t{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=a});URL.revokeObjectURL(a),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:r,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return W`
${this._t("space.loading")}
`;const e=t.saved||[];if(!e.length)return W`
${this._t("space.no_saved")}
`;return W`
+
`:W``}_openingClick(t,e=!1){const i=1.5*this._gridPitch,s=this._openingsR.find(e=>Math.hypot(t[0]-e.rx,t[1]-e.ry)<=Math.max(e.rlen/2,i));if(s)return void this._editOpening(s);const n=De(t,this._spaceModel().rooms,i);if(!n)return void this._showToast(this._t("toast.opening_no_wall"));if(function(t,e,i,s,n){for(const o of s)if(!(Bi([t,e],o)>n)&&Nn([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(n.x,n.y,n.angle,this._openCuts(),i))return void this._showToast(this._t("toast.opening_on_virtual"));const o=this._opRuler(n,this._cmToUnits(90),e);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{Or(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1}}}_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=De(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,n=s?.openings?.find(t=>t.id===e.id);if(!n)return;const o=this._opRuler(i,n.length*Ar,t.shiftKey);this._opMeasure=o.measure;const r=o.x/Ar,a=o.y/this._spaceH;n.x===r&&n.y===a&&n.angle===i.angle||(this._opDrag.dirty=!0),n.x=r,n.y=a,n.angle=i.angle,this.requestUpdate()}_opRuler(t,e,i){const s=this._spaceModel().rooms,n=this._gridPitch/2;let o=t.x,r=t.y,a=Te([o,r],t.angle,e,s,n);if(!a||!a.centered||i||o===a.wallCenter[0]&&r===a.wallCenter[1]){if(a&&!i){const[i,l]=a.wallA,[c,h]=a.wallB,d=c-i,p=h-l,u=Math.hypot(d,p);if(u>0){const c=this._gridPitch,h=Math.min(e/2,u/2);let _=Math.round(((o-i)*d+(r-l)*p)/u/c)*c;_=Math.max(h,Math.min(u-h,_)),o=i+_/u*d,r=l+_/u*p,a=Te([o,r],t.angle,e,s,n)||a}}}else[o,r]=a.wallCenter,a=Te([o,r],t.angle,e,s,n);if(!a)return{x:o,y:r,angle:t.angle,measure:null};const l="mi"===this.hass?.config?.unit_system?.length,c=(t,e)=>({x:e[0],y:e[1],text:$e(t/this._gridPitch*this._cellCm,l)});return{x:o,y:r,angle:t.angle,measure:{labels:[c(a.sideA,a.midA),c(a.sideB,a.midB)],guide:a.centered&&!i?{x:a.wallCenter[0],y:a.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;const i=this._opDrag.moved;this._opMeasure=null,i&&this._opDrag.dirty&&this._saveConfig(),i?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._spaceH,s={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Ar,y:t.y/i,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/Ar,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===s.id);n>=0?e.openings[n]=s:e.openings.push(s),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;t?.id&&e?.openings&&(e.openings=e.openings.filter(e=>e.id!==t.id),this._saveConfig(),this._openingDialog=null,this.requestUpdate())}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&t.push([e,s?.attributes?.friendly_name||e,n?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_mergeClick(t){const e=this._spaceModel().rooms,i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const s=i.id;if(!this._mergeSel||this._mergeSel===s)return void(this._mergeSel=this._mergeSel===s?null:s);const n=e.find(t=>t.id===this._mergeSel),o=n?Se(n):null,r=Se(i),a=o&&r?Ve(o,r):null;if(!a)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:s,poly:a,pick:"a"},this._mergeSel=null}_commitMerge(){const t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._spaceH,s="a"===t.pick?t.aId:t.bId,n="a"===t.pick?t.bId:t.aId,o=e.rooms.find(t=>t.id===s);o?(o.poly=t.poly.map(t=>[t[0]/Ar,t[1]/i]),delete o.x,delete o.y,delete o.w,delete o.h,e.rooms=e.rooms.filter(t=>t.id!==n),this._commitOpenSpans(),this._saveConfig(),this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.rooms_merged",{name:o.name||""}))):this._mergeDialog=null}_splitClick(t,e=!1){const i=this._spaceModel().rooms;if(!this._splitSel){const e=[...i].reverse().find(e=>this._pointInRoom(t,e));if(!e?.id)return;return void(this._splitSel={roomId:e.id,pts:[]})}const s=i.find(t=>t.id===this._splitSel.roomId),n=s?Se(s):null;if(!s||!n)return void(this._splitSel=null);const o=.02*this._gridPitch,r=6*this._gridPitch,a=Ee(t,n),l=a&&!e&&function(t,e,i){let s=null,n=1/0;for(let o=0;o=n)continue;n=_;const g=Math.sqrt(p),m=(i>0?Math.max(0,Math.min(g,Math.round(u*g/i)*i)):u*g)/g;s=[r+m*h,a+m*d]}return s}(a,n,this._gridPitch)||a,c=a&&l&&Math.hypot(a[0]-t[0],a[1]-t[1])<=r?l:null,h=!!c&&Ne(c,n,o),d=this._splitSel.pts;if(!d.length)return h?void(this._splitSel={...this._splitSel,pts:[c]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const i=this._snap(t,e);return Oe(i,n,o)?void(this._splitSel={...this._splitSel,pts:[...d,i]}):void this._showToast(this._t("toast.split_pick_inside"))}const p=function(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(Pe(s,n,i))return null;const o=Ge(t,s,i),r=Ge(t,n,i);if(o<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!Oe(e,t,i))return null;for(let i=0;i{const r=[e];let a=(s+1)%t.length;for(let e=0;e<=t.length&&(r.push(t[a]),a!==o);e++)a=(a+1)%t.length;return r.push(n),Ke(r,i)};let c,h;if(o===r){const r=Ke([...e],i);if(r.length<3||Be(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}c=Ke(a,i),h=r}else c=Ke([...l(s,o,n,r),...[...a].reverse()],i),h=Ke([...l(n,r,s,o),...a],i);return c.length<3||h.length<3||Be(c)<=i||Be(h)<=i||Math.abs(Be(c)+Be(h)-Be(t))>Math.max(i,1e-6*Be(t))?null:[c,h]}(n,[...d,c],o);if(!p)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,g=Be(u)>=Be(_)?u:_,m=g===u?_:u;this._pendingSplit={roomId:s.id,mainPoly:g,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("opening"===this._tool||"openwall"===this._tool||"wallthick"===this._tool)return this._opShift=!!t.shiftKey,void(this._cursorPt=this._svgPoint(t));const e="draw"===this._tool&&this._path.length&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;(e||i)&&(this._cursorPt=this._snap(this._svgPoint(t),t))}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),n=De(t,this._spaceModel().rooms,e);if(!n)return null;const o=this._opRuler(n,s,this._opShift);return{x:o.x,y:o.y,angle:o.angle,rlen:s,measure:o.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._spaceH,i=!!this._pendingSplit;let s;if(this._pendingSplit){const i=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!i)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);i.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Ar,t[1]/e]),delete i.x,delete i.y,delete i.w,delete i.h,s=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;s=this._path.slice(0,-1)}const n=this._areaSel?this.hass.areas[this._areaSel]?.name:"",o={id:"r"+Date.now().toString(36),name:this._nameSel||n||this._t("room.default_name"),area:this._areaSel||null,poly:s.map(t=>[t[0]/Ar,t[1]/e]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(o),i&&this._commitOpenSpans(),!i){const e=this._drawWallCm;if(null!=e){this._cfgEpoch++;const i=this._openCuts();let s=function(t,e,i,s,n,o=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=Gn(e,i,o,n,r);if(!a)return t?t.slice():[];const l=Kn(t,a,n,r);let c=t?t.slice():[];for(let t=0;t0||(c=On(c,e,i,s,n,r))}return c}(t.walls,this._spaceModel().rooms,o.id,e,this._wallKeyPitch,i,Ar);s=this._normalizeWalls(s,i),s.length?t.walls=s:delete t.walls}}this._saveConfig(),this._path=[],this._pendingSplit=null,this._splitSel=null;const r=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let a=0;if(r){const t=Ar,e={...this._layout};for(const i of this._devices){if(i.area!==r||i.space!==this._space)continue;if(a++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/Ar,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const l=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(r?this._t("toast.room_saved",{n:l,added:a}):this._t("toast.room_saved_no_area",{n:l}))}_cancelPath(){this._path=[],this._cursorPt=null,this._roomDialog=!1,this._pendingSplit=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._openWallAnchor=null}_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: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:[...t.marker?.controls||[]],controlsFilter:"",isLight:!0===t.marker?.is_light,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 li)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=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&e.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&e.add("entity:"+t.bindingRef));const i=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&i.add(t.name.trim()+"|"+(t.area||""));const s=[];for(const n of Object.values(t.devices)){if("service"===n.entry_type)continue;const t="device:"+n.id;if(e.has(t))continue;const o=(n.name_by_user||n.name||n.id).trim();t!==this._markerDialog?.binding&&i.has(o+"|"+(n.area_id||""))||s.push({value:t,label:o,sub:(n.model||this._t("marker.sub_device"))+("Group"===n.model?this._t("marker.sub_z2m_group"):"")})}const n=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[i,o]of Object.entries(t.entities)){const r="entity:"+i;if(e.has(r))continue;const a=n.has(o.platform),l="group"===o.platform;if(!a&&!l)continue;if(o.hidden)continue;const c=t.states[i];s.push({value:r,label:o.name||c?.attributes?.friendly_name||i,sub:i.split(".")[0]+" · "+("group"===o.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const i=new Set(s.map(t=>t.value));for(const[n,o]of Object.entries(t.entities)){const r="entity:"+n;if(e.has(r)||i.has(r)||o.hidden)continue;const a=t.states[n],l=o.name||a?.attributes?.friendly_name||n,c=o.device_id?t.devices[o.device_id]:null,h=c&&(c.name_by_user||c.name)||"";s.push({value:r,label:l,sub:n.split(".")[0]+" · "+this._t("marker.sub_entity")+(h?" · "+h:"")})}}const o=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),r=o?s.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(o)):s;return r.sort((t,e)=>t.label.localeCompare(e.label)),r.slice(0,200)}_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",n=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const t={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||t.name,url:o.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}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 n=s?.space||null,o=s?.area||null;const r=s?.roomId||null;"virtual"!==t.binding||n||(n=this._space),i=function(t,e,i){const[s,n]=t.split(":");return"device"===s?n:"entity"===s?"lg_"+n:e&&e.startsWith("v_")?e:i()}(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const a=t.devId,l=e.markers.find(t=>t.id===i||t.id===a)?.vacuum||null,c={id:i,vacuum:l,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"badge"!==t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"badge"!==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:t.controls.length?t.controls:null,is_light:!!t.isLight||null,use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=parseFloat(t.glowRadius);return!Number.isFinite(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)&&(c.space=n,c.area=o,c.room_id=r);const h=a?this._devices.find(t=>t.id===a):null,d=h?.marker?.room_id??null,p=!!t.room&&null!=h&&(h.space!==n||h.area!==o||d!==r);let u=!1;const _=t.uploadId||a;if(_&&_!==i&&c.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:_,to_id:i}),e=t?.mapping||{};c.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const n="/files/"+e+"/",o="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(n))return t;const e=t.url.split(n)[1]||"",[i,r]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(n+i)[0]+o+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(n).join(o)}})}(c.pdfs,_,i,e),u=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),e.markers.push(c);let g=null;const m=n||h?.space||this._space,f=a?this._layout[a]:null,v=f?{s:f.s||h?.space||this._space,x:f.x,y:f.y}:a&&h&&this._defPos[a]?this._normPos(h.space,this._defPos[a].x,this._defPos[a].y):null;if(v&&v.s===m)i===a&&this._layout[i]&&!p||(g={s:v.s,x:v.x,y:v.y},this._layout={...this._layout,[i]:g});else if(!this._layout[i]||p){const t=this._spaceModel(n||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):o?t.rooms.find(t=>t.area===o):void 0;a&&([e,s]=this._roomCenter(a)),g=this._normPos(n||this._space,e,s),this._layout={...this._layout,[i]:g}}await this._saveConfigNow(),g&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:i,pos:g})),a&&a!==i&&(delete this._layout[a],await this.hass.callWS({type:"houseplan/layout/delete",device_id:a}).then(t=>this._noteLayoutRev(t)).catch(()=>{})),u&&_&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:_}).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)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null,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||[],e&&"virtual"===e.bindingKind?s.markers=s.markers.filter(t=>t.id!==e.id):e&&e.marker?(s.markers=s.markers.filter(t=>t.id!==e.id),"device"===e.bindingKind&&e.bindingRef?s.markers.push({id:e.id,binding:"device:"+e.bindingRef,hidden:!0}):"entity"===e.bindingKind&&e.bindingRef&&s.markers.push({id:e.id,binding:"entity:"+e.bindingRef,hidden:!0})):e&&"device"===e.bindingKind&&e.bindingRef?s.markers.push({id:e.id,binding:"device:"+e.bindingRef,hidden:!0}):e&&"entity"===e.bindingKind&&e.bindingRef&&s.markers.push({id:e.id,binding:"entity:"+e.bindingRef,hidden:!0});try{await this._saveConfigNow(),e&&"virtual"===e.bindingKind&&this._layout[e.id]&&(delete this._layout[e.id],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e.id}).then(t=>this._noteLayoutRev(t)).catch(()=>{})),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Ar,y:i/Ar}}_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=fi(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:Ss({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:gi,roomOpacity:mi,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 n=new Uint8Array(await i.arrayBuffer());let o="";for(let t=0;t{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=a});URL.revokeObjectURL(a),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:r,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return W`
${this._t("space.loading")}
`;const e=t.saved||[];if(!e.length)return W`
${this._t("space.no_saved")}
`;return W`
${e.map(e=>W`
@@ -2858,7 +2858,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
- `}_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 Ss(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Ms(this._sunGlobal(),this._sunSpace())}_effSunRays(){return Cs(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Ts(this.hass):null}_renderSunRays(t){const e=B``;if(this._editing||!this._effSunRays())return this._sunFadeReset(),e;const i=this._effNorth(),s=null!==i?Ts(this.hass):null;if(!s||s.elevation<=0)return this._sunFadeReset(),e;const n=Ds(this._sunGlobal()),o=function(t){if(!t)return 1;const e=$s[String(t).toLowerCase()];return void 0===e?1:e}(n?this.hass?.states?.[n]?.state:null),r=function(t=1){return.3*gs(t)}(o);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:Se(t)})).filter(t=>!!t.id&&!!t.poly),n=this._openingsR.filter(t=>"window"===t.type).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),o=this._spaceWalls,r=this._openPairs().flatMap(t=>t.segs),a={},c={};if(o.length){for(const i of e){const e=Qn(t.rooms,i.id,o,r,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr);e&&(a[i.id]=e)}for(const e of n){const i=no(t.rooms,{x:e.x,y:e.y,angle:e.angle,length:e.length},o,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr);i.cm>0&&(c[e.id]=xn(i.cm,this._cellCm,this._gridPitch))}}const h=ws(e,n,s.azimuth,s.elevation,i,o.length?a:void 0,o.length?c:void 0);this._sunRaysCache={key:l,rays:h,rims:h.map(t=>function(t,e=1e-4){const[i,s]=t.dir,n=-s,o=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((h[0]-a[0])*n+(h[1]-a[1])*o)>e)continue;const d=(c[0]-a[0])*i+(c[1]-a[1])*s,p=(h[0]-a[0])*i+(h[1]-a[1])*s;Math.abs(p-d)<=e||l.push(dt[0]-e[0]);const c=[];for(const t of l){const i=c[c.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):c.push([t[0],t[1]])}for(const[t,e]of c)r.push([[a[0]+i*t,a[1]+s*t],[a[0]+i*e,a[1]+s*e]])}return r}(t))}}const c=this._sunRaysCache.rays,h=this._sunRaysCache.rims;if(!c.length)return e;const d=(p=ms(s.elevation).warmth,ki("#ffe9c2","#ff9a45",gs(p)));var p;const u=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],_=function(t=1){return.42*gs(t)}(o),g=[[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 Ss(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Ms(this._sunGlobal(),this._sunSpace())}_effSunRays(){return Cs(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Ts(this.hass):null}_renderSunRays(t){const e=B``;if(this._editing||!this._effSunRays())return this._sunFadeReset(),e;const i=this._effNorth(),s=null!==i?Ts(this.hass):null;if(!s||s.elevation<=0)return this._sunFadeReset(),e;const n=Ds(this._sunGlobal()),o=function(t){if(!t)return 1;const e=$s[String(t).toLowerCase()];return void 0===e?1:e}(n?this.hass?.states?.[n]?.state:null),r=function(t=1){return.3*gs(t)}(o);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:Se(t)})).filter(t=>!!t.id&&!!t.poly),n=this._openingsR.filter(t=>"window"===t.type).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),o=this._spaceWalls,r=this._openPairs().flatMap(t=>t.segs),a={},c={};if(o.length){for(const i of e){const e=Qn(t.rooms,i.id,o,r,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar);e&&(a[i.id]=e)}for(const e of n){const i=no(t.rooms,{x:e.x,y:e.y,angle:e.angle,length:e.length},o,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar);i.cm>0&&(c[e.id]=xn(i.cm,this._cellCm,this._gridPitch))}}const h=ws(e,n,s.azimuth,s.elevation,i,o.length?a:void 0,o.length?c:void 0);this._sunRaysCache={key:l,rays:h,rims:h.map(t=>function(t,e=1e-4){const[i,s]=t.dir,n=-s,o=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((h[0]-a[0])*n+(h[1]-a[1])*o)>e)continue;const d=(c[0]-a[0])*i+(c[1]-a[1])*s,p=(h[0]-a[0])*i+(h[1]-a[1])*s;Math.abs(p-d)<=e||l.push(dt[0]-e[0]);const c=[];for(const t of l){const i=c[c.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):c.push([t[0],t[1]])}for(const[t,e]of c)r.push([[a[0]+i*t,a[1]+s*t],[a[0]+i*e,a[1]+s*e]])}return r}(t))}}const c=this._sunRaysCache.rays,h=this._sunRaysCache.rims;if(!c.length)return e;const d=(p=ms(s.elevation).warmth,ki("#ffe9c2","#ff9a45",gs(p)));var p;const u=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],_=function(t=1){return.42*gs(t)}(o),g=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]];return B` ${c.map((t,e)=>{const i=(t.a[0]+t.b[0])/2,s=(t.a[1]+t.b[1])/2,n=i+t.normal[0]*t.depth,o=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 ms(ks(t.elevation)).bg}const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":vi(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._serverCfg={...this._serverCfg,spaces:t.spaces},this._layout=t.layout;for(const e of Object.keys(t.layout))this._dirtyPos.add(e);this._modelCache=null,this._frame=null,await this._saveConfigNow(),await this._persistLayoutNow(),this._alignDialog=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}async _persistLayoutNow(){if(!this._serverStorage)return localStorage.setItem(Mr,JSON.stringify(this._layout)),void this._dirtyPos.clear();const t=[...this._dirtyPos];this._dirtyPos.clear(),await Promise.all(t.map(t=>{const e=this._layout[t];return e?(this._sentPos.set(t,e),this.hass.callWS({type:"houseplan/layout/update",device_id:t,pos:e}).then(t=>this._noteLayoutRev(t)).finally(()=>{this._sentPos.get(t)===e&&this._sentPos.delete(t)})):Promise.resolve()})),this._cacheSnapshot()}_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(bi),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const n=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(n)&&n>0&&300!==Math.round(n)?s.glow_radius_cm=Math.round(n):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 o=(t.weatherEntity||"").trim();o?s.weather_entity=o: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,n){const o=t=>{const e=Number(t.target.value);Number.isFinite(e)&&n(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 ms(ks(t.elevation)).bg}const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":vi(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._serverCfg={...this._serverCfg,spaces:t.spaces},this._layout=t.layout;for(const e of Object.keys(t.layout))this._dirtyPos.add(e);this._modelCache=null,this._frame=null,await this._saveConfigNow(),await this._persistLayoutNow(),this._alignDialog=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}async _persistLayoutNow(){if(!this._serverStorage)return localStorage.setItem(Cr,JSON.stringify(this._layout)),void this._dirtyPos.clear();const t=[...this._dirtyPos];this._dirtyPos.clear(),await Promise.all(t.map(t=>{const e=this._layout[t];return e?(this._sentPos.set(t,e),this.hass.callWS({type:"houseplan/layout/update",device_id:t,pos:e}).then(t=>this._noteLayoutRev(t)).finally(()=>{this._sentPos.get(t)===e&&this._sentPos.delete(t)})):Promise.resolve()})),this._cacheSnapshot()}_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(bi),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const n=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(n)&&n>0&&300!==Math.round(n)?s.glow_radius_cm=Math.round(n):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 o=(t.weatherEntity||"").trim();o?s.weather_entity=o: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,n){const o=t=>{const e=Number(t.target.value);Number.isFinite(e)&&n(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,n=t.rooms.map(t=>({r:t,poly:Se(t)})).filter(t=>!!t.poly),o=this._openingsR.filter(t=>"door"===t.type),r=[];for(const a of this._devices){if(a.space!==t.id)continue;if(a.hidden)continue;const l=Lo(this.hass,a);if(!l)continue;const c=Ti(this.hass.states[l],e.glow_light.c);if(!c)continue;const h=Number(a.marker?.glow_radius_cm),d=Number.isFinite(h)&&h>0?h/this._cellCm*this._gridPitch:i,p=this._pos(a),u=[...n].reverse().find(t=>this._pointInRoom([p.x,p.y],t.r));let _=null;if(u){const e=u.r.id?Ei(u.r.id,t.rooms):new Set([u.r.id]),i=n.filter(t=>t.r.id&&e.has(t.r.id)),r=i.length?i:[u],a=this._spaceWalls,l=this._openPairs().flatMap(t=>t.segs),c=r.map(e=>"M "+(a.length&&e.r.id&&Qn(t.rooms,e.r.id,a,l,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)||e.poly).map(t=>t[0]+" "+t[1]).join(" L ")+" Z"),h=n.filter(t=>!r.includes(t)).map(t=>t.poly);for(const t of o){const e=r.some(e=>{const i=Ee([t.rx,t.ry],e.poly);return i&&Math.hypot(i[0]-t.rx,i[1]-t.ry)<=.75*s});if(!e)continue;const i=t.angle*Math.PI/180,n=Math.cos(i)*t.rlen/2,o=Math.sin(i)*t.rlen/2;if(!Pi([t.rx,t.ry],t.angle,[p.x,p.y],h,.6*s))continue;const a=zi([p.x,p.y],[t.rx-n,t.ry-o],[t.rx+n,t.ry+o],d);a&&c.push("M "+a.map(t=>t[0]+" "+t[1]).join(" L ")+" Z")}_=c}r.push({pos:p,c:c.c,alpha:e.glow_light.a*c.bri,clip:_,r:d})}return r.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,n=t.rooms.map(t=>({r:t,poly:Se(t)})).filter(t=>!!t.poly),o=this._openingsR.filter(t=>"door"===t.type),r=[];for(const a of this._devices){if(a.space!==t.id)continue;if(a.hidden)continue;const l=qo(this.hass,a);if(!l)continue;const c=Ti(this.hass.states[l],e.glow_light.c);if(!c)continue;const h=Number(a.marker?.glow_radius_cm),d=Number.isFinite(h)&&h>0?h/this._cellCm*this._gridPitch:i,p=this._pos(a),u=[...n].reverse().find(t=>this._pointInRoom([p.x,p.y],t.r));let _=null;if(u){const e=u.r.id?Ei(u.r.id,t.rooms):new Set([u.r.id]),i=n.filter(t=>t.r.id&&e.has(t.r.id)),r=i.length?i:[u],a=this._spaceWalls,l=this._openPairs().flatMap(t=>t.segs),c=r.map(e=>"M "+(a.length&&e.r.id&&Qn(t.rooms,e.r.id,a,l,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)||e.poly).map(t=>t[0]+" "+t[1]).join(" L ")+" Z"),h=n.filter(t=>!r.includes(t)).map(t=>t.poly);for(const t of o){const e=r.some(e=>{const i=Ee([t.rx,t.ry],e.poly);return i&&Math.hypot(i[0]-t.rx,i[1]-t.ry)<=.75*s});if(!e)continue;const i=t.angle*Math.PI/180,n=Math.cos(i)*t.rlen/2,o=Math.sin(i)*t.rlen/2;if(!Pi([t.rx,t.ry],t.angle,[p.x,p.y],h,.6*s))continue;const a=zi([p.x,p.y],[t.rx-n,t.ry-o],[t.rx+n,t.ry+o],d);a&&c.push("M "+a.map(t=>t[0]+" "+t[1]).join(" L ")+" Z")}_=c}r.push({pos:p,c:c.c,alpha:e.glow_light.a*c.bri,clip:_,r:d})}return r.length?B` ${r.map((t,e)=>B` @@ -3009,7 +3009,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow -
${this._t("gs.about_version",{v:yr})}
+
${this._t("gs.about_version",{v:wr})}
${this._t("gs.about_github")} @@ -3069,7 +3069,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow - `}_saveKioskScale(t){this._kioskScale={...this._kioskScale,...t};try{localStorage.setItem(zr,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(Pr,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])}% @@ -3172,7 +3172,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${e.bg&&this._display(e.bg.href)?B``:V} ${""} ${o.hideDecor&&"decor"!==this._mode?V:this._renderDecorLayer()} - ${(()=>{const t=this._openPairs(),i=new Map,s=t=>(i.has(t)||i.set(t,Se(t)),i.get(t));return e.rooms.filter(t=>t.area||this._markup||o.showBorders).map(i=>{let n="room "+(e.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||i.id!==this._mergeSel&&i.id!==this._splitSel?.roomId||(n+=" picked");let a="";const l=Hi(o.fill,i);if(!this._markup&&(o.showBorders||"none"!==l)){n+=" styled";const t=[];t.push(`--room-stroke:${o.color}`,`--room-stroke-op:${o.showBorders?o.opacity:0}`);const e="glow"===l?this._fillColors.glow_base:"temp"===l?$i("temp",null,"none",this._roomTemp(i),o.tempMin,o.tempMax,this._fillColors):i.area?$i(l,"lqi"===l?this._roomLqi(i.area):null,"light"===l?Yo(this.hass,this._devices,i.area):"none",null,o.tempMin,o.tempMax,this._fillColors):null;e?(n+=" 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(";")}const c=t=>this._showTip(t,i.name,"",r?this._roomLqi(i.area):null,this._roomTemp(i)),h=!e.bg&&!o.showNames&&!this._markup,d=this._roomCenter(i),p=this._markup&&(i.id===this._mergeSel||i.id===this._splitSel?.roomId),u=i.id&&!p?t.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):[],_=p?[]:this._thickWallCuts(),g=u.concat(_);g.length&&(n+=" noedge");const m=s(i),f=this._spaceWalls,v=f.length&&i.id&&m&&Qn(e.rooms,i.id,f,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)||m,b=v?function(t,e,i=1e-6){const s=e.filter(e=>Ue(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ue(e,t,i)))}(v,(y=i,e.rooms.filter(t=>t!==y).map(s).filter(Boolean))):[];var y;const w=i.id||V,k=i.area||V,$=b.length&&v?B`{const t=this._openPairs(),i=new Map,s=t=>(i.has(t)||i.set(t,Se(t)),i.get(t));return e.rooms.filter(t=>t.area||this._markup||o.showBorders).map(i=>{let n="room "+(e.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||i.id!==this._mergeSel&&i.id!==this._splitSel?.roomId||(n+=" picked");let a="";const l=qi(o.fill,i);if(!this._markup&&(o.showBorders||"none"!==l)){n+=" styled";const t=[];t.push(`--room-stroke:${o.color}`,`--room-stroke-op:${o.showBorders?o.opacity:0}`);const e="glow"===l?this._fillColors.glow_base:"temp"===l?$i("temp",null,"none",this._roomTemp(i),o.tempMin,o.tempMax,this._fillColors):i.area?$i(l,"lqi"===l?this._roomLqi(i.area):null,"light"===l?Xo(this.hass,this._devices,i.area):"none",null,o.tempMin,o.tempMax,this._fillColors):null;e?(n+=" 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(";")}const c=t=>this._showTip(t,i.name,"",r?this._roomLqi(i.area):null,this._roomTemp(i)),h=!e.bg&&!o.showNames&&!this._markup,d=this._roomCenter(i),p=this._markup&&(i.id===this._mergeSel||i.id===this._splitSel?.roomId),u=i.id&&!p?t.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):[],_=p?[]:this._thickWallCuts(),g=u.concat(_);g.length&&(n+=" noedge");const m=s(i),f=this._spaceWalls,v=f.length&&i.id&&m&&Qn(e.rooms,i.id,f,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)||m,b=v?function(t,e,i=1e-6){const s=e.filter(e=>Ue(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ue(e,t,i)))}(v,(y=i,e.rooms.filter(t=>t!==y).map(s).filter(Boolean))):[];var y;const w=i.id||V,k=i.area||V,$=b.length&&v?B`t[0]+" "+t[1]).join(" L ")+" Z").join(" ")}" @mousemove=${c} @@ -3193,12 +3193,13 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow x="${d[0]}" y="${d[1]}">${i.name}`:V}`})})()} ${"glow"!==o.fill||this._markup?V:this._renderGlowLayer(e)} ${this._renderSunRays(e)} - ${this._renderOpenWalls(o)} ${this._editing?this._renderAlignGuides():V} ${u?.guide?this._renderOpeningCenterTick(u.guide):V} ${this._markup?this._renderMarkupLayer(i):V} ${""} ${this._renderWallBodies(o)} + ${""} + ${this._renderOpenWalls(o)} ${o.hideOpenings&&!this._markup?V:this._renderOpenings(o)} ${this._renderWallThickUi()} ${this._markup&&"resize"===this._tool?this._renderResizeLayer(c):V} @@ -3284,7 +3285,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
`:V} ${this._toast?W`
${this._toast}
`:V} - `}_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(Mo(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)}_senseTick(){if(this.hass)for(const t of this._devices){if(t.hidden||!t.primary||!t.primary.startsWith("binary_sensor."))continue;const e=this.hass.states[t.primary];if("motion"!==e?.attributes?.device_class)continue;const i=this._senseRt.get(t.id);i?("on"===e.state&&"off"===i.last&&(i.flashTs=Date.now(),i.gen++,clearTimeout(i.timer),i.timer=window.setTimeout(()=>this.requestUpdate(),3360)),i.last=e.state):this._senseRt.set(t.id,{last:e.state,flashTs:0,timer:0,gen:0})}}_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=zo(this.hass.states[i||""]?.state),n=So(this.hass.states[e]?.attributes);let o=this._vacRt.get(t.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(t.id,o)),s&&!o.moving&&(o.trail=[],o.lastPos=null);const r="never"!==Eo(t.marker?.vacuum)&&!n?.path;!s&&o.moving&&(o.endedTs=Date.now(),r&&o.lastPos&&(o.trail=To(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=s;const a=n?.pos;if(s&&a){const t=a.x+":"+a.y;if(t!==o.lastKey){const e=Date.now();o.jump=o.lastTs>0&&e-o.lastTs>1e4,o.lastKey=t,o.lastTs=e,r&&o.lastPos&&(o.trail=To(o.trail,o.lastPos,40)),o.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 V;const i=e.marker?.vacuum||{},s=this._vacSource(e),n=s?So(this.hass?.states[s]?.attributes):null,o=!!(n&&n.rooms.length>=3),r=n?.pos?_i(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(Co(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)}_senseTick(){if(this.hass)for(const t of this._devices){if(t.hidden||!t.primary||!t.primary.startsWith("binary_sensor."))continue;const e=this.hass.states[t.primary];if("motion"!==e?.attributes?.device_class)continue;const i=this._senseRt.get(t.id);i?("on"===e.state&&"off"===i.last&&(i.flashTs=Date.now(),i.gen++,clearTimeout(i.timer),i.timer=window.setTimeout(()=>this.requestUpdate(),3360)),i.last=e.state):this._senseRt.set(t.id,{last:e.state,flashTs:0,timer:0,gen:0})}}_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=Po(this.hass.states[i||""]?.state),n=Mo(this.hass.states[e]?.attributes);let o=this._vacRt.get(t.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(t.id,o)),s&&!o.moving&&(o.trail=[],o.lastPos=null);const r="never"!==No(t.marker?.vacuum)&&!n?.path;!s&&o.moving&&(o.endedTs=Date.now(),r&&o.lastPos&&(o.trail=zo(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=s;const a=n?.pos;if(s&&a){const t=a.x+":"+a.y;if(t!==o.lastKey){const e=Date.now();o.jump=o.lastTs>0&&e-o.lastTs>1e4,o.lastKey=t,o.lastTs=e,r&&o.lastPos&&(o.trail=zo(o.trail,o.lastPos,40)),o.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 V;const i=e.marker?.vacuum||{},s=this._vacSource(e),n=s?Mo(this.hass?.states[s]?.attributes):null,o=!!(n&&n.rooms.length>=3),r=n?.pos?_i(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}
@@ -3301,19 +3302,19 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${a.length?W`
${_i(this._t("vac.cal_maps"),{maps:a.join(", ")})}
`:V} `:V} -
`}_vacMapId(t,e){const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return n=e.mapId,o=s,"default"!==n?n:null!=o?String(o):"default";var n,o}_vacSaveMatrix(t,e,i,s){const n=this._devices.find(e=>e.id===t),o=n?this._vacEnsureMarker(n):this._serverCfg?.markers?.find(e=>e.id===t);if(!o)return!1;const r={...o.vacuum||{}};return r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},o.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate(),!0}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?So(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),n=(s?.rooms||[]).map(t=>({r:t,poly:Se(t)})).filter(({r:t,poly:e})=>t.name&&e).map(({r:t,poly:e})=>{const i=Le(e);return{name:t.name,cx:i[0],cy:i[1]}}),o=Do(i.rooms,n);o?this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),o.matrix)&&(o.residual>50&&this._showToast(_i(this._t("vac.autocal_res_warn"),{rooms:String(o.matched.length)})),this._showToast(_i(this._t("vac.autocal_done"),{rooms:String(o.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartFit(t){const e=this._vacSource(t),i=e?So(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),n=t.marker?.vacuum?.calibration?.[s],o=this._spaceModel(t.space),r=o?.vb||[0,0,Pr,Pr],a=n&&6===n.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 n=180*Math.atan2(-t[1],t[4])/Math.PI;return n=(90*Math.round(n/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:n,mir:i}}(n)||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 n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1,c={ox:0,oy:0,s:.6*Math.min(e[2],e[3])/l,rot:0,mir:!0},h=Ao(c),[d,p]=ko(h,(n+o)/2,(r+a)/2);return c.ox=e[0]+e[2]/2-d,c.oy=e[1]+e[3]/2-p,c}(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,Ao(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=So(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),n={...e.p,...t};this._vacFit={...e,p:Ro(n,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,n=s?.clientWidth||1,o=s?.clientHeight||1;return[e/n*t.w,i/o*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[n,o]=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+n,oy:s.p0.oy+o}};else{const t=So(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),r=Ao(s.p0),[a,l]=ko(r,e[0],e[1]),[c,h]=ko(r,s.fx,s.fy),d=Math.hypot(a-c,l-h)||1,[p,u]=[2*a-c,2*l-h],_=Math.hypot(p+2*n-c,u+2*o-h)/2,g=Math.max(.05,_/d),m={...s.p0,s:s.p0.s*g};this._vacFit={...i,p:Ro(m,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 V;const i=So(this.hass?.states[e.source]?.attributes);if(!i)return V;const s=Ao(e.p),n=[],o=[],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])=>ko(s,t,e));e.forEach(([t,e])=>{o.push(t),r.push(e)});const[i,a]=ko(s,t.cx,t.cy);n.push(B` - ${t.name}`)}let a=V;if(i.pos){const[e,n]=ko(s,i.pos.x,i.pos.y);a=B``}const l=[];if(o.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(...o),n=Math.max(...o),a=Math.min(...r),c=Math.max(...r),h=.022*t.w,d=h/4;for(const[t,s,o,r]of[[i,a,n,c],[n,a,i,c],[n,c,i,a],[i,c,n,a]]){const i=e(o,r);l.push(B``}_vacMapId(t,e){const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return n=e.mapId,o=s,"default"!==n?n:null!=o?String(o):"default";var n,o}_vacSaveMatrix(t,e,i,s){const n=this._devices.find(e=>e.id===t),o=n?this._vacEnsureMarker(n):this._serverCfg?.markers?.find(e=>e.id===t);if(!o)return!1;const r={...o.vacuum||{}};return r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},o.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate(),!0}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?Mo(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),n=(s?.rooms||[]).map(t=>({r:t,poly:Se(t)})).filter(({r:t,poly:e})=>t.name&&e).map(({r:t,poly:e})=>{const i=Le(e);return{name:t.name,cx:i[0],cy:i[1]}}),o=To(i.rooms,n);o?this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),o.matrix)&&(o.residual>50&&this._showToast(_i(this._t("vac.autocal_res_warn"),{rooms:String(o.matched.length)})),this._showToast(_i(this._t("vac.autocal_done"),{rooms:String(o.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartFit(t){const e=this._vacSource(t),i=e?Mo(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),n=t.marker?.vacuum?.calibration?.[s],o=this._spaceModel(t.space),r=o?.vb||[0,0,Ar,Ar],a=n&&6===n.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 n=180*Math.atan2(-t[1],t[4])/Math.PI;return n=(90*Math.round(n/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:n,mir:i}}(n)||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 n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1,c={ox:0,oy:0,s:.6*Math.min(e[2],e[3])/l,rot:0,mir:!0},h=Ro(c),[d,p]=$o(h,(n+o)/2,(r+a)/2);return c.ox=e[0]+e[2]/2-d,c.oy=e[1]+e[3]/2-p,c}(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,Ro(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=Mo(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),n={...e.p,...t};this._vacFit={...e,p:Eo(n,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,n=s?.clientWidth||1,o=s?.clientHeight||1;return[e/n*t.w,i/o*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[n,o]=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+n,oy:s.p0.oy+o}};else{const t=Mo(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),r=Ro(s.p0),[a,l]=$o(r,e[0],e[1]),[c,h]=$o(r,s.fx,s.fy),d=Math.hypot(a-c,l-h)||1,[p,u]=[2*a-c,2*l-h],_=Math.hypot(p+2*n-c,u+2*o-h)/2,g=Math.max(.05,_/d),m={...s.p0,s:s.p0.s*g};this._vacFit={...i,p:Eo(m,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 V;const i=Mo(this.hass?.states[e.source]?.attributes);if(!i)return V;const s=Ro(e.p),n=[],o=[],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])=>$o(s,t,e));e.forEach(([t,e])=>{o.push(t),r.push(e)});const[i,a]=$o(s,t.cx,t.cy);n.push(B` + ${t.name}`)}let a=V;if(i.pos){const[e,n]=$o(s,i.pos.x,i.pos.y);a=B``}const l=[];if(o.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(...o),n=Math.max(...o),a=Math.min(...r),c=Math.max(...r),h=.022*t.w,d=h/4;for(const[t,s,o,r]of[[i,a,n,c],[n,a,i,c],[n,c,i,a],[i,c,n,a]]){const i=e(o,r);l.push(B` `)}}return W`this._vacFitPointer(e,t)} @pointermove=${e=>this._vacFitPointer(e,t)} @pointerup=${e=>this._vacFitPointer(e,t)} - @pointercancel=${e=>this._vacFitPointer(e,t)}>${n}${a}${l}`}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const t=this.renderRoot,e=this._stageEl,i=this._vacLastView,s=t?.querySelectorAll?.(".vacpuck")||[];if(!e||!i||!s.length)return void(this._vacRaf=0);const n=e.getBoundingClientRect();for(const e of s){const s=e.getAttribute("data-mid"),o=e.getBoundingClientRect(),r=i.x+(o.left+o.width/2-n.left)/n.width*i.w,a=i.y+(o.top+o.height/2-n.top)/n.height*i.h;for(const e of t.querySelectorAll(`line.tip[data-mid="${s}"]`))e.setAttribute("x2",r.toFixed(1)),e.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return V;const i=this._space+"|"+e.x+"|"+e.y+"|"+e.w+"|"+e.h,s=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const n=[],o=[];for(const i of t){if(i.hidden||!this._isVacDev(i))continue;const t=this._vacSource(i);if(!t)continue;const r=So(this.hass?.states[t]?.attributes);if(!r)continue;const a=i.marker?.vacuum?.calibration?.[this._vacMapId(i,r)];if(!a||6!==a.length)continue;const l=this._vacRt.get(i.id),c=l?.moving??!1,h=Eo(i.marker?.vacuum),d="always"===h||"cleaning"===h&&c,p=this._vacSrvTrails[i.id],u=this._vacMapId(i,r),_=p?.current?.map_id===u&&Array.isArray(p.current.points)?p.current:null,g=p?.previous?.map_id===u&&Array.isArray(p.previous.points)?p.previous:null;if("always"===h&&g&&g.points.length>1){const t=g.points.map(([t,e])=>{const[i,s]=ko(a,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");o.push(B``)}if(d&&(c||_)){const t=_?.points||r.path||l?.trail||[],e=c&&(_||r.path)&&t.length>1?t.slice(0,-1):t;if(e.length>1){const t=e.map(([t,e])=>{const[i,s]=ko(a,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");if(o.push(B``),c){const[t,s]=ko(a,e[e.length-1][0],e[e.length-1][1]),n=t.toFixed(1),r=s.toFixed(1);o.push(B``)}}}if(!c||!r.pos)continue;const[m,f]=ko(a,r.pos.x,r.pos.y),v=(m-e.x)/e.w*100,b=(f-e.y)/e.h*100,y=l&&l.lastTs>0&&Date.now()-l.lastTs>6e4,w=i.marker?.icon||i.icon||"mdi:robot-vacuum";n.push(W`
this._vacFitPointer(e,t)}>${n}${a}${l}`}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const t=this.renderRoot,e=this._stageEl,i=this._vacLastView,s=t?.querySelectorAll?.(".vacpuck")||[];if(!e||!i||!s.length)return void(this._vacRaf=0);const n=e.getBoundingClientRect();for(const e of s){const s=e.getAttribute("data-mid"),o=e.getBoundingClientRect(),r=i.x+(o.left+o.width/2-n.left)/n.width*i.w,a=i.y+(o.top+o.height/2-n.top)/n.height*i.h;for(const e of t.querySelectorAll(`line.tip[data-mid="${s}"]`))e.setAttribute("x2",r.toFixed(1)),e.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return V;const i=this._space+"|"+e.x+"|"+e.y+"|"+e.w+"|"+e.h,s=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const n=[],o=[];for(const i of t){if(i.hidden||!this._isVacDev(i))continue;const t=this._vacSource(i);if(!t)continue;const r=Mo(this.hass?.states[t]?.attributes);if(!r)continue;const a=i.marker?.vacuum?.calibration?.[this._vacMapId(i,r)];if(!a||6!==a.length)continue;const l=this._vacRt.get(i.id),c=l?.moving??!1,h=No(i.marker?.vacuum),d="always"===h||"cleaning"===h&&c,p=this._vacSrvTrails[i.id],u=this._vacMapId(i,r),_=p?.current?.map_id===u&&Array.isArray(p.current.points)?p.current:null,g=p?.previous?.map_id===u&&Array.isArray(p.previous.points)?p.previous:null;if("always"===h&&g&&g.points.length>1){const t=g.points.map(([t,e])=>{const[i,s]=$o(a,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");o.push(B``)}if(d&&(c||_)){const t=_?.points||r.path||l?.trail||[],e=c&&(_||r.path)&&t.length>1?t.slice(0,-1):t;if(e.length>1){const t=e.map(([t,e])=>{const[i,s]=$o(a,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");if(o.push(B``),c){const[t,s]=$o(a,e[e.length-1][0],e[e.length-1][1]),n=t.toFixed(1),r=s.toFixed(1);o.push(B``)}}}if(!c||!r.pos)continue;const[m,f]=$o(a,r.pos.x,r.pos.y),v=(m-e.x)/e.w*100,b=(f-e.y)/e.h*100,y=l&&l.lastTs>0&&Date.now()-l.lastTs>6e4,w=i.marker?.icon||i.icon||"mdi:robot-vacuum";n.push(W`
`)}return this._vacLastView=e,n.length&&!this._vacRaf&&this._vacRafLoop(),n.length||o.length?W` ${o.length?B`${o}`:V} - ${n}`:V}_renderDevice(t,e,i=!0,s=!1){const n=this._pos(t),o=(n.x-e.x)/e.w*100,r=(n.y-e.y)/e.h*100;let a=t.hidden?"":this._stateClass(t);s&&"on"===a&&Lo(this.hass,t)&&(a="");const l=t.hidden?null:this._liveTemp(t),c=t.hidden?null:this._liveHum(t),h=!i||t.virtual||t.hidden?null:Ho(this.hass,t.entities),d=t.marker,p=d?.display||"badge",u=("ripple"===p||"icon_ripple"===p)&&!t.hidden,_=this._actEntity(t),g=_?this.hass.states[_]:void 0,m="value"!==p||t.hidden||null!=l||null!=c||!g||isNaN(parseFloat(g.state))?null:ci(this.hass,_),f="value"!==p||t.hidden?null:null!=l?l+"°":null!=c?c+"%":m?m.formatted?hi(m,g.attributes?.unit_of_measurement):parseFloat(g.state)+(g.attributes?.unit_of_measurement?" "+g.attributes.unit_of_measurement:""):null,v=_?_.split(".")[0]:null,b=this._config?.live_states&&!t.hidden?Ci(t.icon,v,g?.attributes?.device_class,g?.state,!!d?.icon):t.icon,y=(d?.controls||[]).filter(Ai),w=this._config?.live_states&&!t.hidden?y.length?y.map(t=>Di(this.hass.states[t])).find(t=>t)||null:"light"===v?Di(g):null:null,k=this._config?.live_states&&!t.hidden&&function(t,e,i){return"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&ji.has(e))}(v,g?.attributes?.device_class,g?.state),$=u&&!t.hidden&&!!_&&ze(this.hass.states[_]?.state),x=Number(d?.size)>0?Number(d.size):1,S=Number(d?.angle)||0,M=Number(d?.ripple_size)>0?Number(d.ripple_size):3,C=[`left:${o}%`,`top:${r}%`];return 1!==x&&C.push(`--dev-scale:${x}`),u&&(C.push(`--ripple-scale:${M}`),d?.ripple_color?C.push(`--ripple-color:${d.ripple_color}`):w&&C.push(`--ripple-color:${w}`)),W`
Di(this.hass.states[t])).find(t=>t)||null:"light"===v?Di(g):null:null,k=this._config?.live_states&&!t.hidden&&function(t,e,i){return"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&ji.has(e))}(v,g?.attributes?.device_class,g?.state),$=u&&!t.hidden&&!!_&&ze(this.hass.states[_]?.state),x=Number(d?.size)>0?Number(d.size):1,S=Number(d?.angle)||0,M=Number(d?.ripple_size)>0?Number(d.ripple_size):3,C=[`left:${o}%`,`top:${r}%`];return 1!==x&&C.push(`--dev-scale:${x}`),u&&(C.push(`--ripple-scale:${M}`),d?.ripple_color?C.push(`--ripple-color:${d.ripple_color}`):w&&C.push(`--ripple-color:${w}`)),W`
${l}°`:V} ${null!=c&&null==f?W`${c}%`:V} ${null!=h?W`${h}`:V} -
`}_roomTemp(t){const e=t.settings?.temp_source;return e?Xo(this.hass,e,"temp"):t.area?this._climate().get(t.area)?.temp??null:null}_roomHum(t){const e=t.settings?.hum_source;return e?Xo(this.hass,e,"hum"):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 n=new Set;for(const t of i||[]){if(!0!==t?.use_climate_temp)continue;const e=(t.binding||"").indexOf(":");e>0&&n.add(t.binding.slice(e+1))}const o=new Map;for(const[e,i]of Object.entries(t.entities)){const s=i.device_id?t.devices?.[i.device_id]:null,r=i.area_id||s?.area_id||null;if(!r)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(ht.has(i.platform))continue;if(Zo.test(e))continue}let a=o.get(r);a||(a=new Map,o.set(r,a));const l=i.device_id||e;let c=a.get(l);if(!c){const n=t.states?.[e];c={name:(s?s.name_by_user||s.name:i.name||n?.attributes?.friendly_name||e)||e,model:s?.model,ents:[]},a.set(l,c)}c.ents.push(e)}for(const[i,r]of o){const o=[],a=[];for(const[i,s]of r){const r=Vo(t,s.name,s.model,s.ents,e),l="mdi:thermometer"===r||"mdi:air-filter"===r;if(l){const e=qo(t,s.ents);null!=e&&o.push(e)}if(n.size>0&&(n.has(i)||s.ents.some(t=>n.has(t)))){const e=Uo(t,s.ents);null!=e&&o.push(e)}if(l||"mdi:water-percent"===r){const e=Bo(t,s.ents);null!=e&&a.push(e)}}(o.length||a.length)&&s.set(i,{temp:o.length?Math.round(o.reduce((t,e)=>t+e,0)/o.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=qi(t.settings?.name_scale),this._roomLabelScale=qi(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=this._roomSrcFilter.trim().toLowerCase(),i=[];for(const s of Object.values(t.devices)){if("service"===s.entry_type)continue;const t=(s.name_by_user||s.name||s.id).trim();e&&!t.toLowerCase().includes(e)||i.push({value:"device:"+s.id,label:t,sub:s.model||this._t("marker.sub_device")})}for(const[s,n]of Object.entries(t.entities)){if(!s.startsWith("sensor.")||n.hidden)continue;const o=n.name||t.states[s]?.attributes?.friendly_name||s;e&&!(o+" "+s).toLowerCase().includes(e)||i.push({value:"entity:"+s,label:o,sub:s})}return i.sort((t,e)=>t.label.localeCompare(e.label)),i.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*Pr,y:i.y*Pr};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},Nr(t),this._tip=null}_labelMove(t,e,i){const s="rl_"+(e.id||"");if(!this._drag||this._drag.id!==s)return;const n=this._stageEl;if(!n)return;const o=this._spaceModel(i).vb,r=n.getBoundingClientRect(),a=this._viewOr(o),l=(t.clientX-this._drag.sx)/r.width*a.w,c=(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 h=Ws(this._drag.ox+l),d=Ws(this._drag.oy+c);this._savePos({id:s,space:i},h,d,t.shiftKey)}_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 n=s.getBoundingClientRect(),o=n.left+n.width/2,r=n.top+n.height/2,a=Math.max(8,Math.hypot(t.clientX-o,t.clientY-r));this._rlResize={id:"rl_"+(e.id||""),space:i,k0:this._labelScale(e),cx:o,cy:r,d0:a},Nr(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))),n=this._layout[e.id];if(n)this._layout={...this._layout,[e.id]:{...n,k:s}};else{const t=e.id.slice(3),i=this._spaceModel(e.space).rooms.find(e=>e.id===t);if(!i)return;const n=this._labelPos(i,e.space);this._layout={...this._layout,[e.id]:{s:e.space,x:n.x/Pr,y:n.y/Pr,k:s}}}this._dirtyPos.add(e.id)}_rlResizeUp(){this._rlResize&&(this._rlResize=null,this._persistLayout())}_renderRoomGear(t,e,i){if(!t.id)return V;let s=null;if(t.poly?(s=this._gearPtCache.get(t.poly)||null,s||(s=Le(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 V;const n=(s[0]-i.x)/i.w*100,o=(s[1]-i.y)/i.h*100;return W``}_renderRoomLabel(t,e,i,s){if(!t.name&&!this._markup)return V;const n=this._labelPos(t,e.id),o=(n.x-i.x)/i.w*100,r=(n.y-i.y)/i.h*100,a=Math.min(1,s.opacity+.25),l=this._labelScale(t),c=[];if(t.area||t.settings?.temp_source||t.settings?.hum_source){if(s.labelTemp){const e=this._roomTemp(t);null!=e&&c.push(W`${e}°`)}if(s.labelHum){const e=this._roomHum(t);null!=e&&c.push(W`${e}%`)}if(s.labelLqi&&t.area){const e=this._roomLqi(t.area);null!=e&&c.push(W`${e}`)}if(s.labelLight&&t.area){const e=function(t,e,i){const s=new Set;let n=0;for(const o of e)if(o.area===i&&!o.hidden)for(const e of o.entities)e.startsWith("light.")&&!s.has(e)&&(s.add(e),"on"===t.states[e]?.state&&n++);return s.size?{on:n,total:s.size}:null}(this.hass,this._devices,t.area);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});c.push(W`${t}`)}}}return W`
this._labelDown(i,t,e.id)} @pointermove=${i=>this._labelMove(i,t,e.id)} @pointerup=${()=>this._labelUp(t)} @@ -3369,12 +3370,12 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @pointerup=${()=>this._rlResizeUp()} @pointercancel=${()=>this._rlResizeUp()}>`):V}
`}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"===this._tool&&this._path.length&&!this._contourClosed?this._path[this._path.length-1]:"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:null}_renderMeasureLabel(t){const e=this._measureAnchor,i=this._cursorPt,s=(i[0]-t.x)/t.w*100,n=(i[1]-t.y)/t.h*100,o=Ui(e,i),r=Math.round(10*o)/10,a=Wi(o);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,n]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-n)<1e-6)return null;const o=(e+s)/2,r=(i+n)/2;if("line"===t.kind){const e=Ui(t.a,t.b);return{x:o,y:r,on45:Wi(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}return{x:o,y:r,on45:!1,text:`${this._fmtLen([e,i],[s,i])} × ${this._fmtLen([s,i],[s,n])}`}}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=Pr,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=Se(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 i=Pr,s=this._decorH,n=this._decorMove?.id;for(const e of this._decorList)e.id!==n&&("line"===e.kind?t.push([e.x1*i,e.y1*s],[e.x2*i,e.y2*s]):"text"===e.kind?t.push([e.x*i,e.y*s]):t.push([e.x*i,e.y*s],[(e.x+e.w)*i,e.y*s],[e.x*i,(e.y+e.h)*s],[(e.x+e.w)*i,(e.y+e.h)*s]));this._decorDraft&&t.push(this._decorDraft.a);for(const i of e.rooms){const e=Se(i);if(e)for(const i of e)t.push(i)}return 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,n=null;for(const o of e)if(!(Math.abs(o[0]-t[0])<1e-6&&Math.abs(o[1]-t[1])<1e-6)){if(Math.abs(o[0]-t[0])<=i){const e=Math.abs(o[1]-t[1]);e>1e-6&&(!s||e1e-6&&(!n||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,n]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-n)<1e-6)return null;const o=(e+s)/2,r=(i+n)/2;if("line"===t.kind){const e=Ui(t.a,t.b);return{x:o,y:r,on45:Wi(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}return{x:o,y:r,on45:!1,text:`${this._fmtLen([e,i],[s,i])} × ${this._fmtLen([s,i],[s,n])}`}}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=Ar,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=Se(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 i=Ar,s=this._decorH,n=this._decorMove?.id;for(const e of this._decorList)e.id!==n&&("line"===e.kind?t.push([e.x1*i,e.y1*s],[e.x2*i,e.y2*s]):"text"===e.kind?t.push([e.x*i,e.y*s]):t.push([e.x*i,e.y*s],[(e.x+e.w)*i,e.y*s],[e.x*i,(e.y+e.h)*s],[(e.x+e.w)*i,(e.y+e.h)*s]));this._decorDraft&&t.push(this._decorDraft.a);for(const i of e.rooms){const e=Se(i);if(e)for(const i of e)t.push(i)}return 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,n=null;for(const o of e)if(!(Math.abs(o[0]-t[0])<1e-6&&Math.abs(o[1]-t[1])<1e-6)){if(Math.abs(o[0]-t[0])<=i){const e=Math.abs(o[1]-t[1]);e>1e-6&&(!s||e1e-6&&(!n||e ${i.map(e=>{const[i,o,r,a]="x"===e.axis?[e.at,e.from[1],e.at,t[1]+Math.sign(t[1]-e.from[1])*n]:[e.from[0],e.at,t[0]+Math.sign(t[0]-e.from[0])*n,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.hass.states[t.contact]?.state:null;return function(t,e,i=!1){return null==e||"unavailable"===e||"unknown"===e?"door"===t?1:0:ze(e)!==!!i?1:0}(t.type,e,!!t.invert)}_renderOpenings(t){const e=this._openingsR;if(!e.length)return B``;const i=t.color,s=this._spaceWalls,n=this._spaceModel().rooms;return B`${e.map(t=>{const e=t.rlen/2,o=this._openingAmt(t),r=o>0&&!!t.contact?"var(--hp-open)":i,a=s.length?no(n,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:t.flip_v},s,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr):{ox:0,oy:0,cm:0},l=a.cm>0?a.cm/this._cellCm*this._gridPitch/2:4,c=t.flip_h?-1:1,h=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*=h,p*=c}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.hass.states[t.contact]?.state:null;return function(t,e,i=!1){return null==e||"unavailable"===e||"unknown"===e?"door"===t?1:0:ze(e)!==!!i?1:0}(t.type,e,!!t.invert)}_renderOpenings(t){const e=this._openingsR;if(!e.length)return B``;const i=t.color,s=this._spaceWalls,n=this._spaceModel().rooms;return B`${e.map(t=>{const e=t.rlen/2,o=this._openingAmt(t),r=o>0&&!!t.contact?"var(--hp-open)":i,a=s.length?no(n,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:t.flip_v},s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar):{ox:0,oy:0,cm:0},l=a.cm>0?a.cm/this._cellCm*this._gridPitch/2:4,c=t.flip_h?-1:1,h=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*=h,p*=c}if("window"===t.type){const t=Math.PI/2*e,i=a.cm>0?B``:V;d=B` `}
- `}}Or.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},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{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},_decorTextDialog:{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}},Or.ZOOM_MAX=8,Or.ZOOM_MIN=1/3,Or._touchSeen=!1,Or._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,Or.styles=sr,customElements.get("houseplan-card")||customElements.define("houseplan-card",Or),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${yr} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); + `}}Ir.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},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{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},_decorTextDialog:{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}},Ir.ZOOM_MAX=8,Ir.ZOOM_MIN=1/3,Ir._touchSeen=!1,Ir._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,Ir.styles=nr,customElements.get("houseplan-card")||customElements.define("houseplan-card",Ir),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${wr} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); diff --git a/custom_components/houseplan/manifest.json b/custom_components/houseplan/manifest.json index f3e41f8d..4c8185cf 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.59.0-beta.8" + "version": "1.59.0-beta.9" } diff --git a/demo/smoke_resize_virtual_thick.mjs b/demo/smoke_resize_virtual_thick.mjs new file mode 100644 index 00000000..0bc9a315 --- /dev/null +++ b/demo/smoke_resize_virtual_thick.mjs @@ -0,0 +1,151 @@ +/** + * Regression: resize a shared wall whose middle is virtual and whose solid + * remainders carry thickness. The live overlay, commit and Undo must move one + * geometry transaction; virtual drawing must paint above the real wall body. + */ +import { launch, checkAll, finish } from './serve.mjs'; +const { page, browser } = await launch(); + +const res = await page.evaluate(async () => { + const out = {}; + const c = window.__card; + const sr = () => c.shadowRoot || c.renderRoot; + const sp = () => c._serverCfg.spaces.find((s) => s.id === c._space); + const upd = async () => { c._cfgEpoch++; c.requestUpdate(); await c.updateComplete; }; + + sp().settings = { ...(sp().settings || {}), show_borders: true }; + delete sp().walls; + delete sp().open_spans; + delete sp().openings; + for (const r of sp().rooms || []) delete r.open_to; + + c._setMode('plan'); + c._tool = 'wallthick'; + await upd(); + c._wallThickClick([550, 250]); + await upd(); + if (c._wallDialog) { + c._wallDialog = { ...c._wallDialog, value: '20' }; + c._wallThickApply(false); + } + await upd(); + out.realBodyReady = !!sr().querySelector('.wallbodies'); + + // The two-click rubber band used to be below .wallbodies and disappeared + // exactly where it crossed the real T receiver. + c._tool = 'openwall'; + c._openWallClick([550, 200]); + c._cursorPt = [550, 300]; + await upd(); + const bodyGroup = sr().querySelector('.wallbodies'); + const virtualGroup = sr().querySelector('.openwalls'); + out.virtualPreviewDrawn = !!sr().querySelector('.openwall-preview'); + out.virtualPreviewAboveReal = !!bodyGroup && !!virtualGroup + && !!(bodyGroup.compareDocumentPosition(virtualGroup) & Node.DOCUMENT_POSITION_FOLLOWING); + + c._openWallClick([550, 300]); + c._cursorPt = null; + await upd(); + out.partialSpanCreated = (sp().open_spans || []).length === 1; + out.atomicThicknessBefore = c._intervalCm([550, 145, 550, 195]) === 20 + && c._intervalCm([550, 205, 550, 295]) === 0 + && c._intervalCm([550, 305, 550, 455]) === 20; + + const before = JSON.stringify({ + rooms: sp().rooms, + walls: sp().walls, + open_spans: sp().open_spans, + }); + c._tool = 'resize'; + await upd(); + const stage = () => sr().querySelector('.stage'); + const toScreen = (x, y) => { + const r = stage().getBoundingClientRect(); + const v = c._viewOr(c._baseVb()); + return { + clientX: r.left + ((x - v.x) / v.w) * r.width, + clientY: r.top + ((y - v.y) / v.h) * r.height, + }; + }; + const pev = (type, target, x, y) => { + const { clientX, clientY } = toScreen(x, y); + target.dispatchEvent(new PointerEvent(type, { + bubbles: true, composed: true, cancelable: true, + pointerId: 77, clientX, clientY, button: 0, isPrimary: true, + })); + }; + const handle = [...sr().querySelectorAll('.rszhandle:not(.rszcorner)')].find((h) => { + const x = +h.getAttribute('cx'), y = +h.getAttribute('cy'); + return Math.abs(x - 550) < c._gridPitch * 2 && y > 140 && y < 460; + }); + out.resizeHandleFound = !!handle; + if (handle) { + const x = +handle.getAttribute('cx'); + const y = +handle.getAttribute('cy'); + const step = c._gridPitch; + pev('pointerdown', handle, x, y); + pev('pointermove', handle, x + step, y); + await c.updateComplete; + const liveCut = c._openCuts()[0]; + const liveX = liveCut?.[0]; + out.liveSpanMoves = Number.isFinite(liveX) && Math.abs(liveX - (550 + step)) < 0.1; + out.liveThicknessStays = c._intervalCm([liveX, 145, liveX, 195]) === 20 + && c._intervalCm([liveX, 205, liveX, 295]) === 0 + && c._intervalCm([liveX, 305, liveX, 455]) === 20; + out.liveBodyStays = !!sr().querySelector('.wallbodies'); + + pev('pointerup', handle, x + step, y); + await upd(); + const finalCut = c._openCuts()[0]; + const finalX = finalCut?.[0]; + out.commitSpanMoves = Number.isFinite(finalX) && Math.abs(finalX - (550 + step)) < 0.1; + out.commitThicknessStays = c._intervalCm([finalX, 145, finalX, 195]) === 20 + && c._intervalCm([finalX, 205, finalX, 295]) === 0 + && c._intervalCm([finalX, 305, finalX, 455]) === 20; + out.commitKeepsAtomicKeys = (sp().walls || []).filter((w) => w.cm === 20).length >= 2; + + c._rszUndoPop(); + await upd(); + out.undoRestoresAll = JSON.stringify({ + rooms: sp().rooms, + walls: sp().walls, + open_spans: sp().open_spans, + }) === before; + } + + // The screenshot topology: the two real arms belong to different room + // contours and only meet at the endpoint of B↔C's virtual boundary. Their + // per-room butt caps must be completed into one clean outer mitre. + sp().rooms = [ + { id: 'ta', name: 'A', poly: [[0.1, 0.1], [0.5, 0.1], [0.5, 0.5], [0.1, 0.5]] }, + { id: 'tb', name: 'B', poly: [[0.5, 0.5], [0.9, 0.5], [0.9, 0.9], [0.5, 0.9]] }, + { id: 'tc', name: 'C', poly: [[0.5, 0.1], [0.9, 0.1], [0.9, 0.5], [0.5, 0.5]] }, + ]; + delete sp().walls; + delete sp().open_spans; + c._tool = 'wallthick'; + await upd(); + for (const p of [[300, 500], [500, 700]]) { + c._wallThickClick(p); + await upd(); + if (c._wallDialog) { + c._wallDialog = { ...c._wallDialog, value: '20' }; + c._wallThickApply(false); + await upd(); + } + } + sp().open_spans = [{ a: [0.5, 0.5], b: [0.9, 0.5] }]; + sp().rooms.find((r) => r.id === 'tb').open_to = ['tc']; + sp().rooms.find((r) => r.id === 'tc').open_to = ['tb']; + await upd(); + const tPath = sr().querySelector('.wallbody')?.getAttribute('d') || ''; + const values = (tPath.match(/-?\d+(?:\.\d+)?/g) || []).map(Number); + const half = (20 / c._cellCm) * c._gridPitch / 2; + out.virtualTJunctionMitred = values.some((x, i) => i % 2 === 0 + && Math.abs(x - (500 + half)) < 1e-6 + && Math.abs(values[i + 1] - (500 - half)) < 1e-6); + return out; +}); + +checkAll(res); +await finish(browser, res); diff --git a/demo/srv/assets/houseplan-card.js b/demo/srv/assets/houseplan-card.js index cfa97412..c729d259 100755 --- a/demo/srv/assets/houseplan-card.js +++ b/demo/srv/assets/houseplan-card.js @@ -1,10 +1,10 @@ -const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,g=_?_.emptyScript:"",m=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?g: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}},b=(t,e)=>!a(t,e),y={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:b};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=y){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??y}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=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??b)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,m?.({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,T=`<${D}>`,z=document,P=()=>z.createComment(""),A=t=>null===t||"object"!=typeof t&&"function"!=typeof t,R=Array.isArray,E="[ \t\n\f\r]",N=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,O=/-->/g,I=/>/g,F=RegExp(`>|${E}(?:([^\\s"'>=/]+)(${E}*=${E}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,H=/"/g,q=/^(?: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"),V=Symbol.for("lit-nothing"),G=new WeakMap,K=z.createTreeWalker(z,129);function Y(t,e){if(!R(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const X=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",r=N;for(let e=0;e"===l[0]?(r=n??N,c=-1):void 0===l[1]?c=-2:(c=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?F:'"'===l[3]?H:L):r===H||r===L?r=F:r===O||r===I?r=N:(r=F,n=void 0);const d=r===F&&t[e+1].startsWith("/>")?" ":"";o+=r===N?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,o+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const r=t.length-1,a=this.parts,[l,c]=X(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iR(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!==V&&A(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=G.get(t.strings);return void 0===e&&G.set(t.strings,e=new Z(t)),e}k(t){R(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(P()),this.O(P()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=V}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=J(this,t,e,0),o=!A(t)||t!==this._$AH&&t!==j,o&&(this._$AH=t);else{const s=t;let r,a;for(t=n[0],r=0;r{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(P(),t),t,void 0,i??{})}return n._$AI(t),n})(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 ct=at.litElementPolyfillSupport;ct?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ht=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),dt=[{pattern:"протечк|leak|water sensor",icon:"mdi:water-alert"},{pattern:"клапан|valve",icon:"mdi:pipe-valve"},{pattern:"дым|smoke",icon:"mdi:smoke-detector"},{pattern:"термоголов|trv|radiator",icon:"mdi:radiator"},{pattern:"чайник|kettle|термопот",icon:"mdi:kettle"},{pattern:"сауна|sauna|harvia|парная|парилк",icon:"mdi:hot-tub"},{pattern:"температ|temperature|climate sensor",icon:"mdi:thermometer"},{pattern:"qingping|air monitor|молекул|air quality",icon:"mdi:air-filter"},{pattern:"штор|curtain|blind|shade",icon:"mdi:roller-shade"},{pattern:"розетк|plug|socket|outlet",icon:"mdi:power-socket-de"},{pattern:"выключат|switch",icon:"mdi:light-switch"},{pattern:"лампа|лампочк|bulb|gx53|светильник|rgb|lamp|light strip",icon:"mdi:lightbulb"},{pattern:"камер|camera",icon:"mdi:cctv"},{pattern:"замок|ttlock|lock|sn609|sn9161",icon:"mdi:lock"},{pattern:"ворота|garage|gate",icon:"mdi:garage-variant"},{pattern:"калитк|door|открыт|contact",icon:"mdi:door"},{pattern:"счётчик|счетчик|kws|meter",icon:"mdi:meter-electric"},{pattern:"вводный автомат|breaker|wifimcbn",icon:"mdi:electric-switch"},{pattern:"myheat|котёл|котел|boiler|отоплен|heating",icon:"mdi:water-boiler"},{pattern:"холодильник|fridge",icon:"mdi:fridge"},{pattern:"стиральн|washer|washing",icon:"mdi:washing-machine"},{pattern:"сушилк|dryer",icon:"mdi:tumble-dryer"},{pattern:"пылесос|vacuum|dreame|roborock",icon:"mdi:robot-vacuum"},{pattern:"soundbar",icon:"mdi:soundbar"},{pattern:"колонк|станц|speaker|яндекс|yandex|алиса|alice",icon:"mdi:speaker"},{pattern:"tv|телевизор|hyundaitv|mitv|television",icon:"mdi:television"},{pattern:"keenetic|роутер|router|mesh|access point",icon:"mdi:router-wireless"},{pattern:"ибп|ups|kirpich",icon:"mdi:battery-charging-high"},{pattern:"slzb|координат|zigbee|coordinator",icon:"mdi:zigbee"},{pattern:"motion|движен|presence|присутств",icon:"mdi:motion-sensor"},{pattern:"humidity|влажн",icon:"mdi:water-percent"}];function pt(t){const e=[];for(const i of t)if(i&&"string"==typeof i.pattern&&i.icon)try{e.push({re:new RegExp(i.pattern,"i"),icon:i.icon})}catch{}return e}const ut=pt(dt),_t={temperature:"mdi:thermometer",humidity:"mdi:water-percent",motion:"mdi:motion-sensor",occupancy:"mdi:motion-sensor",door:"mdi:door",window:"mdi:window-closed",garage_door:"mdi:garage-variant",smoke:"mdi:smoke-detector",moisture:"mdi:water-alert",gas:"mdi:gas-cylinder",power:"mdi:meter-electric",energy:"mdi:meter-electric",illuminance:"mdi:brightness-5",co2:"mdi:molecule-co2",pm25:"mdi:air-filter",battery:"mdi:battery"},gt="mdi:chip";function mt(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??ut)if(t.test(s))return e;return gt}const ft=["light","switch","cover","valve","lock","climate","fan","media_player","camera","vacuum","humidifier","water_heater","alarm_control_panel","sensor","binary_sensor","event","button","number","select","update"];var vt=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,bt=Math.ceil,yt=Math.floor,wt="[BigNumber Error] ",kt=wt+"Number primitive has more than 15 significant digits: ",$t=1e14,xt=14,St=9007199254740991,Mt=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],Ct=1e7,Dt=1e9;function Tt(t){var e=0|t;return t>0||t===e?e:e-1}function zt(t){for(var e,i,s=1,n=t.length,o=t[0]+"";sc^i?1:-1;for(a=(l=n.length)<(c=o.length)?l:c,r=0;ro[r]^i?1:-1;return l==c?0:l>c^i?1:-1}function At(t,e,i,s){if(ti||t!==yt(t))throw Error(wt+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Rt(t){var e=t.c.length-1;return Tt(t.e/xt)==e&&t.c[e]%2!=0}function Et(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Nt(t,e,i){var s,n;if(e<0){for(n=i+".";++e;n+=i);t=n+t}else if(++e>(s=t.length)){for(n=i,e-=s;--e;n+=i);t+=n}else eb?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>b?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!vt.test(d=String(t)))return n(p,d,c);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(At(e,2,x.length,"Base"),10==e&&S)return z(p=new M(t),_+p.e+1,g);if(d=String(t),c="number"==typeof t){if(0*t!=0)return n(p,d,c,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(kt+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,h=d.length;la){a=h;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return n(p,String(t),c,e)}c=!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(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(t>St||t!==yt(t)))throw Error(kt+p.s*t);if((a=a-l-1)>b)p.c=p.e=null;else if(a=f)?Et(l,r):Nt(l,r,"0");else if(o=(t=z(new M(t),e,i)).e,a=(l=zt(t.c)).length,1==s||2==s&&(e<=o||o<=m)){for(;ar),l=Nt(l,o,"0"),o+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=o-a)>0)for(o+1==a&&(l+=".");e--;l+="0");return t.s<0&&n?"-"+l:l}function D(t,e){for(var i,s,n=1,o=new M(t[0]);n=10;n/=10,s++);return(i=s+i*xt-1)>b?t.c=t.e=null:i=10;a/=10,n++);if((o=e-n)<0)o+=xt,r=e,l=d[c=0],h=yt(l/p[n-r-1]%10);else if((c=bt((o+1)/xt))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,n=1,r=(o%=xt)-xt+1}else{for(l=a=d[c],n=1;a>=10;a/=10,n++);h=(r=(o%=xt)-xt+n)<0?0:yt(l/p[n-r-1]%10)}if(s=s||e<0||null!=d[c+1]||(r<0?l:l%p[n-r-1]),s=i<4?(h||s)&&(0==i||i==(t.s<0?3:2)):h>5||5==h&&(4==i||s||6==i&&(o>0?r>0?l/p[n-r]:0:d[c-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[(xt-e%xt)%xt],t.e=-e||0):d[0]=t.e=0,t;if(0==o?(d.length=c,a=1,c--):(d.length=c+1,a=p[xt-o],d[c]=r>0?yt(l/p[n-r]%p[r])*a:0),s)for(;;){if(0==c){for(o=1,r=d[0];r>=10;r/=10,o++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);o!=a&&(t.e++,d[0]==$t&&(d[0]=1));break}if(d[c]+=a,d[c]!=$t)break;d[c--]=0,a=1}for(o=d.length;0===d[--o];d.pop());}t.e>b?t.c=t.e=null:t.e=f?Et(e,i):Nt(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(wt+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(At(i=t[e],0,Dt,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(At(i=t[e],0,8,e),g=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(At(i[0],-Dt,0,e),At(i[1],0,Dt,e),m=i[0],f=i[1]):(At(i,-Dt,Dt,e),m=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)At(i[0],-Dt,-1,e),At(i[1],1,Dt,e),v=i[0],b=i[1];else{if(At(i,-Dt,Dt,e),!i)throw Error(wt+e+" cannot be zero: "+i);v=-(b=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(wt+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw y=!i,Error(wt+"crypto unavailable");y=i}else y=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(At(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(At(i=t[e],0,Dt,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(wt+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(wt+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:g,EXPONENTIAL_AT:[m,f],RANGE:[v,b],CRYPTO:y,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,n=t.e,o=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===o||-1===o)&&n>=-Dt&&n<=Dt&&n===yt(n)){if(0===s[0]){if(0===n&&1===s.length)return!0;break t}if((e=(n+1)%xt)<1&&(e+=xt),String(s[0]).length==e){for(e=0;e=$t||i!==yt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===n&&(null===o||1===o||-1===o))return!0;throw Error(wt+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(o=9007199254740992,r=Math.random()*o&2097151?function(){return yt(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,n,o,a=0,l=[],c=new M(u);if(null==t?t=_:At(t,0,Dt),n=bt(t/xt),y)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(n*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(o%1e14),a+=2);a=n/2}else{if(!crypto.randomBytes)throw y=!1,Error(wt+"crypto unavailable");for(e=crypto.randomBytes(n*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(o%1e14),a+=7);a=n/7}if(!y)for(;a=10;o/=10,a++);ai-1&&(null==r[n+1]&&(r[n+1]=0),r[n+1]+=r[n]/i|0,r[n]%=i)}return r.reverse()}return function(s,n,o,r,a){var l,c,h,d,p,u,m,f,v=s.indexOf("."),b=_,y=g;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(n)).pow(s.length-v),k=d,f.c=e(Nt(zt(u.c),u.e,"0"),10,o,t),f.e=f.c.length),h=d=(m=e(s,n,o,a?(l=x,t):(l=t,x))).length;0==m[--d];m.pop());if(!m[0])return l.charAt(0);if(v<0?--h:(u.c=m,u.e=h,u.s=r,m=(u=i(u,f,b,y,o)).c,p=u.r,h=u.e),v=m[c=h+b+1],d=o/2,p=p||c<0||null!=m[c+1],p=y<4?(null!=v||p)&&(0==y||y==(u.s<0?3:2)):v>d||v==d&&(4==y||p||6==y&&1&m[c-1]||y==(u.s<0?8:7)),c<1||!m[0])s=p?Nt(l.charAt(1),-b,l.charAt(0)):l.charAt(0);else{if(m.length=c,p)for(--o;++m[--c]>o;)m[c]=0,c||(++h,m=[1].concat(m));for(d=m.length;!m[--d];);for(v=0,s="";v<=d;s+=l.charAt(m[v++]));s=Nt(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,n,o,r,a=0,l=t.length,c=e%Ct,h=e/Ct|0;for(t=t.slice();l--;)a=((n=c*(o=t[l]%Ct)+(s=h*o+(r=t[l]/Ct|0)*c)%Ct*Ct+a)/i|0)+(s/Ct|0)+h*r,t[l]=n%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var n,o;if(i!=s)o=i>s?1:-1;else for(n=o=0;ne[n]?1:-1;break}return o}function i(t,e,i,s){for(var n=0;i--;)t[i]-=n,n=t[i]1;t.splice(0,1));}return function(s,n,o,r,a){var l,c,h,d,p,u,_,g,m,f,v,b,y,w,k,$,x,S=s.s==n.s?1:-1,C=s.c,D=n.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&n.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(m=(g=new M(S)).c=[],S=o+(c=s.e-n.e)+1,a||(a=$t,c=Tt(s.e/xt)-Tt(n.e/xt),S=S/xt|0),h=0;D[h]==(C[h]||0);h++);if(D[h]>(C[h]||0)&&c--,S<0)m.push(1),d=!0;else{for(w=C.length,$=D.length,h=0,S+=2,(p=yt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),$=D.length,w=C.length),y=$,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(b=f[0],$!=v&&(b=b*a+(f[1]||0)),(p=yt(b/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,h++);z(g,o+(g.e=h+c*xt-1)+1,r,d)}else g.e=c,g.r=+d;return g}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,n=function(t,e,i,s){var n,o=i?e:e.replace(d,"");if(h.test(o))t.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(a,function(t,e,i){return n="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=n?t:e}),s&&(n=s,o=o.replace(l,"$1").replace(c,"0.$1")),e!=o))return new M(o,n);if(M.DEBUG)throw Error(wt+"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 Pt(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,n,o=this;if(null!=t)return At(t,0,Dt),null==e?e=g:At(e,0,8),z(new M(o),t+o.e+1,e);if(!(i=o.c))return null;if(s=((n=i.length-1)-Tt(this.e/xt))*xt,n=i[n])for(;n%10==0;n/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,g)},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,n,o,r,a,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(wt+"Exponent not an integer: "+P(t));if(null!=e&&(e=new M(e)),r=t.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!t.c||!t.c[0])return c=new M(Math.pow(+P(h),r?t.s*(2-Rt(t)):+P(t))),e?c.mod(e):c;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&h.isInteger()&&e.isInteger())&&(h=h.mod(e))}else{if(t.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||r&&h.c[1]>=24e7:h.c[0]<8e13||r&&h.c[0]<=9999975e7)))return o=h.s<0&&Rt(t)?-0:0,h.e>-1&&(o=1/o),new M(a?1/o:o);k&&(o=bt(k/xt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Rt(t)):l=(n=Math.abs(+P(t)))%2,c=new M(u);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):s&&(c=c.mod(e))}if(n){if(0===(n=yt(n/2)))break;l=n%2}else if(z(t=t.times(i),t.e+1,1),t.e>14)l=Rt(t);else{if(0===(n=+P(t)))break;l=n%2}h=h.times(h),o?h.c&&h.c.length>o&&(h.c.length=o):s&&(h=h.mod(e))}return s?c:(a&&(c=u.div(c)),e?c.mod(e):o?z(c,k,g,void 0):c)},p.integerValue=function(t){var e=new M(this);return null==t?t=g:At(t,0,8),z(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===Pt(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return Pt(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=Pt(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&Tt(this.e/xt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return Pt(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=Pt(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,n,o,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/xt,c=t.e/xt,h=r.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?r:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?r:3==g?-0:0)}if(l=Tt(l),c=Tt(c),h=h.slice(),a=l-c){for((o=a<0)?(a=-a,n=h):(c=l,n=d),n.reverse(),e=a;e--;n.push(0));n.reverse()}else for(s=(o=(a=h.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;h[i++]=0);for(e=$t-1;s>a;){if(h[--s]=0;){for(i=0,p=b[n]%m,u=b[n]/m|0,o=n+(r=l);o>n;)i=((c=p*(c=v[--r]%m)+(a=u*c+(h=v[r]/m|0)*p)%m*m+_[o]+i)/g|0)+(a/m|0)+u*h,_[o--]=c%g;_[o]=i}return i?++s:_.splice(0,1),T(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,n=s.s;if(e=(t=new M(t,e)).s,!n||!e)return new M(NaN);if(n!=e)return t.s=-e,s.minus(t);var o=s.e/xt,r=t.e/xt,a=s.c,l=t.c;if(!o||!r){if(!a||!l)return new M(n/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*n)}if(o=Tt(o),r=Tt(r),a=a.slice(),n=o-r){for(n>0?(r=o,i=l):(n=-n,i=a),i.reverse();n--;i.push(0));i.reverse()}for((n=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=n),n=0;e;)n=(a[--e]=a[e]+l[e]+n)/$t|0,a[e]=$t===a[e]?0:a[e]%$t;return n&&(a=[n].concat(a),++r),T(t,a,r)},p.precision=p.sd=function(t,e){var i,s,n,o=this;if(null!=t&&t!==!!t)return At(t,1,Dt),null==e?e=g:At(e,0,8),z(new M(o),t,e);if(!(i=o.c))return null;if(s=(n=i.length-1)*xt+1,n=i[n]){for(;n%10==0;n/=10,s--);for(n=i[0];n>=10;n/=10,s++);}return t&&o.e+1>s&&(s=o.e+1),s},p.shiftedBy=function(t){return At(t,-9007199254740991,St),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,n,o,r=this,a=r.c,l=r.s,c=r.e,h=_+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(+P(r)))||l==1/0?(((e=zt(a)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=Tt((c+1)/2)-(c<0||c%2),s=new M(e=l==1/0?"5e"+c:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+c)):s=new M(l+""),s.c[0])for((l=(c=s.e)+h)<3&&(l=0);;)if(o=s,s=d.times(o.plus(i(r,o,h,1))),zt(o.c).slice(0,l)===(e=zt(s.c)).slice(0,l)){if(s.e0&&_>0){for(o=_%a||a,h=u.substr(0,o);o<_;o+=a)h+=c+u.substr(o,a);l>0&&(h+=c+u.slice(o)),p&&(h="-"+h)}s=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,n,o,r,a,l,c,h,d,p,_,m=this,f=m.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(wt+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+P(l));if(!f)return new M(m);for(e=new M(u),h=s=new M(u),n=c=new M(u),_=zt(f),r=e.e=_.length-m.e-1,e.c[0]=Mt[(a=r%xt)<0?xt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:h:l,a=b,b=1/0,l=new M(_),c.c[0]=0;d=i(l,e,0,1),1!=(o=s.plus(d.times(n))).comparedTo(t);)s=n,n=o,h=c.plus(d.times(o=h)),c=o,e=l.minus(d.times(o=e)),l=o;return o=i(t.minus(s),n,0,1),c=c.plus(o.times(h)),s=s.plus(o.times(n)),c.s=h.s=m.s,p=i(h,n,r*=2,g).minus(m).abs().comparedTo(i(c,s,r,g).minus(m).abs())<1?[h,n]:[c,s],b=a,p},p.toNumber=function(){return+P(this)},p.toPrecision=function(t,e){return null!=t&&At(t,1,Dt),C(this,t,e,2)},p.toString=function(t){var e,i=this,n=i.s,o=i.e;return null===o?n?(e="Infinity",n<0&&(e="-"+e)):e="NaN":(null==t?e=o<=m||o>=f?Et(zt(i.c),o):Nt(zt(i.c),o,"0"):10===t&&S?e=Nt(zt((i=z(new M(i),_+o+1,g)).c),i.e,"0"):(At(t,2,x.length,"Base"),e=s(Nt(zt(i.c),o,"0"),10,t,n,!0)),n<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return P(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),It=class{key;left=null;right=null;constructor(t){this.key=t}},Ft=class extends It{constructor(t){super(t)}},Lt=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,n=null,o=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==n?o=r:n.right=r,n=r,r=e}}return null!=n&&(n.right=r.left,r.left=o),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)}}},Ht=class t extends Lt{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 Ft(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ft(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 Ft(t.key);return function t(e,i){let s,n;do{if(s=e.left,n=e.right,null!=s){const e=new Ft(s.key);i.left=e,t(s,e)}if(null!=n){const t=new Ft(n.key);i.right=t,e=n,i=t}}while(null!=n)}(t,e),e}toSet(){return this.clone()}entries(){return new Wt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Ut(this.wrap())}[Symbol.toStringTag]="[object Set]"},qt=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}},Ut=class extends qt{getValue(t){return t.key}},Wt=class extends qt{getValue(t){return[t.key,t.key]}},Bt=t=>()=>t,jt=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Bt(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Vt(t){const e=t?(e,i,s,n,o)=>e.exponentiatedBy(2).isLessThanOrEqualTo(n.minus(i).exponentiatedBy(2).plus(o.minus(s).exponentiatedBy(2)).times(t)):Bt(!1);return(t,i,s)=>{const n=t.x,o=t.y,r=s.x,a=s.y,l=o.minus(a).times(i.x.minus(r)).minus(n.minus(r).times(i.y.minus(a)));return e(l,n,o,r,a)?0:l.comparedTo(0)}}var Gt=t=>t,Kt=t=>{if(t){const e=new Ht(jt(t)),i=new Ht(jt(t)),s=(t,e)=>e.addAndReturn(t),n=t=>({x:s(t.x,e),y:s(t.y,i)});return n({x:new Ot(0),y:new Ot(0)}),n}return Gt},Yt=t=>({set:t=>{Xt=Yt(t)},reset:()=>Yt(t),compare:jt(t),snap:Kt(t),orient:Vt(t)}),Xt=Yt(),Zt=(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),Jt=(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}}},Qt=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),te=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ee=t=>te(t,t).sqrt(),ie=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return Qt(n,s).div(ee(n)).div(ee(s))},se=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return te(n,s).div(ee(n)).div(ee(s))},ne=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},oe=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},re=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:_e.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:ie(this.point,t.point,s.point),cosine:se(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:n,cosine:o}=e.get(t),{sine:r,cosine:a}=e.get(s);return n.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?o.isLessThan(a)?1:o.isGreaterThan(a)?-1:0:n.isLessThan(0)&&r.isLessThan(0)?o.isLessThan(a)?-1:o.isGreaterThan(a)?1:0:r.isLessThan(n)?-1:r.isGreaterThan(n)?1:0}}},ae=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,n=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}}},le=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&&n){const t=s.getIntersection(n);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(c))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(c))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(n.isLessThan(o)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(n.isGreaterThan(o)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!n.eq(o)){const t=l.minus(r),e=n.minus(i),h=c.minus(a),d=o.minus(s);if(t.isGreaterThan(e)&&h.isLessThan(d))return 1;if(t.isLessThan(e)&&h.isGreaterThan(d))return-1}return n.isGreaterThan(o)?1:n.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++ue,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 n,o,r;const a=re.comparePoints(e,i);if(a<0)n=e,o=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);n=i,o=e,r=-1}const l=new re(n,!0),c=new re(o,!1);return new t(l,c,[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 Xt.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=Jt(e,i);if(null===s)return null;const n=this.leftSE.point,o=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=Zt(e,r)&&0===this.comparePoint(r),c=Zt(i,n)&&0===t.comparePoint(n),h=Zt(e,a)&&0===this.comparePoint(a),d=Zt(i,o)&&0===t.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?a:null;if(c)return h&&n.x.eq(a.x)&&n.y.eq(a.y)?null:n;if(l)return d&&o.x.eq(r.x)&&o.y.eq(r.y)?null:r;if(d&&h)return null;if(d)return o;if(h)return a;const p=((t,e,i,s)=>{if(e.x.isZero())return oe(i,s,t.x);if(s.x.isZero())return oe(t,e,i.x);if(e.y.isZero())return ne(i,s,t.y);if(s.y.isZero())return ne(t,e,i.y);const n=Qt(e,s);if(n.isZero())return null;const o={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=Qt(o,e).div(n),a=Qt(o,s).div(n),l=t.x.plus(a.times(e.x)),c=i.x.plus(r.times(s.x)),h=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(n,this.vector(),r,t.vector());return null===p?null:Zt(s,p)?Xt.snap(p):null}split(e){const i=[],s=void 0!==e.events,n=new re(e,!0),o=new re(e,!1),r=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(n);const a=new t(n,r,this.rings.slice(),this.windings.slice());return re.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),re.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(n.checkForConsuming(),o.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}},ge=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=Xt.snap({x:new Ot(t[0][0]),y:new Ot(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let n=s;for(let e=1,i=t.length;epe.run("union",t,e),be=(t,...e)=>pe.run("intersection",t,e),ye=(t,...e)=>pe.run("difference",t,e);function we(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function ke(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 $e(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 xe(t,e,i=1){const s=t[0].toFixed(i),n=t[1].toFixed(i),o=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 Me(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 Ce(t){const e=[],i=new Set;for(const s of t||[]){const t=Se(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),o=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(o,Math.min(i-o,a));const l=a/i;n={x:e+l*c,y:r+l*h,angle:t}}else n={x:u[0],y:u[1],angle:t}}}return n}function Te(t,e,i,s,n,o=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,c=o;for(const e of Ce(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])>o||s(i[1])>o)continue;const n=(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],h=Math.min(n,r),d=Math.max(n,r),p=h>0?h:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],m=(h+d)/2;return{wallA:g(h),wallB:g(d),sideA:u,sideB:_,midA:g((h-p)/2),midB:g((p+d)/2),wallCenter:g(m),centered:Math.abs(m)<=n}}function ze(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}function Pe(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}function Re(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;let r=o?((t[0]-e[0])*s+(t[1]-e[1])*n)/o:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*n))}function Ee(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let n=0;nn&&r<-n||o<-n&&r>n)&&(a>n&&l<-n||a<-n&&l>n)}function Le(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1;let c=0,h=0,d=0;for(let e=0;e1e-9?[h/(3*c),d/(3*c)]:[(n+o)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!Ae([e,i],t))return-1/0;let s=1/0;for(let n=0;ng&&(g=c,_=[s,l])}if(_){const[t,i]=_,s=(o-n)/e,l=(a-r)/e;for(let e=-4;e<=4;e++)for(let n=-4;n<=4;n++){const o=t+s*e/4,r=i+l*n/4,a=u(o,r);a>g&&(g=a,_=[o,r])}}return _||He(t)||t[0]}function He(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(Oe(s,t,e))return s;for(let s=0;s[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ve(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=ve(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}function Ge(t,e,i){for(let s=0;s1&&Pe(i[0],i[i.length-1],e)&&i.pop(),i}function Ye(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function Xe(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 Ze(t,e,i,s){if(t.length<2)return;const n=e.x+s,o=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}function ai(t){return"opening"===t||"closing"===t}const li=["automation","script","scene"];function ci(t,e,i){const s=String(e??"").trim();if(!s)return null;const n=t?.states?.[s];if(!n)return null;const o=String(i??"").trim(),r=t=>t.slice(0,60);if(o){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)}(n.attributes?.[o]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,n,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=n.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function hi(t,e,i){const s=String(e??"").trim(),n=String(i??"").trim()||s;if(!n)return t.text;const o=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`${o} ${n}`}function di(t,e,i){const s=t??"";if(!(e?.entity||"").trim())return s;const n=function(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],n=s?.state;if(!s||null==n||""===n||"unavailable"===n||"unknown"===n)return"—";const o=(e?.attr||"").trim(),r=ci(t,i,o||null);return null===r?"—":hi(r,o?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}(i,e),o=s.indexOf("{}");return o>=0?s.slice(0,o)+n+s.slice(o+2):s?`${s} ${n}`:n}function pi(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 ui(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}function _i(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 gi="#55606c",mi=.55;function fi(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:gi,opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):mi,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 vi(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 bi={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}},yi=/^#[0-9a-f]{6}$/i;function wi(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(bi)){const s=bi[t],n=i[t];e[t]={c:n&&"string"==typeof n.c&&yi.test(n.c)?n.c:s.c,a:n&&"number"==typeof n.a?Math.min(1,Math.max(0,n.a)):s.a}}return e}function ki(t,e,i){const s=Math.min(1,Math.max(0,i)),n=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),o=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=n.map((t,e)=>Math.round(t+(o[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function $i(t,e,i,s,n,o,r){if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:ki(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(n,o),e=Math.max(n,o);return se?r.temp_hot:r.temp_ok}return null}const xi={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"]},Si=[["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 Mi(t){for(const e of[...Object.values(xi),...Si])if(t===e[0]||t===e[1])return e;return null}function Ci(t,e,i,s,n){if(!s||"unavailable"===s||"unknown"===s)return t;if(n){const i="cover"===e?Mi(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=xi[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const n=Mi(t);return n?"closed"===s?n[0]:n[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 Di(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}function Ti(t,e){if(!t||"on"!==t.state)return null;const i=t.attributes||{},s=Number(i.brightness),n=Number.isFinite(s)&&s>0?Math.max(.15,Math.min(1,s/255)):1,o=i.rgb_color;if(Array.isArray(o)&&o.length>=3&&o.every(t=>Number.isFinite(t)))return{c:`rgb(${o[0]}, ${o[1]}, ${o[2]})`,bri:n};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),n=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,o=t=>Math.round(Math.min(255,Math.max(0,t)));return[o(i),o(s),o(n)]}(r);return{c:`rgb(${t}, ${e}, ${i})`,bri:n}}return{c:e,bri:n}}function zi(t,e,i,s,n=170){const o=Math.hypot(e[0]-t[0],e[1]-t[1]),r=Math.hypot(i[0]-t[0],i[1]-t[1]);if(o<1e-6||r<1e-6||Math.min(o,r)>=s)return null;let a=Math.atan2(e[1]-t[1],e[0]-t[0]),l=Math.atan2(i[1]-t[1],i[0]-t[0])-a;for(;l>Math.PI;)l-=2*Math.PI;for(;l<-Math.PI;)l+=2*Math.PI;const c=n*Math.PI/180;if(Math.abs(l)>c){const t=a+l/2;l=c*Math.sign(l),a=t-l/2}const h=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=a+l*e/8;h.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return h}function Pi(t,e,i,s,n){const o=e*Math.PI/180,r=[-Math.sin(o),Math.cos(o)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*n*a,t[1]+r[1]*n*a];return s.some(t=>Oe(l,t,1e-9))}function Ai(t){return t.startsWith("light.")||t.startsWith("switch.")}function Ri(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let n=0;np||l>p)continue;const u=(n[0]-o[0])*h+(n[1]-o[1])*d,_=(r[0]-o[0])*h+(r[1]-o[1])*d,g=Math.max(0,Math.min(u,_)),m=Math.min(c,Math.max(u,_));m-g>i&&s.push([o[0]+h*g,o[1]+d*g,o[0]+h*m,o[1]+d*m])}}return s}function Ei(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let n=!0;for(;n;){n=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const o of e)if(o.id&&i.has(o.id)&&s(t,o)){i.add(t.id),n=!0;break}}return i}function Ni(t,e,i=1e-6){const s=[];for(const n of t){const t=[n[0],n[1]],o=[n[2],n[3]],r=o[0]-t[0],a=o[1]-t[1],l=Math.hypot(r,a);if(lo||n>o)continue;const r=(s[0]-t[0])*c+(s[1]-t[1])*h,a=(s[2]-t[0])*c+(s[3]-t[1])*h,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],o[0],o[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,n]of d)e-p>i&&s.push([t[0]+c*p,t[1]+h*p,t[0]+c*e,t[1]+h*e]),p=Math.max(p,n);l-p>i&&s.push([t[0]+c*p,t[1]+h*p,o[0],o[1]])}return s}const Oi=864e5,Ii=576e5;function Fi(t){const e=new Set,i=t=>{if("string"!=typeof t||!t)return;const i=Li(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 Li(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 Hi(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i?i:t}function qi(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Ui(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Wi(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Bi(t,e){const i=e[2]-e[0],s=e[3]-e[1],n=i*i+s*s;if(!n)return Math.hypot(t[0]-e[0],t[1]-e[1]);let o=((t[0]-e[0])*i+(t[1]-e[1])*s)/n;return o=Math.max(0,Math.min(1,o)),Math.hypot(t[0]-(e[0]+o*i),t[1]-(e[1]+o*s))}const ji=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Vi=(t,e)=>[t[0]-e[0],t[1]-e[1]],Gi=(t,e)=>[t[0]+e[0],t[1]+e[1]],Ki=(t,e)=>t[0]*e[0]+t[1]*e[1],Yi=t=>Math.hypot(t[0],t[1]);function Xi(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function Qi(t,e,i,s){const n=s||Ji(t,e),o=(e+1)%t.length;return t.map((t,s)=>s===e||s===o?[t[0]+n[0]*i,t[1]+n[1]*i]:[...t])}function ts(t,e,i,s){const n=[],o=Vi(i,e),r=Yi(o);if(rs||h>s)continue;const d=Ki(Vi(o,e),a),p=Ki(Vi(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&n.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return n}function es(t,e,i,s,n){const o=ts(t,e,i,n);if(!o.length)return null;const r=t=>o.some(([e,i])=>Zi(t,e,i)<=n),a=t.length,l=[];for(let o=0;on||f>n)continue;const v=Ki(Vi(e,c),u),b=Ki(Vi(i,c),u),y=Math.max(0,Math.min(v,b)),w=Math.min(p,Math.max(v,b));if(!(w-y<=n)){if(y>n&&yn&&wYi(Vi(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const n=i[(s-1+i.length)%i.length],o=i[(s+1)%i.length],r=(t[0]-n[0])*(o[1]-n[1])-(t[1]-n[1])*(o[0]-n[0]),a=Yi(Vi(o,n))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function ss(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-n[0])*a[1]-(t[1]-n[1])*a[0],c=t=>(t[0]-n[0])*a[0]+(t[1]-n[1])*a[1],h=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const n=c(e);n<=h||n>=d||tMath.abs(r+(t-p)/m*(a-r));s=Math.min(s,f(_),f(g))}}return s}function os(t,e,i){if(We(t,e,i))return!0;if(Ue(t,e,i)||Ue(e,t,i))return!1;let s=0;try{const i=be([[...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+=Be(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function rs(t,e,i,s,n){const o=[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]=Qi(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[Gi(i.a,o),Gi(i.b,o)]];continue}const t=ts(e.poly,i.a,i.b,n);if(!t.length)continue;const a=es(e.poly,i.a,i.b,o,n);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[Gi(t,o),Gi(e,o)]))}for(const t of e)Zi([t.x,t.y],i.a,i.b)<=n&&(r.openings[t.id]=[t.x+o[0],t.y+o[1]]);return r}function as(t,e,i){for(const s of e)for(let e=0;ei)continue;const c=(t.x-n[0])*l[0]+(t.y-n[1])*l[1];if(c-t.length/2>=-i&&c+t.length/2<=a+i)return!0}return!1}function ls(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),n=a.polys[e];if(!ss(n))return!1;const l=Xi(s.poly),h=Xi(n);if(Math.abs(h)t.find(t=>t.id===e).poly),d=t.map(c);for(const t of ls(e,h,2*r)){const e=a.openings[t.id];if(!as(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function hs(t,e,i,s,n,o){const r=t=>[s[0]+(t[0]-s[0])*n,s[1]+(t[1]-s[1])*n],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 c=t.filter(t=>t.id!==i).map(t=>t.poly),h=hs(l,e,c,s,n,2*a),d=h.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 n=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;n.length>=2&&i(n[n.length-2],n[n.length-1],s)<=0;)n.pop();n.push(s)}return s.pop(),n.pop(),s.concat(n)}(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 ls(e,[l.poly],2*a)){const e=h.openings[t.id];if(!as(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function ps(t,e,i){const s=i/e;return Be(t)*s*s/1e4}function us(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const _s=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],gs=t=>Math.min(1,Math.max(0,t));function ms(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=_s[_s.length-1][1];for(let t=1;t<_s.length;t++){const[s,n]=_s[t-1],[o,r]=_s[t];if(e<=o){i=ki(n,r,(e-s)/(o-s));break}}return{bg:i,planDim:.1*gs((10-e)/16),warmth:e<0?1:gs(1-e/10)}}function fs(t,e,i=6){const s=t.angle*Math.PI/180,n=[Math.sin(s),-Math.cos(s)],o=s=>{const o=[t.x+n[0]*i*s,t.y+n[1]*i*s];return e.find(t=>t.poly.length>=3&&Ae(o,t.poly))||null},r=o(1),a=o(-1);return r&&a?null:r||a?r?{normal:[-n[0],-n[1]],roomId:r.id}:{normal:n,roomId:a.id}:null}function vs(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function bs(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 ys(t,e){try{const i=be([[...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 ws(t,e,i,s,n,o,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,n),l=[-a[0],-a[1]],c=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),h=[];for(const i of e){if(!(i.length>0))continue;const e=fs(i,t);if(!e||!vs(e.normal,a,s))continue;const n=t.find(t=>t.id===e.roomId);if(!n)continue;const d=o&&o[e.roomId]||n.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],g=Math.max(0,r?.[i.id]||0),m=i.x+_[0]*g/2,f=i.y+_[1]*g/2,v=Math.cos(p)*u,b=Math.sin(p)*u,y=[m-v,f-b],w=[m+v,f+b],k=c*i.length,$=ys(bs(y,w,l,k),d);if(!$.length)continue;const x=l[0]*_[0]+l[1]*_[1];h.push({openingId:i.id,roomId:e.roomId,polys:$,a:y,b:w,dir:l,len:k,normal:_,depth:k*x})}return h}function ks(t){return Math.round(10*(Number(t)||0))/10}const $s={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 xs=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Ss(t,e){const i=xs(e?.north_deg);return null!==i?i:xs(t?.north_deg)}function Ms(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function Cs(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Ds(t){const e=t?.weather_entity;return"string"==typeof e&&e.trim()?e.trim():null}function Ts(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 zs=1e3;function Ps(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,n=s>=1?e:e*s,o=s>=1?e/s:e;return{x:(e-n)/2,y:(e-o)/2,w:n,h:o}}const As=.01;function Rs(t,e=1e3){const i=Ps(t?.plan_aspect,e),s=Number(t?.plan_scale),n=Number.isFinite(s)&&s>0?Math.min(100,Math.max(As,s)):1,o=Number(t?.plan_x),r=Number(t?.plan_y);return{x:i.x+(Number.isFinite(o)?Bs(o):0)*e,y:i.y+(Number.isFinite(r)?Bs(r):0)*e,w:i.w*n,h:i.h*n}}function Es(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 Ns(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=zs,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]*zs,i[1]*e,i[2]*zs,i[3]*e],bg:t.plan_url?{href:Li(t.plan_url),...Rs(t,zs)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...Es(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*zs:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*zs:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*zs,t[1]*e]):void 0}})}}):[]}const Os=5e3,Is=Os*zs,Fs=240,Ls=zs/Fs,Hs=1/Fs;function qs(t){if(!Number.isFinite(t))return t;const e=Math.round(t*Fs/zs)*zs/Fs;return Math.abs(e-t)<=1e-9*Ls?t:e}function Us(t){return{x:qs(t.x),y:qs(t.y)}}function Ws(t){return Number.isFinite(t)?Math.min(Is,Math.max(-Is,t)):0}function Bs(t){return Number.isFinite(t)?Math.min(Os,Math.max(-Os,t)):0}const js=1/3,Vs=200;function Gs(t){let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t){const t=Number(o[0]),r=Number(o[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>n&&(n=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:n}}function Ks(t){return t.poly&&t.poly.length?Gs(t.poly):null==t.x||null==t.y?null:Gs([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function Ys(t,e){const i=[];for(const e of t.rooms||[]){const t=Ks(e);t&&i.push(t)}t.bg&&i.push({minX:t.bg.x,minY:t.bg.y,maxX:t.bg.x+t.bg.w,maxY:t.bg.y+t.bg.h});for(const t of e||[])if(Array.isArray(t)){const e=Gs([t]);e&&i.push(e)}else i.push(t);return i}const Xs=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},Zs=t=>{let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t)o.minXs&&(s=o.maxX),o.maxY>n&&(n=o.maxY);return e>s||i>n?null:{x:e,y:i,w:s-e,h:n-i}};function Js(t,e){let{x:i,y:s,w:n,h:o}=t;n<30&&(i=i+n/2-100,n=Vs),o<30&&(s=s+o/2-100,o=Vs);const r=Math.max(n,o)*e;return{x:i-r,y:s-r,w:n+2*r,h:o+2*r}}function Qs(t,e={}){const i=e.pad??.05,s=e.k??10,n=e.minSpread??50,o=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=Is&&Math.abs(t.maxX)<=Is&&Math.abs(t.minY)<=Is&&Math.abs(t.maxY)<=Is);if(!o.length)return{core:null,all:null,outliers:0};const r=Zs(o);if(o.length<4){const t=Js(r,i);return{core:t,all:t,outliers:0}}const a=o.map(t=>(t.minX+t.maxX)/2),l=o.map(t=>(t.minY+t.maxY)/2),c=Xs(a),h=Xs(l),d=o.map((t,e)=>Math.max(Math.abs(a[e]-c),Math.abs(l[e]-h))),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),n),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,g=_&&_<=o.length*js?o.filter((t,e)=>!u[e]):o;return{core:Js(Zs(g)||r,i),all:Js(r,i),outliers:g===o?0:_}}function tn(t,e,i=.05){const s=Qs(Ys(t,e),{pad:i});if(s.core)return s.core;const n=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,zs,zs];return{x:n[0],y:n[1],w:n[2],h:n[3]}}function en(t){const e=tn(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function sn(t){const e=[];for(const i of t.rooms||[]){const t=Ks(i);t&&e.push(t)}const i=Qs(e,{pad:0}).core;return i?Math.max(zs,Math.min(Is,Math.max(i.w,i.h))):zs}function nn(t,e,i,s=1){const n=Number(i),o=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(n)||n<=0?t*o:t*sn(e)*o/n}const on=[1,2,5,10,20,50,100,200,500,1e3];function rn(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),n=Math.min(...i);return{x:s,y:n,w:Math.max(...e)-s,h:Math.max(...i)-n}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function an(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 ln=["furniture","appliance","sanitary","other"],cn=[{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]]}],hn=new Map(cn.map(t=>[t.id,t]));function dn(t){return t&&hn.get(t)||null}function pn(t,e,i=Ls,s=1e3){const n=Number(e)>0?Number(e):5;return(Number(t)||0)/n*i/s}const un=5e-4,_n=Os;function gn(t){return Number.isFinite(t)?Math.max(un,Math.min(_n,t)):un}const mn=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function fn(t,e,i){const s=dn(t);if(!(s&&e>0&&i>0))return"";const n=t=>mn(t*e),o=t=>mn(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}H${n(e+s)}V${o(i+a)}H${n(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}L${n(s)} ${o(a)}`)}else if("e"===t[0]){const[,s,a,l,c]=t;r.push(`M${n(s-l)} ${o(a)}A${mn(l*e)} ${mn(c*i)} 0 0 1 ${n(s+l)} ${o(a)}A${mn(l*e)} ${mn(c*i)} 0 0 1 ${n(s-l)} ${o(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${n(e[0])} ${o(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,n,o=0){let r=null,a=n;for(const n of s){const[s,l,c,h]=n,d=c-s,p=h-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let g=((t-s)*d+(e-l)*p)/u;g=Math.max(0,Math.min(1,g));let m=s+g*d,f=l+g*p;const v=Math.hypot(t-m,e-f);if(!(v0){const t=Math.round(g*_/o)*o;m=s+t/_*d,f=l+t/_*p}r={cx:m+b*(i/2),cy:f+y*(i/2),angle:vn(180*Math.atan2(-b,y)/Math.PI),dist:v}}return r}function yn(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function wn(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function kn(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function $n(t,e){const i="number"==typeof t?t:parseFloat(String(t).trim().replace(",","."));if(!Number.isFinite(i)||i<=0)return null;return wn(e?2.54*i:i)}function xn(t,e,i){const s=Number(e)>0?Number(e):5;return wn(t)/s*i}function Sn(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function Mn(t,e){let i=e[0]-t[0],s=e[1]-t[1];const n=Math.hypot(i,s);return n<1e-12?[1,0]:(i/=n,s/=n,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function Cn(t,e,i){const s=Sn((t[0]+e[0])/2,i),n=Sn((t[1]+e[1])/2,i),[o,r]=Mn(t,e);let a=Math.atan2(r,o);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,c=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(c)},${n.toFixed(c)}@${l.toFixed(4)}`}function Dn(t,e,i,s){return 1===s?Cn(t,e,i):Cn([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function Tn(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[n,o]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[n,o,r].every(Number.isFinite)&&s.push({w:e,x:n*i,y:o*i,ang:r})}return s}function zn(t,e){const[i,s]=Mn(t,e);let n=Math.atan2(s,i);return n<0&&(n+=Math.PI),n}function Pn(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function An(t,e,i,s,n=1){if(!t?.length)return null;const o=Dn(e,i,s,n),r=t.find(t=>t.key===o);if(r)return r;const a=n>0?n:1,l=(e[0]+i[0])/2,c=(e[1]+i[1])/2,h=zn(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of Tn(t,a))if(Pn(e.ang,h)&&Math.hypot(e.x-l,e.y-c)<=d)return e.w;return null}function Rn(t,e,i,s,n=1){const o=An(t,e,i,s,n);return o&&o.cm>0?wn(o.cm):0}function En(t,e,i,s=1,n=[]){if(!t?.length)return[];const o=new Set;for(const t of Ce(e))o.add(Dn([t[0],t[1]],[t[2],t[3]],i,s));const r=e||[],a=Math.max(i*s*.02,1e-9);for(let t=0;to.has(t.key)&&t.cm>=1&&t.cm<=100)}function Nn(t,e,i,s=8){const[n,o]=Mn(t,e);let r=Math.atan2(o,n);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 On(t,e,i,s,n,o=1){const r=Dn(e,i,n,o),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,{key:r,cm:wn(s)}]}function In(t,e,i,s,n,o=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,n=1){const o=Gn(t,e,i,s,n);if(!o)return[];const r=[];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),n=Bn(i,t);if(s&&n)return`${jn(s)} ${jn(Vn(n))}`}}let s="";for(let i=0;i.05)&&Jn(r,a,t[0],t[1],t[2],t[3])<=o)return!0}return!1}function Hn(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function qn(t,e){const i=t[e],s=t[(e+1)%t.length],n=s[0]-i[0],o=s[1]-i[1],r=Math.hypot(n,o)||1;let a=-o/r,l=n/r;const c=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,n=e.length-1;st[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r+0)+o&&(i=!i)}return i}([c[0]+.001*a,c[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function Un(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 Wn(t,e,i,s){const n=e[0]*s[1]-e[1]*s[0];if(Math.abs(n)<1e-12)return null;const o=[i[0]-t[0],i[1]-t[1]],r=(o[0]*s[1]-o[1]*s[0])/n;return[t[0]+r*e[0],t[1]+r*e[1]]}function Bn(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 n=0;n0||d>0)){s.push([t[n][0],t[n][1]]);continue}if(Un(y,w)){const e=t[n],i=[e[0]+p*h,e[1]+u*h],o=[e[0]+_*d,e[1]+g*d];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=Wn(k,y,$,w),S=Math.max(h,d,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}h>0&&s.push([t[n][0]+p*h,t[n][1]+u*h]),d>0&&s.push([t[n][0]+_*d,t[n][1]+g*d]),h>0||d>0||s.push([t[n][0],t[n][1]])}return s.length>=3?s:null}function jn(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),r=Se(o);if(!r||r.length<3)return null;const a=Hn(s,n),l=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Se(i);if(t)for(const e of Ri(r,t,a))l.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])l.push([t[0],t[1]],[t[2],t[3]]);const c=[],h=[];for(let t=0;ta)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=n||r>=1-n||(o.some(t=>Math.abs(t-r)*s<=2*a)||o.push(r))}o.sort((t,e)=>t-e);for(const s of o)c.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),h.push(t)}return{poly:c,parent:h,orig:r}}function Kn(t,e,i,s){const n=e.poly.length,o=new Array(n).fill(0);if(!t?.length)return o;const r=new Set,a=[];for(let l=0;l0?(o[l]=wn(c.cm),r.add(c.key)):a.push(l)}if(!a.length)return o;const l=s>0?s:1,c=Math.max(.5*i,1e-9)*l,h=Tn(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],n=e.orig[(t+1)%e.orig.length],a=zn(s,n),l=(s[0]+n[0])/2,d=(s[1]+n[1])/2;let p=null;for(const t of h){if(r.has(t.w.key))continue;if(!Pn(t.ang,a))continue;if(Jn(t.x,t.y,s[0],s[1],n[0],n[1])>c)continue;const e=Math.hypot(t.x-l,t.y-d);(!p||et?.id===e),n=Se(s);if(!n)return[];const o=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Se(s);if(t)for(const e of Ri(n,t,i))o.push(e)}return o}(t,e,Hn(n,a)),h=function(t,e,i,s,n){const o=Hn(s,n),r=[];for(let a=0;aJn(h,d,t[0],t[1],t[2],t[3])<=o);r.push(p?"shared":"outer")}return r}(l.poly,c,s,n,a),d=Kn(i,l,n,a),p=d.map((t,e)=>h[e]&&t>0?xn(t,o,r)/2:0);return{...l,kinds:h,cms:d,offsets:p}}function Xn(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Yn(t,l.id,e,i,s,n,o,r);if(c)for(let t=0;t4*l||(!p||tt?.id===e),c=Se(l);if(!c||c.length<3)return null;if(!i?.length)return c.map(t=>[t[0],t[1]]);const h=Yn(t,e,i,s,n,o,r,a);return h&&h.offsets.some(t=>t>0)&&Bn(h.poly,h.offsets)||c.map(t=>[t[0],t[1]])}function to(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=[],n,o,r,a=1){if(!e?.length)return null;const l=[],c=[];let h=0;for(const s of t||[]){if(!s?.id)continue;const d=Yn(t,s.id,e,i,n,o,r,a);if(!d||d.poly.length<3||!d.offsets.some(t=>t>0))continue;for(const t of d.offsets)t>0&&(h=Math.max(h,2*t));const p=io(d.poly,d.offsets),u=Bn(d.poly,d.offsets);p&&l.push(p),u&&c.push(u)}if(!l.length)return null;try{let t=to(l[0]);for(let e=1;e0))continue;const i=t.angle*Math.PI/180,s=Math.cos(i),o=Math.sin(i),r=-o,l=s,c=t.length/2,d=1.25*Math.max(h,n*a),p=[[t.x-s*c-r*d,t.y-o*c-l*d],[t.x+s*c-r*d,t.y+o*c-l*d],[t.x+s*c+r*d,t.y+o*c+l*d],[t.x-s*c+r*d,t.y-o*c+l*d]];e=ye(e,to(p))}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?" ":"")+jn(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:h}:null}catch{const s=function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Yn(t,l.id,e,i,s,n,o,r);if(!c||c.poly.length<3||!c.offsets.some(t=>t>0))continue;const h=io(c.poly,c.offsets),d=Bn(c.poly,c.offsets);if(!h||!d)continue;const p=`${jn(h)} ${jn(Vn(d))}`;let u="",_="outer",g=0,m=0;for(let t=0;t0){u=Dn(c.poly[t],c.poly[(t+1)%c.poly.length],s,r),_=c.kinds[t]||"outer",g=c.cms[t],m=xn(g,n,o);break}a.push({d:p,key:u,kind:_,cm:g,depthUnits:m})}return a}(t,e,i,n,o,r,a);return s.length?{d:s.map(t=>t.d).join(" "),depthUnits:h}: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]]);Vn(t),e.slice().reverse();const s=[];for(let n=0;n0||a>0)){s.push([t[n][0],t[n][1]]);continue}if(Un(y,w)){const e=t[n],i=[e[0]-l*r,e[1]-c*r],o=[e[0]-h*a,e[1]-d*a];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=Wn(k,y,$,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[n][0]-l*r,t[n][1]-c*r]),a>0&&s.push([t[n][0]-h*a,t[n][1]-d*a])}return s.length>=3?s:null}function so(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){const c=Se(l);if(c&&c.length>=3){const h=Yn(t,l.id,e,i,s,n,o,r),d=h&&h.offsets.some(t=>t>0)?io(h.poly,h.offsets):null,p=d||c;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 no(t,e,i,s,n,o,r=1){let a=null;for(const i of t||[]){const t=Se(i);if(t)for(let s=0;ss*r)return{ox:0,oy:0,cm:0};const l=Rn(i,a.a,a.b,s,r);if(!(l>0))return{ox:0,oy:0,cm:0};const c=xn(l,n,o),h=Se(a.room),[d,p]=qn(h,a.edge),u=e.flip_v?-1:1,_=c/2;return{ox:d*_*u,oy:p*_*u,cm:l}}function oo(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function ro(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function ao(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function lo(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!ao(t.a)||!ao(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],n=[Number(t.b[0]),Number(t.b[1])];Math.hypot(n[0]-s[0],n[1]-s[1])<.001||e.push({a:s,b:n})}return e}function co(t,e){const i=e[0],s=e[1],n=e[2]-i,o=e[3]-s,r=n*n+o*o;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*n+(t[1]-s)*o)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*n,s+a*o];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function ho(t,e){return co(t,e).q}function po(t,e,i,s,n){const o=co(t,e).q;let r=o,a=1/0;for(const t of i){const e=Math.hypot(o[0]-t[0],o[1]-t[1]);e<=n&&e0?s:1,g=Math.round(u/_)*_,m=Math.max(0,Math.min(p,g))/p;return[l+h*m,c+d*m]}function uo(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt?.id);let o=null;for(let e=0;et.seg)}(e,s);let o=null;for(const r of e||[]){if(!r?.id)continue;const e=Se(r);if(e)for(let a=0;ai)continue;let p=!1;for(const t of n)if(Bi([(l[0]+c[0])/2,(l[1]+c[1])/2],t)<2*s){p=!0;break}p||(!o||dt?.id),r=lo(e);if(r.length)return r.map(t=>ro(t,i));if(!n)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;too([t[0],t[1]],[t[2],t[3]],e))}function vo(t,e,i){let s=null;for(const n of e){const e=Bi(t,n);e<=i&&(!s||e.05)continue;const d=Rn(t,[e[0],e[1]],[e[2],e[3]],s,n);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-h[0],p[1]-h[1]);u0?l:o}(t,e,i,s,n,o);return On(t,[e[0],e[1]],[e[2],e[3]],r,s,n)}function yo(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],n=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const o of e)if(!(Bi(n,o)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Bi([o[0],o[1]],t)>i))for(const e of[[o[0],o[1]],[o[2],o[3]]])Bi(e,t)<=2*i&&s.push(e);return s}class wo{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=Li(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],n=s?this.now()-s.at:1/0;return n{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 n=0;for(const o of i){const i=t?.urls?.[o];"string"==typeof i&&i?(s[o]={url:i,at:e},this.retry.delete(o),n++):this.backOff(o)}n&&(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,n]of Object.entries(this.signed))e.has(t)&&i-n.at{const e=Number(t);return Number.isFinite(e)?e:null};function So(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=xo(e.x)&&null!=xo(e.y)?{x:xo(e.x),y:xo(e.y),a:xo(e.a??e.angle??e.theta)}:null;let s=null;const n=t.path?.points??t.path;if(Array.isArray(n)&&n.length){s=[];for(const t of n){const e=xo(Array.isArray(t)?t[0]:t?.x),i=xo(Array.isArray(t)?t[1]:t?.y);null!=e&&null!=i&&s.push([e,i])}s.length||(s=null)}const o=[],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=xo(e.cx??e.center?.x),n=xo(e.cy??e.center?.y);if(null==s||null==n){const t=xo(e.x0),i=xo(e.y0),o=xo(e.x1),r=xo(e.y1);null!=t&&null!=i&&null!=o&&null!=r&&(s=(t+o)/2,n=(i+r)/2)}if(null!=s&&null!=n||(s=xo(e.x),n=xo(e.y)),i&&null!=s&&null!=n){const r={id:t,name:i,cx:s,cy:n},a=xo(e.x0),l=xo(e.y0),c=xo(e.x1),h=xo(e.y1);null!=a&&null!=l&&null!=c&&null!=h&&(r.x0=Math.min(a,c),r.y0=Math.min(l,h),r.x1=Math.max(a,c),r.y1=Math.max(l,h)),o.push(r)}}const l=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||o.length||s?{pos:i,path:s,rooms:o,mapId:l}:null}function Mo(t){const e=t?.attributes;return!(!e||!e.vacuum_position&&!e.robot_position)}const Co=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function Do(t,e){const i=new Map(e.map(t=>[Co(t.name),t])),s=[],n=[];for(const e of t){const t=i.get(Co(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),n.push(e.name))}if(s.length<3)return null;const o=function(t){if(t.length<3)return null;let e=0,i=0,s=0,n=0,o=0,r=0,a=0,l=0,c=0,h=0,d=0,p=0;for(const[[u,_],[g,m]]of t){if(![u,_,g,m].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,n+=_*_,o+=_,r+=1,a+=u*g,l+=_*g,c+=g,h+=u*m,d+=_*m,p+=m}const u=[e,i,s,i,n,o,s,o,r],_=t=>{const[e,i,s,n,o,r,a,l,c]=u,h=e*(o*c-r*l)-i*(n*c-r*a)+s*(n*l-o*a);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-r*l)/h,(s*l-i*c)/h,(i*r-s*o)/h,(r*a-n*c)/h,(e*c-s*a)/h,(s*n-e*r)/h,(n*l-o*a)/h,(i*a-e*l)/h,(e*o-i*n)/h];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]]},g=_([a,l,c]),m=_([h,d,p]);if(!g||!m)return null;const f=[g[0],g[1],g[2],m[0],m[1],m[2]];return f.every(Number.isFinite)?f:null}(s);return o?{matrix:o,matched:n,residual:$o(o,s)}:null}function To(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 n=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[n,o]=s.pop(),[r,a]=t[n],[l,c]=t[o],h=l-r,d=c-a,p=Math.hypot(h,d)||1e-9;let u=0,_=-1;for(let e=n+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([n,_],[_,o]))}const n=[];for(let e=0;e600&&(n=n.filter((t,e)=>e%2==0||e===n.length-1)),n}function zo(t){return"cleaning"===t||"returning"===t||"on"===t}const Po={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function Ao(t){const[e,i]=Po[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 Ro(t,e,i,s){const[n,o]=ko(Ao(e),i,s),r=Ao({...t,ox:0,oy:0}),[a,l]=ko(r,i,s);return{...t,ox:n-a,oy:o-l}}function Eo(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}function No(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 Oo(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 Io(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)}function Fo(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),n=[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 n){const i=e.find(e=>Io(t,e.eid));if(i)return i.eid}for(const t of n)for(const e of ft){const i=t.find(t=>t.eid.split(".")[0]===e);if(i)return i.eid}for(const t of n)if(t.length)return t[0].eid}function Lo(t,e){const i=!0===e.marker?.is_light?[...e.marker?.controls||[],...e.entities]:e.entities.filter(t=>t.startsWith("light."));return i.find(e=>"on"===t.states[e]?.state)||null}function Ho(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const n=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===n){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const o=e.attributes?.linkquality??e.attributes?.lqi;if(null!=o){const t=parseFloat(o);isNaN(t)||i.push(t)}}return Ye(i)}function qo(t,e){for(const i of e){if(!Io(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 Uo(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 Wo(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 Bo(t,e){for(const i of e){if(!Wo(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 jo(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden)continue;let n=null;if("group"===s.platform)n=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if("Group"!==e?.model)continue;n=e.area_id||s.area_id||null}}if(!n)continue;const o=t.states[e];i.push({eid:e,name:s.name||o?.attributes?.friendly_name||e,area:n})}return i}function Vo(t,e,i,s,n){const o=mt(e,i,n);if(o!==gt)return o;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=_t[e];if(t)return t}return null}(r)??gt}function Go(t,e){t.marker=e,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 Ko(t){const{hass:e,areaToSpace:i,markers:s,settings:n,excluded:o,showAll:r,firstSpaceId:a,loc:l,iconRules:c}=t,h=!1!==n.group_lights,d=jo(e,h),p=new Set(d.map(t=>t.area)),u=No(e),_=new Set;for(const t of s){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||_.add(t.binding)}const g=(t,e)=>s.find(i=>i.binding===t+":"+e),m={},f=[];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(_.has("device:"+t.id))continue;const a=g("device",t.id);if(a&&a.hidden&&!n.filter_seeded)continue;const d=u[t.id]||[],v=Oo(e,t,d),b=!n.filter_seeded;if(b&&!r){if(o.has(v))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===v&&t.via_device_id)continue}const y=(t.name_by_user||t.name||l("device.unnamed")).trim(),w=y+"|"+s;let k=Vo(e,y,t.model,d,c);if(d.some(t=>t.startsWith("lock."))&&(k="mdi:lock"),b&&!r&&h&&"mdi:lightbulb"===k&&p.has(s))continue;m[w]=(m[w]||0)+1;const $=m[w]>1?y+" "+m[w]:y,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=Fo(e,d,k),"mdi:thermometer"!==k&&"mdi:air-filter"!==k||(x.temp=qo(e,d)),x.primary&&Wo(e,x.primary)&&(x.hum=Bo(e,d)),f.push(x)}for(const t of d)i[t.area]&&(_.has("entity:"+t.eid)||f.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.hidden&&!n.filter_seeded)continue;const[s,o]=t.binding.split(":");if("device"===s){const s=e.devices[o],n=t.area||s?.area_id||"",r=n&&i[n]||t.space||a,h=s&&u[s.id]||[];let d=s?Vo(e,s.name_by_user||s.name||"",s.model,h,c):"mdi:help-circle";h.some(t=>t.startsWith("lock."))&&(d="mdi:lock");const p={id:t.id,name:s?.name_by_user||s?.name||l("device.fallback"),model:s?.model||"",area:n,space:r,icon:d,entities:h,bindingKind:"device",bindingRef:o};p.primary=Fo(e,h,d),"mdi:thermometer"!==d&&"mdi:air-filter"!==d||(p.temp=qo(e,h)),p.primary&&Wo(e,p.primary)&&(p.hum=Bo(e,h)),p.primary&&Wo(e,p.primary)&&(p.hum=Bo(e,h)),Go(p,t),f.push(p)}else if("entity"===s){const s=e.entities[o],n=t.area||s?.area_id||s?.device_id&&e.devices[s.device_id]?.area_id||"",r=n&&i[n]||t.space||a,l=e.states[o],h=s?.name||l?.attributes?.friendly_name||o;let d=Vo(e,h,"",[o],c);o.startsWith("lock.")&&(d="mdi:lock");const p={id:t.id,name:h,model:"",area:n,space:r,icon:d,entities:[o],primary:o,bindingKind:"entity",bindingRef:o};"mdi:thermometer"!==d&&"mdi:air-filter"!==d||(p.temp=qo(e,[o])),Wo(e,o)&&(p.hum=Bo(e,[o])),Go(p,t),f.push(p)}else{const e=t.area||"",s=t.space||e&&i[e]||a,n={id:t.id,name:t.name||l("device.virtual"),model:t.model||"",area:e,space:s,icon:t.icon||"mdi:map-marker",entities:[],bindingKind:"virtual",virtual:!0};Go(n,t),f.push(n)}}return f}function Yo(t,e,i){let s=!1;for(const n of e)if(n.area===i&&!n.hidden)for(const e of n.entities)if(e.startsWith("light.")&&(s=!0,"on"===t.states[e]?.state))return"on";return s?"off":"none"}function Xo(t,e,i){if(!e)return null;const s=e.indexOf(":");if(s<0)return null;const n=e.slice(0,s),o=e.slice(s+1);if(!o)return null;if("entity"===n){const e=parseFloat(t.states[o]?.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===o).map(([t])=>t);return"temp"===i?qo(t,e):Bo(t,e)}return null}const Zo=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 Jo={"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.remove":"Remove","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":"Add a room: connect grid dots with lines until the outline closes","title.markup_merge":"Merge rooms: 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: click a virtual wall to make it solid; a shared solid wall merges the two rooms; an outer wall or inside a room deletes the room (with confirm).","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":"Add","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","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} · Esc/Ctrl+Z — undo a dot · close the outline by clicking the first one","markup.hint_start":"click a grid dot to start the outline","tip.lqi":"average zigbee signal:","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 badge","display.ripple":"Ripple only","display.icon_ripple":"Icon + ripple","marker.ripple_size":"Ripple 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":'Remove "{name}" from the plan?',"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.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 removed 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","editor.aspect_ratio":"Aspect ratio (e.g. 16:9 or auto)","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.color":"Color","decor.width":"Line width","decor.w_thin":"Thin","decor.w_mid":"Medium","decor.w_thick":"Thick","decor.fill":"Fill","decor.text_title":"Text label","decor.text_label":"Text","decor.text_hint":"A new line here is a new line on the plan; the block is centred. Drag a corner to resize it, the handle above it to turn it.","decor.text_hint_live":"{} is where the value lands. Without it the value is added at the end. A new line here is a new line on the plan.","decor.live_group":"Live value","decor.live_hint":"The label may show one entity's value — as Home Assistant reports it, with no rounding. A dead entity shows a dash.","decor.live_entity":"Entity","decor.live_entity_ph":"none — a plain label","decor.live_attr":"Attribute","decor.live_attr_ph":"the state","decor.live_unit":"Unit","decor.live_preview":"Preview","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag the picture to move it, pull a corner to resize it. Shift — off the grid.","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)","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 light sources","marker.controls_hint":"With tap action “Toggle”, a click flips all bound lights at once (any on → all off). The icon mirrors their state.","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":"Wall thickness","title.markup_wallthick":"Wall 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 for new room walls. Empty or zero — thin walls. 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","markup.openwall":"Open boundary","title.markup_openwall":"Open boundary — click a point on a shared wall, then a second point on the same wall (within the nearest corners) to make that stretch virtual. Click a dashed stretch to close it. 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.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 device from plan","marker.hide_tip":'The device is not drawn on the plan but still counts toward the room signal. To see it: the "Show hidden" button in the device editor.',"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":"Grid","gs.grid_hint":"Everything you draw or drag now lands on the grid. Hold Shift to place something between the nodes on purpose.","gs.align_all":"Align everything to the grid","gs.align_title":"Align everything to the grid","gs.align_none":"Everything is already on the grid — nothing to move.","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_warn":"This rewrites the plan and the device positions in one go. There is no undo: the only way back is to move things by hand. If some elements are deliberately between the nodes, cancel.","gs.align_run":"Align","gs.align_done":"Aligned to the grid: {n} elements moved","decor.furniture":"Furniture","furn.title":"Furniture library","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 Qo={en:Jo,ru:{"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.remove":"Убрать","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":"Добавить комнату: соединяйте точки сетки линиями до замкнутого контура","title.markup_merge":"Объединить комнаты: клик по одной, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить: виртуальная стена → обычная; общая глухая → слияние комнат; внешняя или клик внутрь → удалить комнату (с подтверждением)","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":"Проём","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} · Esc/Ctrl+Z — убрать точку · замкните контур кликом по первой","markup.hint_start":"кликните точку сетки, чтобы начать контур","tip.lqi":"средний сигнал zigbee:","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.ripple":"Только пульсация","display.icon_ripple":"Значок + пульсация","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.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.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":"Путь дашборда (куда вести)","editor.aspect_ratio":"Соотношение сторон (напр. 16:9 или auto)","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.color":"Цвет","decor.width":"Толщина линии","decor.w_thin":"Тонкая","decor.w_mid":"Средняя","decor.w_thick":"Толстая","decor.fill":"Залить","decor.text_title":"Надпись","decor.text_label":"Текст","decor.text_hint":"Перевод строки здесь — перевод строки на плане; блок выравнивается по центру. Тяните угол — меняет размер, ручка сверху — поворот.","decor.text_hint_live":"{} — место, куда встанет значение. Без него значение добавится в конец. Перевод строки здесь — перевод строки на плане.","decor.live_group":"Живое значение","decor.live_hint":"Надпись может показывать значение одной сущности — так, как его отдаёт Home Assistant, без округления. Мёртвая сущность — прочерк.","decor.live_entity":"Сущность","decor.live_entity_ph":"нет — обычная надпись","decor.live_attr":"Атрибут","decor.live_attr_ph":"состояние","decor.live_unit":"Единица","decor.live_preview":"Предпросмотр","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — двигается, тяните угол — меняет размер. Shift — мимо сетки.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","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":"Толщина стен","title.markup_wallthick":"Толщина стен — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина стен новой комнаты. Пустое или ноль — без толщины. Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","markup.openwall":"Открытая граница","title.markup_openwall":"Открытая граница — клик по точке на общей стене, затем второй клик на той же стене (не дальше ближайших углов) открывает отрезок. Клик по пунктиру закрывает. Курсор — крестик.","toast.openwall_pick":"Кликните по стене, разделяющей две комнаты","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.openwall_opened_span":"Виртуальный отрезок открыт","toast.openwall_closed_span":"Виртуальный отрезок закрыт","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":"Устройство не отображается на плане, но участвует в расчёте сигнала комнаты. Показать: кнопка «Показать скрытые» в редакторе устройств.","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":"Всё, что вы рисуете и перетаскиваете, ложится строго на сетку. Shift — временно поставить элемент между узлами.","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_warn":"Действие разом перезапишет план и позиции устройств. Отмены нет: вернуть можно только руками. Если какие-то элементы стоят между узлами намеренно — отмените.","gs.align_run":"Выровнять","gs.align_done":"Выровнено по сетке: сдвинуто элементов — {n}","decor.furniture":"Мебель","furn.title":"Библиотека мебели","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 tr(t,e){if(e&&e in Qo)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function er(t,e,i){return _i(Qo[t][e]??Jo[e]??e,i)}class ir 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 tr(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:er(e,"editor.lang_auto")},{value:"en",label:er(e,"editor.lang_en")},{value:"ru",label:er(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 V;this._loadSpaces();const t=this._lang,e={title:er(t,"editor.title"),default_floor:er(t,"editor.default_floor"),language:er(t,"editor.language"),icon_size:er(t,"editor.icon_size"),show_temperature:er(t,"editor.show_temperature"),live_states:er(t,"editor.live_states"),show_signal:er(t,"editor.show_signal"),kiosk:er(t,"editor.kiosk"),cycle:er(t,"editor.cycle")};return W`{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,g=_?_.emptyScript:"",m=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?g: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}},b=(t,e)=>!a(t,e),y={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:b};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=y){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??y}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=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??b)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,m?.({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,T=`<${D}>`,z=document,P=()=>z.createComment(""),A=t=>null===t||"object"!=typeof t&&"function"!=typeof t,R=Array.isArray,E="[ \t\n\f\r]",N=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,O=/-->/g,I=/>/g,F=RegExp(`>|${E}(?:([^\\s"'>=/]+)(${E}*=${E}*(?:[^ \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"),V=Symbol.for("lit-nothing"),G=new WeakMap,K=z.createTreeWalker(z,129);function Y(t,e){if(!R(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const X=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",r=N;for(let e=0;e"===l[0]?(r=n??N,c=-1):void 0===l[1]?c=-2:(c=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===O||r===I?r=N:(r=F,n=void 0);const d=r===F&&t[e+1].startsWith("/>")?" ":"";o+=r===N?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,o+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const r=t.length-1,a=this.parts,[l,c]=X(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iR(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!==V&&A(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=G.get(t.strings);return void 0===e&&G.set(t.strings,e=new Z(t)),e}k(t){R(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(P()),this.O(P()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=V}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=J(this,t,e,0),o=!A(t)||t!==this._$AH&&t!==j,o&&(this._$AH=t);else{const s=t;let r,a;for(t=n[0],r=0;r{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(P(),t),t,void 0,i??{})}return n._$AI(t),n})(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 ct=at.litElementPolyfillSupport;ct?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ht=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),dt=[{pattern:"протечк|leak|water sensor",icon:"mdi:water-alert"},{pattern:"клапан|valve",icon:"mdi:pipe-valve"},{pattern:"дым|smoke",icon:"mdi:smoke-detector"},{pattern:"термоголов|trv|radiator",icon:"mdi:radiator"},{pattern:"чайник|kettle|термопот",icon:"mdi:kettle"},{pattern:"сауна|sauna|harvia|парная|парилк",icon:"mdi:hot-tub"},{pattern:"температ|temperature|climate sensor",icon:"mdi:thermometer"},{pattern:"qingping|air monitor|молекул|air quality",icon:"mdi:air-filter"},{pattern:"штор|curtain|blind|shade",icon:"mdi:roller-shade"},{pattern:"розетк|plug|socket|outlet",icon:"mdi:power-socket-de"},{pattern:"выключат|switch",icon:"mdi:light-switch"},{pattern:"лампа|лампочк|bulb|gx53|светильник|rgb|lamp|light strip",icon:"mdi:lightbulb"},{pattern:"камер|camera",icon:"mdi:cctv"},{pattern:"замок|ttlock|lock|sn609|sn9161",icon:"mdi:lock"},{pattern:"ворота|garage|gate",icon:"mdi:garage-variant"},{pattern:"калитк|door|открыт|contact",icon:"mdi:door"},{pattern:"счётчик|счетчик|kws|meter",icon:"mdi:meter-electric"},{pattern:"вводный автомат|breaker|wifimcbn",icon:"mdi:electric-switch"},{pattern:"myheat|котёл|котел|boiler|отоплен|heating",icon:"mdi:water-boiler"},{pattern:"холодильник|fridge",icon:"mdi:fridge"},{pattern:"стиральн|washer|washing",icon:"mdi:washing-machine"},{pattern:"сушилк|dryer",icon:"mdi:tumble-dryer"},{pattern:"пылесос|vacuum|dreame|roborock",icon:"mdi:robot-vacuum"},{pattern:"soundbar",icon:"mdi:soundbar"},{pattern:"колонк|станц|speaker|яндекс|yandex|алиса|alice",icon:"mdi:speaker"},{pattern:"tv|телевизор|hyundaitv|mitv|television",icon:"mdi:television"},{pattern:"keenetic|роутер|router|mesh|access point",icon:"mdi:router-wireless"},{pattern:"ибп|ups|kirpich",icon:"mdi:battery-charging-high"},{pattern:"slzb|координат|zigbee|coordinator",icon:"mdi:zigbee"},{pattern:"motion|движен|presence|присутств",icon:"mdi:motion-sensor"},{pattern:"humidity|влажн",icon:"mdi:water-percent"}];function pt(t){const e=[];for(const i of t)if(i&&"string"==typeof i.pattern&&i.icon)try{e.push({re:new RegExp(i.pattern,"i"),icon:i.icon})}catch{}return e}const ut=pt(dt),_t={temperature:"mdi:thermometer",humidity:"mdi:water-percent",motion:"mdi:motion-sensor",occupancy:"mdi:motion-sensor",door:"mdi:door",window:"mdi:window-closed",garage_door:"mdi:garage-variant",smoke:"mdi:smoke-detector",moisture:"mdi:water-alert",gas:"mdi:gas-cylinder",power:"mdi:meter-electric",energy:"mdi:meter-electric",illuminance:"mdi:brightness-5",co2:"mdi:molecule-co2",pm25:"mdi:air-filter",battery:"mdi:battery"},gt="mdi:chip";function mt(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??ut)if(t.test(s))return e;return gt}const ft=["light","switch","cover","valve","lock","climate","fan","media_player","camera","vacuum","humidifier","water_heater","alarm_control_panel","sensor","binary_sensor","event","button","number","select","update"];var vt=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,bt=Math.ceil,yt=Math.floor,wt="[BigNumber Error] ",kt=wt+"Number primitive has more than 15 significant digits: ",$t=1e14,xt=14,St=9007199254740991,Mt=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],Ct=1e7,Dt=1e9;function Tt(t){var e=0|t;return t>0||t===e?e:e-1}function zt(t){for(var e,i,s=1,n=t.length,o=t[0]+"";sc^i?1:-1;for(a=(l=n.length)<(c=o.length)?l:c,r=0;ro[r]^i?1:-1;return l==c?0:l>c^i?1:-1}function At(t,e,i,s){if(ti||t!==yt(t))throw Error(wt+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Rt(t){var e=t.c.length-1;return Tt(t.e/xt)==e&&t.c[e]%2!=0}function Et(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Nt(t,e,i){var s,n;if(e<0){for(n=i+".";++e;n+=i);t=n+t}else if(++e>(s=t.length)){for(n=i,e-=s;--e;n+=i);t+=n}else eb?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>b?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!vt.test(d=String(t)))return n(p,d,c);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(At(e,2,x.length,"Base"),10==e&&S)return z(p=new M(t),_+p.e+1,g);if(d=String(t),c="number"==typeof t){if(0*t!=0)return n(p,d,c,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(kt+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,h=d.length;la){a=h;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return n(p,String(t),c,e)}c=!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(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(t>St||t!==yt(t)))throw Error(kt+p.s*t);if((a=a-l-1)>b)p.c=p.e=null;else if(a=f)?Et(l,r):Nt(l,r,"0");else if(o=(t=z(new M(t),e,i)).e,a=(l=zt(t.c)).length,1==s||2==s&&(e<=o||o<=m)){for(;ar),l=Nt(l,o,"0"),o+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=o-a)>0)for(o+1==a&&(l+=".");e--;l+="0");return t.s<0&&n?"-"+l:l}function D(t,e){for(var i,s,n=1,o=new M(t[0]);n=10;n/=10,s++);return(i=s+i*xt-1)>b?t.c=t.e=null:i=10;a/=10,n++);if((o=e-n)<0)o+=xt,r=e,l=d[c=0],h=yt(l/p[n-r-1]%10);else if((c=bt((o+1)/xt))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,n=1,r=(o%=xt)-xt+1}else{for(l=a=d[c],n=1;a>=10;a/=10,n++);h=(r=(o%=xt)-xt+n)<0?0:yt(l/p[n-r-1]%10)}if(s=s||e<0||null!=d[c+1]||(r<0?l:l%p[n-r-1]),s=i<4?(h||s)&&(0==i||i==(t.s<0?3:2)):h>5||5==h&&(4==i||s||6==i&&(o>0?r>0?l/p[n-r]:0:d[c-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[(xt-e%xt)%xt],t.e=-e||0):d[0]=t.e=0,t;if(0==o?(d.length=c,a=1,c--):(d.length=c+1,a=p[xt-o],d[c]=r>0?yt(l/p[n-r]%p[r])*a:0),s)for(;;){if(0==c){for(o=1,r=d[0];r>=10;r/=10,o++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);o!=a&&(t.e++,d[0]==$t&&(d[0]=1));break}if(d[c]+=a,d[c]!=$t)break;d[c--]=0,a=1}for(o=d.length;0===d[--o];d.pop());}t.e>b?t.c=t.e=null:t.e=f?Et(e,i):Nt(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(wt+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(At(i=t[e],0,Dt,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(At(i=t[e],0,8,e),g=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(At(i[0],-Dt,0,e),At(i[1],0,Dt,e),m=i[0],f=i[1]):(At(i,-Dt,Dt,e),m=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)At(i[0],-Dt,-1,e),At(i[1],1,Dt,e),v=i[0],b=i[1];else{if(At(i,-Dt,Dt,e),!i)throw Error(wt+e+" cannot be zero: "+i);v=-(b=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(wt+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw y=!i,Error(wt+"crypto unavailable");y=i}else y=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(At(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(At(i=t[e],0,Dt,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(wt+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(wt+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:g,EXPONENTIAL_AT:[m,f],RANGE:[v,b],CRYPTO:y,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,n=t.e,o=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===o||-1===o)&&n>=-Dt&&n<=Dt&&n===yt(n)){if(0===s[0]){if(0===n&&1===s.length)return!0;break t}if((e=(n+1)%xt)<1&&(e+=xt),String(s[0]).length==e){for(e=0;e=$t||i!==yt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===n&&(null===o||1===o||-1===o))return!0;throw Error(wt+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(o=9007199254740992,r=Math.random()*o&2097151?function(){return yt(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,n,o,a=0,l=[],c=new M(u);if(null==t?t=_:At(t,0,Dt),n=bt(t/xt),y)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(n*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(o%1e14),a+=2);a=n/2}else{if(!crypto.randomBytes)throw y=!1,Error(wt+"crypto unavailable");for(e=crypto.randomBytes(n*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(o%1e14),a+=7);a=n/7}if(!y)for(;a=10;o/=10,a++);ai-1&&(null==r[n+1]&&(r[n+1]=0),r[n+1]+=r[n]/i|0,r[n]%=i)}return r.reverse()}return function(s,n,o,r,a){var l,c,h,d,p,u,m,f,v=s.indexOf("."),b=_,y=g;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(n)).pow(s.length-v),k=d,f.c=e(Nt(zt(u.c),u.e,"0"),10,o,t),f.e=f.c.length),h=d=(m=e(s,n,o,a?(l=x,t):(l=t,x))).length;0==m[--d];m.pop());if(!m[0])return l.charAt(0);if(v<0?--h:(u.c=m,u.e=h,u.s=r,m=(u=i(u,f,b,y,o)).c,p=u.r,h=u.e),v=m[c=h+b+1],d=o/2,p=p||c<0||null!=m[c+1],p=y<4?(null!=v||p)&&(0==y||y==(u.s<0?3:2)):v>d||v==d&&(4==y||p||6==y&&1&m[c-1]||y==(u.s<0?8:7)),c<1||!m[0])s=p?Nt(l.charAt(1),-b,l.charAt(0)):l.charAt(0);else{if(m.length=c,p)for(--o;++m[--c]>o;)m[c]=0,c||(++h,m=[1].concat(m));for(d=m.length;!m[--d];);for(v=0,s="";v<=d;s+=l.charAt(m[v++]));s=Nt(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,n,o,r,a=0,l=t.length,c=e%Ct,h=e/Ct|0;for(t=t.slice();l--;)a=((n=c*(o=t[l]%Ct)+(s=h*o+(r=t[l]/Ct|0)*c)%Ct*Ct+a)/i|0)+(s/Ct|0)+h*r,t[l]=n%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var n,o;if(i!=s)o=i>s?1:-1;else for(n=o=0;ne[n]?1:-1;break}return o}function i(t,e,i,s){for(var n=0;i--;)t[i]-=n,n=t[i]1;t.splice(0,1));}return function(s,n,o,r,a){var l,c,h,d,p,u,_,g,m,f,v,b,y,w,k,$,x,S=s.s==n.s?1:-1,C=s.c,D=n.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&n.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(m=(g=new M(S)).c=[],S=o+(c=s.e-n.e)+1,a||(a=$t,c=Tt(s.e/xt)-Tt(n.e/xt),S=S/xt|0),h=0;D[h]==(C[h]||0);h++);if(D[h]>(C[h]||0)&&c--,S<0)m.push(1),d=!0;else{for(w=C.length,$=D.length,h=0,S+=2,(p=yt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),$=D.length,w=C.length),y=$,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(b=f[0],$!=v&&(b=b*a+(f[1]||0)),(p=yt(b/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,h++);z(g,o+(g.e=h+c*xt-1)+1,r,d)}else g.e=c,g.r=+d;return g}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,n=function(t,e,i,s){var n,o=i?e:e.replace(d,"");if(h.test(o))t.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(a,function(t,e,i){return n="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=n?t:e}),s&&(n=s,o=o.replace(l,"$1").replace(c,"0.$1")),e!=o))return new M(o,n);if(M.DEBUG)throw Error(wt+"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 Pt(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,n,o=this;if(null!=t)return At(t,0,Dt),null==e?e=g:At(e,0,8),z(new M(o),t+o.e+1,e);if(!(i=o.c))return null;if(s=((n=i.length-1)-Tt(this.e/xt))*xt,n=i[n])for(;n%10==0;n/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,g)},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,n,o,r,a,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(wt+"Exponent not an integer: "+P(t));if(null!=e&&(e=new M(e)),r=t.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!t.c||!t.c[0])return c=new M(Math.pow(+P(h),r?t.s*(2-Rt(t)):+P(t))),e?c.mod(e):c;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&h.isInteger()&&e.isInteger())&&(h=h.mod(e))}else{if(t.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||r&&h.c[1]>=24e7:h.c[0]<8e13||r&&h.c[0]<=9999975e7)))return o=h.s<0&&Rt(t)?-0:0,h.e>-1&&(o=1/o),new M(a?1/o:o);k&&(o=bt(k/xt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Rt(t)):l=(n=Math.abs(+P(t)))%2,c=new M(u);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):s&&(c=c.mod(e))}if(n){if(0===(n=yt(n/2)))break;l=n%2}else if(z(t=t.times(i),t.e+1,1),t.e>14)l=Rt(t);else{if(0===(n=+P(t)))break;l=n%2}h=h.times(h),o?h.c&&h.c.length>o&&(h.c.length=o):s&&(h=h.mod(e))}return s?c:(a&&(c=u.div(c)),e?c.mod(e):o?z(c,k,g,void 0):c)},p.integerValue=function(t){var e=new M(this);return null==t?t=g:At(t,0,8),z(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===Pt(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return Pt(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=Pt(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&Tt(this.e/xt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return Pt(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=Pt(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,n,o,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/xt,c=t.e/xt,h=r.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?r:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?r:3==g?-0:0)}if(l=Tt(l),c=Tt(c),h=h.slice(),a=l-c){for((o=a<0)?(a=-a,n=h):(c=l,n=d),n.reverse(),e=a;e--;n.push(0));n.reverse()}else for(s=(o=(a=h.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;h[i++]=0);for(e=$t-1;s>a;){if(h[--s]=0;){for(i=0,p=b[n]%m,u=b[n]/m|0,o=n+(r=l);o>n;)i=((c=p*(c=v[--r]%m)+(a=u*c+(h=v[r]/m|0)*p)%m*m+_[o]+i)/g|0)+(a/m|0)+u*h,_[o--]=c%g;_[o]=i}return i?++s:_.splice(0,1),T(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,n=s.s;if(e=(t=new M(t,e)).s,!n||!e)return new M(NaN);if(n!=e)return t.s=-e,s.minus(t);var o=s.e/xt,r=t.e/xt,a=s.c,l=t.c;if(!o||!r){if(!a||!l)return new M(n/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*n)}if(o=Tt(o),r=Tt(r),a=a.slice(),n=o-r){for(n>0?(r=o,i=l):(n=-n,i=a),i.reverse();n--;i.push(0));i.reverse()}for((n=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=n),n=0;e;)n=(a[--e]=a[e]+l[e]+n)/$t|0,a[e]=$t===a[e]?0:a[e]%$t;return n&&(a=[n].concat(a),++r),T(t,a,r)},p.precision=p.sd=function(t,e){var i,s,n,o=this;if(null!=t&&t!==!!t)return At(t,1,Dt),null==e?e=g:At(e,0,8),z(new M(o),t,e);if(!(i=o.c))return null;if(s=(n=i.length-1)*xt+1,n=i[n]){for(;n%10==0;n/=10,s--);for(n=i[0];n>=10;n/=10,s++);}return t&&o.e+1>s&&(s=o.e+1),s},p.shiftedBy=function(t){return At(t,-9007199254740991,St),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,n,o,r=this,a=r.c,l=r.s,c=r.e,h=_+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(+P(r)))||l==1/0?(((e=zt(a)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=Tt((c+1)/2)-(c<0||c%2),s=new M(e=l==1/0?"5e"+c:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+c)):s=new M(l+""),s.c[0])for((l=(c=s.e)+h)<3&&(l=0);;)if(o=s,s=d.times(o.plus(i(r,o,h,1))),zt(o.c).slice(0,l)===(e=zt(s.c)).slice(0,l)){if(s.e0&&_>0){for(o=_%a||a,h=u.substr(0,o);o<_;o+=a)h+=c+u.substr(o,a);l>0&&(h+=c+u.slice(o)),p&&(h="-"+h)}s=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,n,o,r,a,l,c,h,d,p,_,m=this,f=m.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(wt+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+P(l));if(!f)return new M(m);for(e=new M(u),h=s=new M(u),n=c=new M(u),_=zt(f),r=e.e=_.length-m.e-1,e.c[0]=Mt[(a=r%xt)<0?xt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:h:l,a=b,b=1/0,l=new M(_),c.c[0]=0;d=i(l,e,0,1),1!=(o=s.plus(d.times(n))).comparedTo(t);)s=n,n=o,h=c.plus(d.times(o=h)),c=o,e=l.minus(d.times(o=e)),l=o;return o=i(t.minus(s),n,0,1),c=c.plus(o.times(h)),s=s.plus(o.times(n)),c.s=h.s=m.s,p=i(h,n,r*=2,g).minus(m).abs().comparedTo(i(c,s,r,g).minus(m).abs())<1?[h,n]:[c,s],b=a,p},p.toNumber=function(){return+P(this)},p.toPrecision=function(t,e){return null!=t&&At(t,1,Dt),C(this,t,e,2)},p.toString=function(t){var e,i=this,n=i.s,o=i.e;return null===o?n?(e="Infinity",n<0&&(e="-"+e)):e="NaN":(null==t?e=o<=m||o>=f?Et(zt(i.c),o):Nt(zt(i.c),o,"0"):10===t&&S?e=Nt(zt((i=z(new M(i),_+o+1,g)).c),i.e,"0"):(At(t,2,x.length,"Base"),e=s(Nt(zt(i.c),o,"0"),10,t,n,!0)),n<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return P(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),It=class{key;left=null;right=null;constructor(t){this.key=t}},Ft=class extends It{constructor(t){super(t)}},Lt=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,n=null,o=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==n?o=r:n.right=r,n=r,r=e}}return null!=n&&(n.right=r.left,r.left=o),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)}}},qt=class t extends Lt{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 Ft(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ft(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 Ft(t.key);return function t(e,i){let s,n;do{if(s=e.left,n=e.right,null!=s){const e=new Ft(s.key);i.left=e,t(s,e)}if(null!=n){const t=new Ft(n.key);i.right=t,e=n,i=t}}while(null!=n)}(t,e),e}toSet(){return this.clone()}entries(){return new Wt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Ut(this.wrap())}[Symbol.toStringTag]="[object Set]"},Ht=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}},Ut=class extends Ht{getValue(t){return t.key}},Wt=class extends Ht{getValue(t){return[t.key,t.key]}},Bt=t=>()=>t,jt=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Bt(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Vt(t){const e=t?(e,i,s,n,o)=>e.exponentiatedBy(2).isLessThanOrEqualTo(n.minus(i).exponentiatedBy(2).plus(o.minus(s).exponentiatedBy(2)).times(t)):Bt(!1);return(t,i,s)=>{const n=t.x,o=t.y,r=s.x,a=s.y,l=o.minus(a).times(i.x.minus(r)).minus(n.minus(r).times(i.y.minus(a)));return e(l,n,o,r,a)?0:l.comparedTo(0)}}var Gt=t=>t,Kt=t=>{if(t){const e=new qt(jt(t)),i=new qt(jt(t)),s=(t,e)=>e.addAndReturn(t),n=t=>({x:s(t.x,e),y:s(t.y,i)});return n({x:new Ot(0),y:new Ot(0)}),n}return Gt},Yt=t=>({set:t=>{Xt=Yt(t)},reset:()=>Yt(t),compare:jt(t),snap:Kt(t),orient:Vt(t)}),Xt=Yt(),Zt=(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),Jt=(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}}},Qt=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),te=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ee=t=>te(t,t).sqrt(),ie=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return Qt(n,s).div(ee(n)).div(ee(s))},se=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return te(n,s).div(ee(n)).div(ee(s))},ne=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},oe=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},re=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:_e.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:ie(this.point,t.point,s.point),cosine:se(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:n,cosine:o}=e.get(t),{sine:r,cosine:a}=e.get(s);return n.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?o.isLessThan(a)?1:o.isGreaterThan(a)?-1:0:n.isLessThan(0)&&r.isLessThan(0)?o.isLessThan(a)?-1:o.isGreaterThan(a)?1:0:r.isLessThan(n)?-1:r.isGreaterThan(n)?1:0}}},ae=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,n=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}}},le=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&&n){const t=s.getIntersection(n);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(c))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(c))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(n.isLessThan(o)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(n.isGreaterThan(o)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!n.eq(o)){const t=l.minus(r),e=n.minus(i),h=c.minus(a),d=o.minus(s);if(t.isGreaterThan(e)&&h.isLessThan(d))return 1;if(t.isLessThan(e)&&h.isGreaterThan(d))return-1}return n.isGreaterThan(o)?1:n.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++ue,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 n,o,r;const a=re.comparePoints(e,i);if(a<0)n=e,o=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);n=i,o=e,r=-1}const l=new re(n,!0),c=new re(o,!1);return new t(l,c,[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 Xt.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=Jt(e,i);if(null===s)return null;const n=this.leftSE.point,o=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=Zt(e,r)&&0===this.comparePoint(r),c=Zt(i,n)&&0===t.comparePoint(n),h=Zt(e,a)&&0===this.comparePoint(a),d=Zt(i,o)&&0===t.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?a:null;if(c)return h&&n.x.eq(a.x)&&n.y.eq(a.y)?null:n;if(l)return d&&o.x.eq(r.x)&&o.y.eq(r.y)?null:r;if(d&&h)return null;if(d)return o;if(h)return a;const p=((t,e,i,s)=>{if(e.x.isZero())return oe(i,s,t.x);if(s.x.isZero())return oe(t,e,i.x);if(e.y.isZero())return ne(i,s,t.y);if(s.y.isZero())return ne(t,e,i.y);const n=Qt(e,s);if(n.isZero())return null;const o={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=Qt(o,e).div(n),a=Qt(o,s).div(n),l=t.x.plus(a.times(e.x)),c=i.x.plus(r.times(s.x)),h=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(n,this.vector(),r,t.vector());return null===p?null:Zt(s,p)?Xt.snap(p):null}split(e){const i=[],s=void 0!==e.events,n=new re(e,!0),o=new re(e,!1),r=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(n);const a=new t(n,r,this.rings.slice(),this.windings.slice());return re.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),re.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(n.checkForConsuming(),o.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}},ge=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=Xt.snap({x:new Ot(t[0][0]),y:new Ot(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let n=s;for(let e=1,i=t.length;epe.run("union",t,e),be=(t,...e)=>pe.run("intersection",t,e),ye=(t,...e)=>pe.run("difference",t,e);function we(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function ke(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 $e(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 xe(t,e,i=1){const s=t[0].toFixed(i),n=t[1].toFixed(i),o=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 Me(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 Ce(t){const e=[],i=new Set;for(const s of t||[]){const t=Se(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),o=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(o,Math.min(i-o,a));const l=a/i;n={x:e+l*c,y:r+l*h,angle:t}}else n={x:u[0],y:u[1],angle:t}}}return n}function Te(t,e,i,s,n,o=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,c=o;for(const e of Ce(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])>o||s(i[1])>o)continue;const n=(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],h=Math.min(n,r),d=Math.max(n,r),p=h>0?h:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],m=(h+d)/2;return{wallA:g(h),wallB:g(d),sideA:u,sideB:_,midA:g((h-p)/2),midB:g((p+d)/2),wallCenter:g(m),centered:Math.abs(m)<=n}}function ze(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}function Pe(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}function Re(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;let r=o?((t[0]-e[0])*s+(t[1]-e[1])*n)/o:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*n))}function Ee(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let n=0;nn&&r<-n||o<-n&&r>n)&&(a>n&&l<-n||a<-n&&l>n)}function Le(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1;let c=0,h=0,d=0;for(let e=0;e1e-9?[h/(3*c),d/(3*c)]:[(n+o)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!Ae([e,i],t))return-1/0;let s=1/0;for(let n=0;ng&&(g=c,_=[s,l])}if(_){const[t,i]=_,s=(o-n)/e,l=(a-r)/e;for(let e=-4;e<=4;e++)for(let n=-4;n<=4;n++){const o=t+s*e/4,r=i+l*n/4,a=u(o,r);a>g&&(g=a,_=[o,r])}}return _||qe(t)||t[0]}function qe(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(Oe(s,t,e))return s;for(let s=0;s[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ve(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=ve(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}function Ge(t,e,i){for(let s=0;s1&&Pe(i[0],i[i.length-1],e)&&i.pop(),i}function Ye(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function Xe(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 Ze(t,e,i,s){if(t.length<2)return;const n=e.x+s,o=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}function ai(t){return"opening"===t||"closing"===t}const li=["automation","script","scene"];function ci(t,e,i){const s=String(e??"").trim();if(!s)return null;const n=t?.states?.[s];if(!n)return null;const o=String(i??"").trim(),r=t=>t.slice(0,60);if(o){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)}(n.attributes?.[o]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,n,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=n.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function hi(t,e,i){const s=String(e??"").trim(),n=String(i??"").trim()||s;if(!n)return t.text;const o=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`${o} ${n}`}function di(t,e,i){const s=t??"";if(!(e?.entity||"").trim())return s;const n=function(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],n=s?.state;if(!s||null==n||""===n||"unavailable"===n||"unknown"===n)return"—";const o=(e?.attr||"").trim(),r=ci(t,i,o||null);return null===r?"—":hi(r,o?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}(i,e),o=s.indexOf("{}");return o>=0?s.slice(0,o)+n+s.slice(o+2):s?`${s} ${n}`:n}function pi(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 ui(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}function _i(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 gi="#55606c",mi=.55;function fi(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:gi,opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):mi,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 vi(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 bi={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}},yi=/^#[0-9a-f]{6}$/i;function wi(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(bi)){const s=bi[t],n=i[t];e[t]={c:n&&"string"==typeof n.c&&yi.test(n.c)?n.c:s.c,a:n&&"number"==typeof n.a?Math.min(1,Math.max(0,n.a)):s.a}}return e}function ki(t,e,i){const s=Math.min(1,Math.max(0,i)),n=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),o=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=n.map((t,e)=>Math.round(t+(o[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function $i(t,e,i,s,n,o,r){if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:ki(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(n,o),e=Math.max(n,o);return se?r.temp_hot:r.temp_ok}return null}const xi={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"]},Si=[["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 Mi(t){for(const e of[...Object.values(xi),...Si])if(t===e[0]||t===e[1])return e;return null}function Ci(t,e,i,s,n){if(!s||"unavailable"===s||"unknown"===s)return t;if(n){const i="cover"===e?Mi(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=xi[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const n=Mi(t);return n?"closed"===s?n[0]:n[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 Di(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}function Ti(t,e){if(!t||"on"!==t.state)return null;const i=t.attributes||{},s=Number(i.brightness),n=Number.isFinite(s)&&s>0?Math.max(.15,Math.min(1,s/255)):1,o=i.rgb_color;if(Array.isArray(o)&&o.length>=3&&o.every(t=>Number.isFinite(t)))return{c:`rgb(${o[0]}, ${o[1]}, ${o[2]})`,bri:n};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),n=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,o=t=>Math.round(Math.min(255,Math.max(0,t)));return[o(i),o(s),o(n)]}(r);return{c:`rgb(${t}, ${e}, ${i})`,bri:n}}return{c:e,bri:n}}function zi(t,e,i,s,n=170){const o=Math.hypot(e[0]-t[0],e[1]-t[1]),r=Math.hypot(i[0]-t[0],i[1]-t[1]);if(o<1e-6||r<1e-6||Math.min(o,r)>=s)return null;let a=Math.atan2(e[1]-t[1],e[0]-t[0]),l=Math.atan2(i[1]-t[1],i[0]-t[0])-a;for(;l>Math.PI;)l-=2*Math.PI;for(;l<-Math.PI;)l+=2*Math.PI;const c=n*Math.PI/180;if(Math.abs(l)>c){const t=a+l/2;l=c*Math.sign(l),a=t-l/2}const h=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=a+l*e/8;h.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return h}function Pi(t,e,i,s,n){const o=e*Math.PI/180,r=[-Math.sin(o),Math.cos(o)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*n*a,t[1]+r[1]*n*a];return s.some(t=>Oe(l,t,1e-9))}function Ai(t){return t.startsWith("light.")||t.startsWith("switch.")}function Ri(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let n=0;np||l>p)continue;const u=(n[0]-o[0])*h+(n[1]-o[1])*d,_=(r[0]-o[0])*h+(r[1]-o[1])*d,g=Math.max(0,Math.min(u,_)),m=Math.min(c,Math.max(u,_));m-g>i&&s.push([o[0]+h*g,o[1]+d*g,o[0]+h*m,o[1]+d*m])}}return s}function Ei(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let n=!0;for(;n;){n=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const o of e)if(o.id&&i.has(o.id)&&s(t,o)){i.add(t.id),n=!0;break}}return i}function Ni(t,e,i=1e-6){const s=[];for(const n of t){const t=[n[0],n[1]],o=[n[2],n[3]],r=o[0]-t[0],a=o[1]-t[1],l=Math.hypot(r,a);if(lo||n>o)continue;const r=(s[0]-t[0])*c+(s[1]-t[1])*h,a=(s[2]-t[0])*c+(s[3]-t[1])*h,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],o[0],o[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,n]of d)e-p>i&&s.push([t[0]+c*p,t[1]+h*p,t[0]+c*e,t[1]+h*e]),p=Math.max(p,n);l-p>i&&s.push([t[0]+c*p,t[1]+h*p,o[0],o[1]])}return s}const Oi=864e5,Ii=576e5;function Fi(t){const e=new Set,i=t=>{if("string"!=typeof t||!t)return;const i=Li(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 Li(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 qi(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i?i:t}function Hi(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Ui(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Wi(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Bi(t,e){const i=e[2]-e[0],s=e[3]-e[1],n=i*i+s*s;if(!n)return Math.hypot(t[0]-e[0],t[1]-e[1]);let o=((t[0]-e[0])*i+(t[1]-e[1])*s)/n;return o=Math.max(0,Math.min(1,o)),Math.hypot(t[0]-(e[0]+o*i),t[1]-(e[1]+o*s))}const ji=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Vi=(t,e)=>[t[0]-e[0],t[1]-e[1]],Gi=(t,e)=>[t[0]+e[0],t[1]+e[1]],Ki=(t,e)=>t[0]*e[0]+t[1]*e[1],Yi=t=>Math.hypot(t[0],t[1]);function Xi(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function Qi(t,e,i,s){const n=s||Ji(t,e),o=(e+1)%t.length;return t.map((t,s)=>s===e||s===o?[t[0]+n[0]*i,t[1]+n[1]*i]:[...t])}function ts(t,e,i,s){const n=[],o=Vi(i,e),r=Yi(o);if(rs||h>s)continue;const d=Ki(Vi(o,e),a),p=Ki(Vi(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&n.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return n}function es(t,e,i,s,n){const o=ts(t,e,i,n);if(!o.length)return null;const r=t=>o.some(([e,i])=>Zi(t,e,i)<=n),a=t.length,l=[];for(let o=0;on||f>n)continue;const v=Ki(Vi(e,c),u),b=Ki(Vi(i,c),u),y=Math.max(0,Math.min(v,b)),w=Math.min(p,Math.max(v,b));if(!(w-y<=n)){if(y>n&&yn&&wYi(Vi(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const n=i[(s-1+i.length)%i.length],o=i[(s+1)%i.length],r=(t[0]-n[0])*(o[1]-n[1])-(t[1]-n[1])*(o[0]-n[0]),a=Yi(Vi(o,n))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function ss(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-n[0])*a[1]-(t[1]-n[1])*a[0],c=t=>(t[0]-n[0])*a[0]+(t[1]-n[1])*a[1],h=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const n=c(e);n<=h||n>=d||tMath.abs(r+(t-p)/m*(a-r));s=Math.min(s,f(_),f(g))}}return s}function os(t,e,i){if(We(t,e,i))return!0;if(Ue(t,e,i)||Ue(e,t,i))return!1;let s=0;try{const i=be([[...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+=Be(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function rs(t,e,i,s,n){const o=[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]=Qi(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[Gi(i.a,o),Gi(i.b,o)]];continue}const t=ts(e.poly,i.a,i.b,n);if(!t.length)continue;const a=es(e.poly,i.a,i.b,o,n);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[Gi(t,o),Gi(e,o)]))}for(const t of e)Zi([t.x,t.y],i.a,i.b)<=n&&(r.openings[t.id]=[t.x+o[0],t.y+o[1]]);return r}function as(t,e,i){for(const s of e)for(let e=0;ei)continue;const c=(t.x-n[0])*l[0]+(t.y-n[1])*l[1];if(c-t.length/2>=-i&&c+t.length/2<=a+i)return!0}return!1}function ls(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),n=a.polys[e];if(!ss(n))return!1;const l=Xi(s.poly),h=Xi(n);if(Math.abs(h)t.find(t=>t.id===e).poly),d=t.map(c);for(const t of ls(e,h,2*r)){const e=a.openings[t.id];if(!as(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function hs(t,e,i,s,n,o){const r=t=>[s[0]+(t[0]-s[0])*n,s[1]+(t[1]-s[1])*n],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 c=t.filter(t=>t.id!==i).map(t=>t.poly),h=hs(l,e,c,s,n,2*a),d=h.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 n=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;n.length>=2&&i(n[n.length-2],n[n.length-1],s)<=0;)n.pop();n.push(s)}return s.pop(),n.pop(),s.concat(n)}(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 ls(e,[l.poly],2*a)){const e=h.openings[t.id];if(!as(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function ps(t,e,i){const s=i/e;return Be(t)*s*s/1e4}function us(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const _s=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],gs=t=>Math.min(1,Math.max(0,t));function ms(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=_s[_s.length-1][1];for(let t=1;t<_s.length;t++){const[s,n]=_s[t-1],[o,r]=_s[t];if(e<=o){i=ki(n,r,(e-s)/(o-s));break}}return{bg:i,planDim:.1*gs((10-e)/16),warmth:e<0?1:gs(1-e/10)}}function fs(t,e,i=6){const s=t.angle*Math.PI/180,n=[Math.sin(s),-Math.cos(s)],o=s=>{const o=[t.x+n[0]*i*s,t.y+n[1]*i*s];return e.find(t=>t.poly.length>=3&&Ae(o,t.poly))||null},r=o(1),a=o(-1);return r&&a?null:r||a?r?{normal:[-n[0],-n[1]],roomId:r.id}:{normal:n,roomId:a.id}:null}function vs(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function bs(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 ys(t,e){try{const i=be([[...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 ws(t,e,i,s,n,o,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,n),l=[-a[0],-a[1]],c=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),h=[];for(const i of e){if(!(i.length>0))continue;const e=fs(i,t);if(!e||!vs(e.normal,a,s))continue;const n=t.find(t=>t.id===e.roomId);if(!n)continue;const d=o&&o[e.roomId]||n.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],g=Math.max(0,r?.[i.id]||0),m=i.x+_[0]*g/2,f=i.y+_[1]*g/2,v=Math.cos(p)*u,b=Math.sin(p)*u,y=[m-v,f-b],w=[m+v,f+b],k=c*i.length,$=ys(bs(y,w,l,k),d);if(!$.length)continue;const x=l[0]*_[0]+l[1]*_[1];h.push({openingId:i.id,roomId:e.roomId,polys:$,a:y,b:w,dir:l,len:k,normal:_,depth:k*x})}return h}function ks(t){return Math.round(10*(Number(t)||0))/10}const $s={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 xs=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Ss(t,e){const i=xs(e?.north_deg);return null!==i?i:xs(t?.north_deg)}function Ms(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function Cs(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Ds(t){const e=t?.weather_entity;return"string"==typeof e&&e.trim()?e.trim():null}function Ts(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 zs=1e3;function Ps(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,n=s>=1?e:e*s,o=s>=1?e/s:e;return{x:(e-n)/2,y:(e-o)/2,w:n,h:o}}const As=.01;function Rs(t,e=1e3){const i=Ps(t?.plan_aspect,e),s=Number(t?.plan_scale),n=Number.isFinite(s)&&s>0?Math.min(100,Math.max(As,s)):1,o=Number(t?.plan_x),r=Number(t?.plan_y);return{x:i.x+(Number.isFinite(o)?Bs(o):0)*e,y:i.y+(Number.isFinite(r)?Bs(r):0)*e,w:i.w*n,h:i.h*n}}function Es(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 Ns(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=zs,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]*zs,i[1]*e,i[2]*zs,i[3]*e],bg:t.plan_url?{href:Li(t.plan_url),...Rs(t,zs)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...Es(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*zs:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*zs:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*zs,t[1]*e]):void 0}})}}):[]}const Os=5e3,Is=Os*zs,Fs=240,Ls=zs/Fs,qs=1/Fs;function Hs(t){if(!Number.isFinite(t))return t;const e=Math.round(t*Fs/zs)*zs/Fs;return Math.abs(e-t)<=1e-9*Ls?t:e}function Us(t){return{x:Hs(t.x),y:Hs(t.y)}}function Ws(t){return Number.isFinite(t)?Math.min(Is,Math.max(-Is,t)):0}function Bs(t){return Number.isFinite(t)?Math.min(Os,Math.max(-Os,t)):0}const js=1/3,Vs=200;function Gs(t){let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t){const t=Number(o[0]),r=Number(o[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>n&&(n=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:n}}function Ks(t){return t.poly&&t.poly.length?Gs(t.poly):null==t.x||null==t.y?null:Gs([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function Ys(t,e){const i=[];for(const e of t.rooms||[]){const t=Ks(e);t&&i.push(t)}t.bg&&i.push({minX:t.bg.x,minY:t.bg.y,maxX:t.bg.x+t.bg.w,maxY:t.bg.y+t.bg.h});for(const t of e||[])if(Array.isArray(t)){const e=Gs([t]);e&&i.push(e)}else i.push(t);return i}const Xs=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},Zs=t=>{let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t)o.minXs&&(s=o.maxX),o.maxY>n&&(n=o.maxY);return e>s||i>n?null:{x:e,y:i,w:s-e,h:n-i}};function Js(t,e){let{x:i,y:s,w:n,h:o}=t;n<30&&(i=i+n/2-100,n=Vs),o<30&&(s=s+o/2-100,o=Vs);const r=Math.max(n,o)*e;return{x:i-r,y:s-r,w:n+2*r,h:o+2*r}}function Qs(t,e={}){const i=e.pad??.05,s=e.k??10,n=e.minSpread??50,o=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=Is&&Math.abs(t.maxX)<=Is&&Math.abs(t.minY)<=Is&&Math.abs(t.maxY)<=Is);if(!o.length)return{core:null,all:null,outliers:0};const r=Zs(o);if(o.length<4){const t=Js(r,i);return{core:t,all:t,outliers:0}}const a=o.map(t=>(t.minX+t.maxX)/2),l=o.map(t=>(t.minY+t.maxY)/2),c=Xs(a),h=Xs(l),d=o.map((t,e)=>Math.max(Math.abs(a[e]-c),Math.abs(l[e]-h))),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),n),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,g=_&&_<=o.length*js?o.filter((t,e)=>!u[e]):o;return{core:Js(Zs(g)||r,i),all:Js(r,i),outliers:g===o?0:_}}function tn(t,e,i=.05){const s=Qs(Ys(t,e),{pad:i});if(s.core)return s.core;const n=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,zs,zs];return{x:n[0],y:n[1],w:n[2],h:n[3]}}function en(t){const e=tn(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function sn(t){const e=[];for(const i of t.rooms||[]){const t=Ks(i);t&&e.push(t)}const i=Qs(e,{pad:0}).core;return i?Math.max(zs,Math.min(Is,Math.max(i.w,i.h))):zs}function nn(t,e,i,s=1){const n=Number(i),o=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(n)||n<=0?t*o:t*sn(e)*o/n}const on=[1,2,5,10,20,50,100,200,500,1e3];function rn(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),n=Math.min(...i);return{x:s,y:n,w:Math.max(...e)-s,h:Math.max(...i)-n}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function an(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 ln=["furniture","appliance","sanitary","other"],cn=[{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]]}],hn=new Map(cn.map(t=>[t.id,t]));function dn(t){return t&&hn.get(t)||null}function pn(t,e,i=Ls,s=1e3){const n=Number(e)>0?Number(e):5;return(Number(t)||0)/n*i/s}const un=5e-4,_n=Os;function gn(t){return Number.isFinite(t)?Math.max(un,Math.min(_n,t)):un}const mn=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function fn(t,e,i){const s=dn(t);if(!(s&&e>0&&i>0))return"";const n=t=>mn(t*e),o=t=>mn(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}H${n(e+s)}V${o(i+a)}H${n(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}L${n(s)} ${o(a)}`)}else if("e"===t[0]){const[,s,a,l,c]=t;r.push(`M${n(s-l)} ${o(a)}A${mn(l*e)} ${mn(c*i)} 0 0 1 ${n(s+l)} ${o(a)}A${mn(l*e)} ${mn(c*i)} 0 0 1 ${n(s-l)} ${o(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${n(e[0])} ${o(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,n,o=0){let r=null,a=n;for(const n of s){const[s,l,c,h]=n,d=c-s,p=h-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let g=((t-s)*d+(e-l)*p)/u;g=Math.max(0,Math.min(1,g));let m=s+g*d,f=l+g*p;const v=Math.hypot(t-m,e-f);if(!(v0){const t=Math.round(g*_/o)*o;m=s+t/_*d,f=l+t/_*p}r={cx:m+b*(i/2),cy:f+y*(i/2),angle:vn(180*Math.atan2(-b,y)/Math.PI),dist:v}}return r}function yn(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function wn(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function kn(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function $n(t,e){const i="number"==typeof t?t:parseFloat(String(t).trim().replace(",","."));if(!Number.isFinite(i)||i<=0)return null;return wn(e?2.54*i:i)}function xn(t,e,i){const s=Number(e)>0?Number(e):5;return wn(t)/s*i}function Sn(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function Mn(t,e){let i=e[0]-t[0],s=e[1]-t[1];const n=Math.hypot(i,s);return n<1e-12?[1,0]:(i/=n,s/=n,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function Cn(t,e,i){const s=Sn((t[0]+e[0])/2,i),n=Sn((t[1]+e[1])/2,i),[o,r]=Mn(t,e);let a=Math.atan2(r,o);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,c=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(c)},${n.toFixed(c)}@${l.toFixed(4)}`}function Dn(t,e,i,s){return 1===s?Cn(t,e,i):Cn([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function Tn(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[n,o]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[n,o,r].every(Number.isFinite)&&s.push({w:e,x:n*i,y:o*i,ang:r})}return s}function zn(t,e){const[i,s]=Mn(t,e);let n=Math.atan2(s,i);return n<0&&(n+=Math.PI),n}function Pn(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function An(t,e,i,s,n=1){if(!t?.length)return null;const o=Dn(e,i,s,n),r=t.find(t=>t.key===o);if(r)return r;const a=n>0?n:1,l=(e[0]+i[0])/2,c=(e[1]+i[1])/2,h=zn(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of Tn(t,a))if(Pn(e.ang,h)&&Math.hypot(e.x-l,e.y-c)<=d)return e.w;return null}function Rn(t,e,i,s,n=1){const o=An(t,e,i,s,n);return o&&o.cm>0?wn(o.cm):0}function En(t,e,i,s=1,n=[]){if(!t?.length)return[];const o=new Set;for(const t of Ce(e))o.add(Dn([t[0],t[1]],[t[2],t[3]],i,s));const r=e||[],a=Math.max(i*s*.02,1e-9);for(let t=0;to.has(t.key)&&t.cm>=1&&t.cm<=100)}function Nn(t,e,i,s=8){const[n,o]=Mn(t,e);let r=Math.atan2(o,n);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 On(t,e,i,s,n,o=1){const r=Dn(e,i,n,o),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,{key:r,cm:wn(s)}]}function In(t,e,i,s,n,o=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,n=1){const o=Gn(t,e,i,s,n);if(!o)return[];const r=[];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),n=Bn(i,t);if(s&&n)return`${jn(s)} ${jn(Vn(n))}`}}let s="";for(let i=0;i.05)&&Jn(r,a,t[0],t[1],t[2],t[3])<=o)return!0}return!1}function qn(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function Hn(t,e){const i=t[e],s=t[(e+1)%t.length],n=s[0]-i[0],o=s[1]-i[1],r=Math.hypot(n,o)||1;let a=-o/r,l=n/r;const c=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,n=e.length-1;st[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r+0)+o&&(i=!i)}return i}([c[0]+.001*a,c[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function Un(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 Wn(t,e,i,s){const n=e[0]*s[1]-e[1]*s[0];if(Math.abs(n)<1e-12)return null;const o=[i[0]-t[0],i[1]-t[1]],r=(o[0]*s[1]-o[1]*s[0])/n;return[t[0]+r*e[0],t[1]+r*e[1]]}function Bn(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 n=0;n0||d>0)){s.push([t[n][0],t[n][1]]);continue}if(Un(y,w)){const e=t[n],i=[e[0]+p*h,e[1]+u*h],o=[e[0]+_*d,e[1]+g*d];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=Wn(k,y,$,w),S=Math.max(h,d,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}h>0&&s.push([t[n][0]+p*h,t[n][1]+u*h]),d>0&&s.push([t[n][0]+_*d,t[n][1]+g*d]),h>0||d>0||s.push([t[n][0],t[n][1]])}return s.length>=3?s:null}function jn(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),r=Se(o);if(!r||r.length<3)return null;const a=qn(s,n),l=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Se(i);if(t)for(const e of Ri(r,t,a))l.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])l.push([t[0],t[1]],[t[2],t[3]]);const c=[],h=[];for(let t=0;ta)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=n||r>=1-n||(o.some(t=>Math.abs(t-r)*s<=2*a)||o.push(r))}o.sort((t,e)=>t-e);for(const s of o)c.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),h.push(t)}return{poly:c,parent:h,orig:r}}function Kn(t,e,i,s){const n=e.poly.length,o=new Array(n).fill(0);if(!t?.length)return o;const r=new Set,a=[];for(let l=0;l0?(o[l]=wn(c.cm),r.add(c.key)):a.push(l)}if(!a.length)return o;const l=s>0?s:1,c=Math.max(.5*i,1e-9)*l,h=Tn(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],n=e.orig[(t+1)%e.orig.length],a=zn(s,n),l=(s[0]+n[0])/2,d=(s[1]+n[1])/2;let p=null;for(const t of h){if(r.has(t.w.key))continue;if(!Pn(t.ang,a))continue;if(Jn(t.x,t.y,s[0],s[1],n[0],n[1])>c)continue;const e=Math.hypot(t.x-l,t.y-d);(!p||et?.id===e),n=Se(s);if(!n)return[];const o=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Se(s);if(t)for(const e of Ri(n,t,i))o.push(e)}return o}(t,e,qn(n,a)),h=function(t,e,i,s,n){const o=qn(s,n),r=[];for(let a=0;aJn(h,d,t[0],t[1],t[2],t[3])<=o);r.push(p?"shared":"outer")}return r}(l.poly,c,s,n,a),d=Kn(i,l,n,a),p=d.map((t,e)=>h[e]&&t>0?xn(t,o,r)/2:0);return{...l,kinds:h,cms:d,offsets:p}}function Xn(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Yn(t,l.id,e,i,s,n,o,r);if(c)for(let t=0;t4*l||(!p||tt?.id===e),c=Se(l);if(!c||c.length<3)return null;if(!i?.length)return c.map(t=>[t[0],t[1]]);const h=Yn(t,e,i,s,n,o,r,a);return h&&h.offsets.some(t=>t>0)&&Bn(h.poly,h.offsets)||c.map(t=>[t[0],t[1]])}function to(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=[],n,o,r,a=1){if(!e?.length)return null;const l=[],c=[];let h=0;for(const s of t||[]){if(!s?.id)continue;const d=Yn(t,s.id,e,i,n,o,r,a);if(!d||d.poly.length<3||!d.offsets.some(t=>t>0))continue;for(const t of d.offsets)t>0&&(h=Math.max(h,2*t));const p=io(d.poly,d.offsets),u=Bn(d.poly,d.offsets);p&&l.push(p),u&&c.push(u)}if(!l.length)return null;const d=function(t,e,i,s,n,o,r){if(!e?.length||!i?.length)return[];const a=4*qn(s,r),l=new Map;for(const a of Xn(t,e,i,s,n,o,r))!a.open&&a.half>0&&!l.has(a.key)&&l.set(a.key,a);const c=[...l.values()];if(c.length<2)return[];const h=[];for(const t of i)for(const e of[[t[0],t[1]],[t[2],t[3]]])h.some(t=>Math.hypot(t[0]-e[0],t[1]-e[1])<=a)||h.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 n=Math.hypot(i,s);return n>a?[i/n,s/n]:null};for(const t of h){const e=c.map(e=>({iv:e,u:p(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(r>0?[t.slice(),h,u,p]:[t.slice(),p,u,h])}}return d}(t,e,i,n,o,r,a);try{let t=to(l[0]);for(let e=1;e0))continue;const i=t.angle*Math.PI/180,s=Math.cos(i),o=Math.sin(i),r=-o,l=s,c=t.length/2,d=1.25*Math.max(h,n*a),p=[[t.x-s*c-r*d,t.y-o*c-l*d],[t.x+s*c-r*d,t.y+o*c-l*d],[t.x+s*c+r*d,t.y+o*c+l*d],[t.x-s*c+r*d,t.y-o*c+l*d]];e=ye(e,to(p))}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?" ":"")+jn(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:h}:null}catch{const s=function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Yn(t,l.id,e,i,s,n,o,r);if(!c||c.poly.length<3||!c.offsets.some(t=>t>0))continue;const h=io(c.poly,c.offsets),d=Bn(c.poly,c.offsets);if(!h||!d)continue;const p=`${jn(h)} ${jn(Vn(d))}`;let u="",_="outer",g=0,m=0;for(let t=0;t0){u=Dn(c.poly[t],c.poly[(t+1)%c.poly.length],s,r),_=c.kinds[t]||"outer",g=c.cms[t],m=xn(g,n,o);break}a.push({d:p,key:u,kind:_,cm:g,depthUnits:m})}return a}(t,e,i,n,o,r,a);return s.length?{d:s.map(t=>t.d).join(" "),depthUnits:h}: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]]);Vn(t),e.slice().reverse();const s=[];for(let n=0;n0||a>0)){s.push([t[n][0],t[n][1]]);continue}if(Un(y,w)){const e=t[n],i=[e[0]-l*r,e[1]-c*r],o=[e[0]-h*a,e[1]-d*a];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=Wn(k,y,$,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[n][0]-l*r,t[n][1]-c*r]),a>0&&s.push([t[n][0]-h*a,t[n][1]-d*a])}return s.length>=3?s:null}function so(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){const c=Se(l);if(c&&c.length>=3){const h=Yn(t,l.id,e,i,s,n,o,r),d=h&&h.offsets.some(t=>t>0)?io(h.poly,h.offsets):null,p=d||c;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 no(t,e,i,s,n,o,r=1){let a=null;for(const i of t||[]){const t=Se(i);if(t)for(let s=0;ss*r)return{ox:0,oy:0,cm:0};const l=Rn(i,a.a,a.b,s,r);if(!(l>0))return{ox:0,oy:0,cm:0};const c=xn(l,n,o),h=Se(a.room),[d,p]=Hn(h,a.edge),u=e.flip_v?-1:1,_=c/2;return{ox:d*_*u,oy:p*_*u,cm:l}}function oo(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function ro(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function ao(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function lo(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!ao(t.a)||!ao(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],n=[Number(t.b[0]),Number(t.b[1])];Math.hypot(n[0]-s[0],n[1]-s[1])<.001||e.push({a:s,b:n})}return e}function co(t,e){const i=e[0],s=e[1],n=e[2]-i,o=e[3]-s,r=n*n+o*o;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*n+(t[1]-s)*o)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*n,s+a*o];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function ho(t,e){return co(t,e).q}function po(t,e,i,s,n){const o=co(t,e).q;let r=o,a=1/0;for(const t of i){const e=Math.hypot(o[0]-t[0],o[1]-t[1]);e<=n&&e0?s:1,g=Math.round(u/_)*_,m=Math.max(0,Math.min(p,g))/p;return[l+h*m,c+d*m]}function uo(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt?.id);let o=null;for(let e=0;et.seg)}(e,s);let o=null;for(const r of e||[]){if(!r?.id)continue;const e=Se(r);if(e)for(let a=0;ai)continue;let p=!1;for(const t of n)if(Bi([(l[0]+c[0])/2,(l[1]+c[1])/2],t)<2*s){p=!0;break}p||(!o||dt?.id),r=lo(e);if(r.length)return r.map(t=>ro(t,i));if(!n)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;too([t[0],t[1]],[t[2],t[3]],e))}function vo(t,e,i){let s=null;for(const n of e){const e=Bi(t,n);e<=i&&(!s||e.05)continue;const d=Rn(t,[e[0],e[1]],[e[2],e[3]],s,n);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-h[0],p[1]-h[1]);u0?l:o}(t,e,i,s,n,o);return On(t,[e[0],e[1]],[e[2],e[3]],r,s,n)}function yo(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],n=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const o of e)if(!(Bi(n,o)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Bi([o[0],o[1]],t)>i))for(const e of[[o[0],o[1]],[o[2],o[3]]])Bi(e,t)<=2*i&&s.push(e);return s}function wo(t,e,i,s){const n=lo(t);if(!n.length)return[];if(e.length!==i.length)return n;const o=[];for(const t of n){const n=ro(t,s),r=[n[0],n[1]],a=[n[2],n[3]];let l=r,c=a;for(let t=0;t{const e=co(t,[s[0],s[1],n[0],n[1]]);if(e.d>.001)return t;const i=h[0]-o[0],r=h[1]-o[1];return[o[0]+i*e.t,o[1]+r*e.t]};if(co([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],n[0],n[1]]).d<.01){l=d(r),c=d(a);break}}o.push(oo(l,c,s))}return o}class ko{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=Li(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],n=s?this.now()-s.at:1/0;return n{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 n=0;for(const o of i){const i=t?.urls?.[o];"string"==typeof i&&i?(s[o]={url:i,at:e},this.retry.delete(o),n++):this.backOff(o)}n&&(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,n]of Object.entries(this.signed))e.has(t)&&i-n.at{const e=Number(t);return Number.isFinite(e)?e:null};function Mo(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=So(e.x)&&null!=So(e.y)?{x:So(e.x),y:So(e.y),a:So(e.a??e.angle??e.theta)}:null;let s=null;const n=t.path?.points??t.path;if(Array.isArray(n)&&n.length){s=[];for(const t of n){const e=So(Array.isArray(t)?t[0]:t?.x),i=So(Array.isArray(t)?t[1]:t?.y);null!=e&&null!=i&&s.push([e,i])}s.length||(s=null)}const o=[],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=So(e.cx??e.center?.x),n=So(e.cy??e.center?.y);if(null==s||null==n){const t=So(e.x0),i=So(e.y0),o=So(e.x1),r=So(e.y1);null!=t&&null!=i&&null!=o&&null!=r&&(s=(t+o)/2,n=(i+r)/2)}if(null!=s&&null!=n||(s=So(e.x),n=So(e.y)),i&&null!=s&&null!=n){const r={id:t,name:i,cx:s,cy:n},a=So(e.x0),l=So(e.y0),c=So(e.x1),h=So(e.y1);null!=a&&null!=l&&null!=c&&null!=h&&(r.x0=Math.min(a,c),r.y0=Math.min(l,h),r.x1=Math.max(a,c),r.y1=Math.max(l,h)),o.push(r)}}const l=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||o.length||s?{pos:i,path:s,rooms:o,mapId:l}:null}function Co(t){const e=t?.attributes;return!(!e||!e.vacuum_position&&!e.robot_position)}const Do=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function To(t,e){const i=new Map(e.map(t=>[Do(t.name),t])),s=[],n=[];for(const e of t){const t=i.get(Do(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),n.push(e.name))}if(s.length<3)return null;const o=function(t){if(t.length<3)return null;let e=0,i=0,s=0,n=0,o=0,r=0,a=0,l=0,c=0,h=0,d=0,p=0;for(const[[u,_],[g,m]]of t){if(![u,_,g,m].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,n+=_*_,o+=_,r+=1,a+=u*g,l+=_*g,c+=g,h+=u*m,d+=_*m,p+=m}const u=[e,i,s,i,n,o,s,o,r],_=t=>{const[e,i,s,n,o,r,a,l,c]=u,h=e*(o*c-r*l)-i*(n*c-r*a)+s*(n*l-o*a);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-r*l)/h,(s*l-i*c)/h,(i*r-s*o)/h,(r*a-n*c)/h,(e*c-s*a)/h,(s*n-e*r)/h,(n*l-o*a)/h,(i*a-e*l)/h,(e*o-i*n)/h];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]]},g=_([a,l,c]),m=_([h,d,p]);if(!g||!m)return null;const f=[g[0],g[1],g[2],m[0],m[1],m[2]];return f.every(Number.isFinite)?f:null}(s);return o?{matrix:o,matched:n,residual:xo(o,s)}:null}function zo(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 n=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[n,o]=s.pop(),[r,a]=t[n],[l,c]=t[o],h=l-r,d=c-a,p=Math.hypot(h,d)||1e-9;let u=0,_=-1;for(let e=n+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([n,_],[_,o]))}const n=[];for(let e=0;e600&&(n=n.filter((t,e)=>e%2==0||e===n.length-1)),n}function Po(t){return"cleaning"===t||"returning"===t||"on"===t}const Ao={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function Ro(t){const[e,i]=Ao[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 Eo(t,e,i,s){const[n,o]=$o(Ro(e),i,s),r=Ro({...t,ox:0,oy:0}),[a,l]=$o(r,i,s);return{...t,ox:n-a,oy:o-l}}function No(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}function Oo(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 Io(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 Fo(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)}function Lo(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),n=[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 n){const i=e.find(e=>Fo(t,e.eid));if(i)return i.eid}for(const t of n)for(const e of ft){const i=t.find(t=>t.eid.split(".")[0]===e);if(i)return i.eid}for(const t of n)if(t.length)return t[0].eid}function qo(t,e){const i=!0===e.marker?.is_light?[...e.marker?.controls||[],...e.entities]:e.entities.filter(t=>t.startsWith("light."));return i.find(e=>"on"===t.states[e]?.state)||null}function Ho(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const n=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===n){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const o=e.attributes?.linkquality??e.attributes?.lqi;if(null!=o){const t=parseFloat(o);isNaN(t)||i.push(t)}}return Ye(i)}function Uo(t,e){for(const i of e){if(!Fo(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 Wo(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 Bo(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 jo(t,e){for(const i of e){if(!Bo(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 Vo(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden)continue;let n=null;if("group"===s.platform)n=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if("Group"!==e?.model)continue;n=e.area_id||s.area_id||null}}if(!n)continue;const o=t.states[e];i.push({eid:e,name:s.name||o?.attributes?.friendly_name||e,area:n})}return i}function Go(t,e,i,s,n){const o=mt(e,i,n);if(o!==gt)return o;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=_t[e];if(t)return t}return null}(r)??gt}function Ko(t,e){t.marker=e,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 Yo(t){const{hass:e,areaToSpace:i,markers:s,settings:n,excluded:o,showAll:r,firstSpaceId:a,loc:l,iconRules:c}=t,h=!1!==n.group_lights,d=Vo(e,h),p=new Set(d.map(t=>t.area)),u=Oo(e),_=new Set;for(const t of s){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||_.add(t.binding)}const g=(t,e)=>s.find(i=>i.binding===t+":"+e),m={},f=[];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(_.has("device:"+t.id))continue;const a=g("device",t.id);if(a&&a.hidden&&!n.filter_seeded)continue;const d=u[t.id]||[],v=Io(e,t,d),b=!n.filter_seeded;if(b&&!r){if(o.has(v))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===v&&t.via_device_id)continue}const y=(t.name_by_user||t.name||l("device.unnamed")).trim(),w=y+"|"+s;let k=Go(e,y,t.model,d,c);if(d.some(t=>t.startsWith("lock."))&&(k="mdi:lock"),b&&!r&&h&&"mdi:lightbulb"===k&&p.has(s))continue;m[w]=(m[w]||0)+1;const $=m[w]>1?y+" "+m[w]:y,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=Lo(e,d,k),"mdi:thermometer"!==k&&"mdi:air-filter"!==k||(x.temp=Uo(e,d)),x.primary&&Bo(e,x.primary)&&(x.hum=jo(e,d)),f.push(x)}for(const t of d)i[t.area]&&(_.has("entity:"+t.eid)||f.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.hidden&&!n.filter_seeded)continue;const[s,o]=t.binding.split(":");if("device"===s){const s=e.devices[o],n=t.area||s?.area_id||"",r=n&&i[n]||t.space||a,h=s&&u[s.id]||[];let d=s?Go(e,s.name_by_user||s.name||"",s.model,h,c):"mdi:help-circle";h.some(t=>t.startsWith("lock."))&&(d="mdi:lock");const p={id:t.id,name:s?.name_by_user||s?.name||l("device.fallback"),model:s?.model||"",area:n,space:r,icon:d,entities:h,bindingKind:"device",bindingRef:o};p.primary=Lo(e,h,d),"mdi:thermometer"!==d&&"mdi:air-filter"!==d||(p.temp=Uo(e,h)),p.primary&&Bo(e,p.primary)&&(p.hum=jo(e,h)),p.primary&&Bo(e,p.primary)&&(p.hum=jo(e,h)),Ko(p,t),f.push(p)}else if("entity"===s){const s=e.entities[o],n=t.area||s?.area_id||s?.device_id&&e.devices[s.device_id]?.area_id||"",r=n&&i[n]||t.space||a,l=e.states[o],h=s?.name||l?.attributes?.friendly_name||o;let d=Go(e,h,"",[o],c);o.startsWith("lock.")&&(d="mdi:lock");const p={id:t.id,name:h,model:"",area:n,space:r,icon:d,entities:[o],primary:o,bindingKind:"entity",bindingRef:o};"mdi:thermometer"!==d&&"mdi:air-filter"!==d||(p.temp=Uo(e,[o])),Bo(e,o)&&(p.hum=jo(e,[o])),Ko(p,t),f.push(p)}else{const e=t.area||"",s=t.space||e&&i[e]||a,n={id:t.id,name:t.name||l("device.virtual"),model:t.model||"",area:e,space:s,icon:t.icon||"mdi:map-marker",entities:[],bindingKind:"virtual",virtual:!0};Ko(n,t),f.push(n)}}return f}function Xo(t,e,i){let s=!1;for(const n of e)if(n.area===i&&!n.hidden)for(const e of n.entities)if(e.startsWith("light.")&&(s=!0,"on"===t.states[e]?.state))return"on";return s?"off":"none"}function Zo(t,e,i){if(!e)return null;const s=e.indexOf(":");if(s<0)return null;const n=e.slice(0,s),o=e.slice(s+1);if(!o)return null;if("entity"===n){const e=parseFloat(t.states[o]?.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===o).map(([t])=>t);return"temp"===i?Uo(t,e):jo(t,e)}return null}const Jo=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 Qo={"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.remove":"Remove","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":"Add a room: connect grid dots with lines until the outline closes","title.markup_merge":"Merge rooms: 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: click a virtual wall to make it solid; a shared solid wall merges the two rooms; an outer wall or inside a room deletes the room (with confirm).","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":"Add","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","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} · Esc/Ctrl+Z — undo a dot · close the outline by clicking the first one","markup.hint_start":"click a grid dot to start the outline","tip.lqi":"average zigbee signal:","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 badge","display.ripple":"Ripple only","display.icon_ripple":"Icon + ripple","marker.ripple_size":"Ripple 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":'Remove "{name}" from the plan?',"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.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 removed 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","editor.aspect_ratio":"Aspect ratio (e.g. 16:9 or auto)","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.color":"Color","decor.width":"Line width","decor.w_thin":"Thin","decor.w_mid":"Medium","decor.w_thick":"Thick","decor.fill":"Fill","decor.text_title":"Text label","decor.text_label":"Text","decor.text_hint":"A new line here is a new line on the plan; the block is centred. Drag a corner to resize it, the handle above it to turn it.","decor.text_hint_live":"{} is where the value lands. Without it the value is added at the end. A new line here is a new line on the plan.","decor.live_group":"Live value","decor.live_hint":"The label may show one entity's value — as Home Assistant reports it, with no rounding. A dead entity shows a dash.","decor.live_entity":"Entity","decor.live_entity_ph":"none — a plain label","decor.live_attr":"Attribute","decor.live_attr_ph":"the state","decor.live_unit":"Unit","decor.live_preview":"Preview","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag the picture to move it, pull a corner to resize it. Shift — off the grid.","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)","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 light sources","marker.controls_hint":"With tap action “Toggle”, a click flips all bound lights at once (any on → all off). The icon mirrors their state.","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":"Wall thickness","title.markup_wallthick":"Wall 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 for new room walls. Empty or zero — thin walls. 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","markup.openwall":"Open boundary","title.markup_openwall":"Open boundary — click a point on a shared wall, then a second point on the same wall (within the nearest corners) to make that stretch virtual. Click a dashed stretch to close it. 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.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 device from plan","marker.hide_tip":'The device is not drawn on the plan but still counts toward the room signal. To see it: the "Show hidden" button in the device editor.',"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":"Grid","gs.grid_hint":"Everything you draw or drag now lands on the grid. Hold Shift to place something between the nodes on purpose.","gs.align_all":"Align everything to the grid","gs.align_title":"Align everything to the grid","gs.align_none":"Everything is already on the grid — nothing to move.","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_warn":"This rewrites the plan and the device positions in one go. There is no undo: the only way back is to move things by hand. If some elements are deliberately between the nodes, cancel.","gs.align_run":"Align","gs.align_done":"Aligned to the grid: {n} elements moved","decor.furniture":"Furniture","furn.title":"Furniture library","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 tr={en:Qo,ru:{"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.remove":"Убрать","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":"Добавить комнату: соединяйте точки сетки линиями до замкнутого контура","title.markup_merge":"Объединить комнаты: клик по одной, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить: виртуальная стена → обычная; общая глухая → слияние комнат; внешняя или клик внутрь → удалить комнату (с подтверждением)","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":"Проём","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} · Esc/Ctrl+Z — убрать точку · замкните контур кликом по первой","markup.hint_start":"кликните точку сетки, чтобы начать контур","tip.lqi":"средний сигнал zigbee:","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.ripple":"Только пульсация","display.icon_ripple":"Значок + пульсация","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.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.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":"Путь дашборда (куда вести)","editor.aspect_ratio":"Соотношение сторон (напр. 16:9 или auto)","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.color":"Цвет","decor.width":"Толщина линии","decor.w_thin":"Тонкая","decor.w_mid":"Средняя","decor.w_thick":"Толстая","decor.fill":"Залить","decor.text_title":"Надпись","decor.text_label":"Текст","decor.text_hint":"Перевод строки здесь — перевод строки на плане; блок выравнивается по центру. Тяните угол — меняет размер, ручка сверху — поворот.","decor.text_hint_live":"{} — место, куда встанет значение. Без него значение добавится в конец. Перевод строки здесь — перевод строки на плане.","decor.live_group":"Живое значение","decor.live_hint":"Надпись может показывать значение одной сущности — так, как его отдаёт Home Assistant, без округления. Мёртвая сущность — прочерк.","decor.live_entity":"Сущность","decor.live_entity_ph":"нет — обычная надпись","decor.live_attr":"Атрибут","decor.live_attr_ph":"состояние","decor.live_unit":"Единица","decor.live_preview":"Предпросмотр","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — двигается, тяните угол — меняет размер. Shift — мимо сетки.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","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":"Толщина стен","title.markup_wallthick":"Толщина стен — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина стен новой комнаты. Пустое или ноль — без толщины. Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","markup.openwall":"Открытая граница","title.markup_openwall":"Открытая граница — клик по точке на общей стене, затем второй клик на той же стене (не дальше ближайших углов) открывает отрезок. Клик по пунктиру закрывает. Курсор — крестик.","toast.openwall_pick":"Кликните по стене, разделяющей две комнаты","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.openwall_opened_span":"Виртуальный отрезок открыт","toast.openwall_closed_span":"Виртуальный отрезок закрыт","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":"Устройство не отображается на плане, но участвует в расчёте сигнала комнаты. Показать: кнопка «Показать скрытые» в редакторе устройств.","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":"Всё, что вы рисуете и перетаскиваете, ложится строго на сетку. Shift — временно поставить элемент между узлами.","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_warn":"Действие разом перезапишет план и позиции устройств. Отмены нет: вернуть можно только руками. Если какие-то элементы стоят между узлами намеренно — отмените.","gs.align_run":"Выровнять","gs.align_done":"Выровнено по сетке: сдвинуто элементов — {n}","decor.furniture":"Мебель","furn.title":"Библиотека мебели","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 er(t,e){if(e&&e in tr)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function ir(t,e,i){return _i(tr[t][e]??Qo[e]??e,i)}class sr 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 er(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:ir(e,"editor.lang_auto")},{value:"en",label:ir(e,"editor.lang_en")},{value:"ru",label:ir(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 V;this._loadSpaces();const t=this._lang,e={title:ir(t,"editor.title"),default_floor:ir(t,"editor.default_floor"),language:ir(t,"editor.language"),icon_size:ir(t,"editor.icon_size"),show_temperature:ir(t,"editor.show_temperature"),live_states:ir(t,"editor.live_states"),show_signal:ir(t,"editor.show_signal"),kiosk:ir(t,"editor.kiosk"),cycle:ir(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)}}ir.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",ir);const sr=o` + >`}_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)}}sr.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",sr);const nr=o` :host { --hp-bg: var(--card-background-color, #16212e); --hp-line: var(--divider-color, #2b3d4f); @@ -2508,7 +2508,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow z-index: 120; max-width: 90vw; } - `;function nr(t){const e=Ns(t.cfg),i=e.find(e=>e.id===t.spaceId);if(!i)return null;const s=fi(t.cfg.spaces.find(e=>e.id===t.spaceId)),n=t.iconSize??2.5,o=n>8?2.5:n,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):ht,l=pt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:dt),c=Ko({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=>er(t.lang,e),iconRules:l}),h=c.filter(e=>e.space===t.spaceId),d=h.filter(t=>!t.hidden),p=function(t,e,i){const s={},n=i/100*sn(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 o=rn(i),r=.1*Math.min(o.w,o.h),a=o.w-2*r,l=o.h-2*r,c=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),h=a/c,d=l/Math.max(Math.ceil(e.length/c),1),p=e.map((t,e)=>({x:o.x+r+h*(e%c+.5),y:o.y+r+d*(Math.floor(e/c)+.5)}));Ze(p,o,n,.5*r),e.forEach((t,e)=>s[t.id]=Us(p[e]))}return s}(h,i,o),u=[];for(const e of d){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*zs,e=i.y*zs;u.push({minX:t,minY:e,maxX:t,maxY:e})}}const _=tn(i,u),g=[_.x,_.y,_.w,_.h],m=i.rooms.filter(t=>t.area||s.showBorders).map(e=>{let n="room "+(i.bg?"overlay":"yard"),o="";const r=Hi(s.fill,e);if(s.showBorders||"none"!==r){n+=" styled";const i=[`--room-stroke:${s.color}`,`--room-stroke-op:${s.showBorders?s.opacity:0}`],a=e.area?$i(r,"lqi"===r?function(t,e,i){const s=[];for(const n of e){if(n.area!==i||n.virtual)continue;const e=Ho(t,n.entities);null!=e&&s.push(e)}return Ye(s)}(t.hass,h,e.area):null,"light"===r?Yo(t.hass,h,e.area):"none","temp"===r?function(t,e,i){const s=[];for(const n of e){if(n.area!==i)continue;if("mdi:thermometer"!==n.icon&&"mdi:air-filter"!==n.icon)continue;const e=qo(t,n.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,h,e.area):null,s.tempMin,s.tempMax,wi(t.cfg?.settings)):null;a?(n+=" filled",i.push(`--room-fill:${a.c}`,`--room-fill-op:${a.a.toFixed(3)}`)):i.push("--room-fill:transparent","--room-fill-op:0"),o=i.join(";")}const a=!i.bg&&!s.showNames,l=an(e),c=e.id||V,d=e.area||V,p=e.poly?B`e.id===t.spaceId);if(!i)return null;const s=fi(t.cfg.spaces.find(e=>e.id===t.spaceId)),n=t.iconSize??2.5,o=n>8?2.5:n,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):ht,l=pt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:dt),c=Yo({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=>ir(t.lang,e),iconRules:l}),h=c.filter(e=>e.space===t.spaceId),d=h.filter(t=>!t.hidden),p=function(t,e,i){const s={},n=i/100*sn(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 o=rn(i),r=.1*Math.min(o.w,o.h),a=o.w-2*r,l=o.h-2*r,c=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),h=a/c,d=l/Math.max(Math.ceil(e.length/c),1),p=e.map((t,e)=>({x:o.x+r+h*(e%c+.5),y:o.y+r+d*(Math.floor(e/c)+.5)}));Ze(p,o,n,.5*r),e.forEach((t,e)=>s[t.id]=Us(p[e]))}return s}(h,i,o),u=[];for(const e of d){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*zs,e=i.y*zs;u.push({minX:t,minY:e,maxX:t,maxY:e})}}const _=tn(i,u),g=[_.x,_.y,_.w,_.h],m=i.rooms.filter(t=>t.area||s.showBorders).map(e=>{let n="room "+(i.bg?"overlay":"yard"),o="";const r=qi(s.fill,e);if(s.showBorders||"none"!==r){n+=" styled";const i=[`--room-stroke:${s.color}`,`--room-stroke-op:${s.showBorders?s.opacity:0}`],a=e.area?$i(r,"lqi"===r?function(t,e,i){const s=[];for(const n of e){if(n.area!==i||n.virtual)continue;const e=Ho(t,n.entities);null!=e&&s.push(e)}return Ye(s)}(t.hass,h,e.area):null,"light"===r?Xo(t.hass,h,e.area):"none","temp"===r?function(t,e,i){const s=[];for(const n of e){if(n.area!==i)continue;if("mdi:thermometer"!==n.icon&&"mdi:air-filter"!==n.icon)continue;const e=Uo(t,n.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,h,e.area):null,s.tempMin,s.tempMax,wi(t.cfg?.settings)):null;a?(n+=" filled",i.push(`--room-fill:${a.c}`,`--room-fill-op:${a.a.toFixed(3)}`)):i.push("--room-fill:transparent","--room-fill-op:0"),o=i.join(";")}const a=!i.bg&&!s.showNames,l=an(e),c=e.id||V,d=e.area||V,p=e.poly?B``:B``;return B`${p}${a?B`${e.name}`:V}`}),f=d.map(e=>{const s=function(t,e,i,s,n){const o=e[t.id];return o&&o.s===t.space?{x:o.x*zs,y:o.y*zs}:s[t.id]?s[t.id]:Us(en(n))}(e,t.layout,t.cfg,p,i),n=(s.x-g[0])/g[2]*100,o=(s.y-g[1])/g[3]*100,r=Number(e.marker?.size)>0?Number(e.marker.size):1,a=Number(e.marker?.angle)||0,l=[`left:${n}%`,`top:${o}%`];return 1!==r&&l.push(`--dev-scale:${r}`),W`
`}),v=s.showNames?i.rooms.filter(t=>t.name).map(e=>{const n=function(t,e,i){const s=i["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*zs,y:s.y*zs};const n=an(t);return Us({x:n[0],y:n[1]})}(e,i.id,t.layout,t.cfg),o=(n.x-g[0])/g[2]*100,r=(n.y-g[1])/g[3]*100,a=Math.min(1,s.opacity+.25);return W`
${e.name}
`}):[],b=i.bg?t.displayUrl?t.displayUrl(i.bg.href):i.bg.href:"",y=t.cfg.spaces.find(e=>e.id===t.spaceId)?.settings||{};let w="";if("daynight"===Ms(t.cfg?.settings,y)&&null!==Ss(t.cfg?.settings,y)){const e=Ts(t.hass);e&&(w=ms(e.elevation).bg)}const k=w||vi(t.cfg?.settings,s),$=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},x=Array.isArray($.walls)?$.walls:[],S=Number($.cell_cm)>0?Number($.cell_cm):5,M=x.length?so(i.rooms,x,[],Hs,S,Ls,zs):Me(i.rooms),C=wi(t.cfg.settings),D=x.length&&s.showBorders?eo(i.rooms,x,[],[],Hs,S,Ls,zs):null,T=t.stageWidth&&g[2]?t.stageWidth/g[2]:1,z=!!D&&yn(D.depthUnits,T);return W` + style="left:${o}%;top:${r}%;color:${s.color};opacity:${a}">${e.name}`}):[],b=i.bg?t.displayUrl?t.displayUrl(i.bg.href):i.bg.href:"",y=t.cfg.spaces.find(e=>e.id===t.spaceId)?.settings||{};let w="";if("daynight"===Ms(t.cfg?.settings,y)&&null!==Ss(t.cfg?.settings,y)){const e=Ts(t.hass);e&&(w=ms(e.elevation).bg)}const k=w||vi(t.cfg?.settings,s),$=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},x=Array.isArray($.walls)?$.walls:[],S=Number($.cell_cm)>0?Number($.cell_cm):5,M=x.length?so(i.rooms,x,[],qs,S,Ls,zs):Me(i.rooms),C=wi(t.cfg.settings),D=x.length&&s.showBorders?eo(i.rooms,x,[],[],qs,S,Ls,zs):null,T=t.stageWidth&&g[2]?t.stageWidth/g[2]:1,z=!!D&&yn(D.depthUnits,T);return W`
${D?B` @@ -2538,13 +2538,13 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${""}
${f}${v}
- `}let or=null,rr=null,ar=!1;const lr=new Set;function cr(){if(or)return or;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 hr(t){return or?Promise.resolve(or):rr||(rr=async function(t){const[e,i]=await Promise.all([t.callWS({type:"houseplan/config/get"}),t.callWS({type:"houseplan/layout/get"})]);if(or={config:e?.config??null,rev:e?.rev??0,layout:i?.layout??{}},!ar&&t.connection?.subscribeEvents){ar=!0;const e=()=>{or=null,lr.forEach(t=>t())};try{await t.connection.subscribeEvents(e,"houseplan_config_updated"),await t.connection.subscribeEvents(e,"houseplan_layout_updated")}catch{ar=!1}}return or}(t).finally(()=>{rr=null}),rr)}class dr 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 tr(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:"aspect_ratio",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}}]}render(){if(!this.hass||!this._config)return V;this._loadSpaces();const t=this._lang,e={space:er(t,"editor.space"),title:er(t,"editor.title"),show_button:er(t,"editor.show_button"),button_label:er(t,"editor.button_label"),button_target:er(t,"editor.button_target"),aspect_ratio:er(t,"editor.aspect_ratio"),icon_size:er(t,"editor.icon_size")};return W`{rr=null,cr.forEach(t=>t())};try{await t.connection.subscribeEvents(e,"houseplan_config_updated"),await t.connection.subscribeEvents(e,"houseplan_layout_updated")}catch{lr=!1}}return rr}(t).finally(()=>{ar=null}),ar)}class pr 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 er(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:"aspect_ratio",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}}]}render(){if(!this.hass||!this._config)return V;this._loadSpaces();const t=this._lang,e={space:ir(t,"editor.space"),title:ir(t,"editor.title"),show_button:ir(t,"editor.show_button"),button_label:ir(t,"editor.button_label"),button_target:ir(t,"editor.button_target"),aspect_ratio:ir(t,"editor.aspect_ratio"),icon_size:ir(t,"editor.icon_size")};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)}}dr.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",dr);const pr=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 ur extends lt{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._stageWidth=0,this._loadedOnce=!1,this._signer=new wo(()=>this.requestUpdate()),this._goToSpace=()=>{const t=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");pr(`${t}#space=${encodeURIComponent(this._config.space)}`)}}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(t){const e=cr();return{type:"custom:houseplan-space-card",space:Ns(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||cr()}connectedCallback(){var t;super.connectedCallback(),this._unsub=(t=()=>{this._loading=!1,this._snap=null,this.requestUpdate()},lr.add(t),()=>lr.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 hr(this.hass);this._snap=t,this._loadedOnce=!0}catch{}finally{this._loading=!1,this.requestUpdate()}}}get _lang(){return tr(this.hass,this._config?.language)}getCardSize(){const t=Ns(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 Fi(this._snap?.config)}render(){if(!this._config)return V;const t=this._snap?.config;if(!t)return W`
${er(this._lang,"space_card.loading")}
`;const e=this._config.space,i=nr({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(er(this._lang,"space_card.not_found",{id:e}));const s=Ns(t).find(t=>t.id===e),n=void 0!==this._config.title?this._config.title:s?.title||"",o=!1!==this._config.show_button,r=this._config.button_label||er(this._lang,"space_card.button");return W` + >
`}_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)}}pr.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",pr);const ur=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 _r extends lt{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._stageWidth=0,this._loadedOnce=!1,this._signer=new ko(()=>this.requestUpdate()),this._goToSpace=()=>{const t=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");ur(`${t}#space=${encodeURIComponent(this._config.space)}`)}}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(t){const e=hr();return{type:"custom:houseplan-space-card",space:Ns(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||hr()}connectedCallback(){var t;super.connectedCallback(),this._unsub=(t=()=>{this._loading=!1,this._snap=null,this.requestUpdate()},cr.add(t),()=>cr.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 dr(this.hass);this._snap=t,this._loadedOnce=!0}catch{}finally{this._loading=!1,this.requestUpdate()}}}get _lang(){return er(this.hass,this._config?.language)}getCardSize(){const t=Ns(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 Fi(this._snap?.config)}render(){if(!this._config)return V;const t=this._snap?.config;if(!t)return W`
${ir(this._lang,"space_card.loading")}
`;const e=this._config.space,i=or({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(ir(this._lang,"space_card.not_found",{id:e}));const s=Ns(t).find(t=>t.id===e),n=void 0!==this._config.title?this._config.title:s?.title||"",o=!1!==this._config.show_button,r=this._config.button_label||ir(this._lang,"space_card.button");return W` ${n?W`
${n}
`:V} ${i} @@ -2552,7 +2552,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow `:V}
- `}}ur.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0}},ur.styles=[sr,o` + `}}_r.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0}},_r.styles=[nr,o` .hp-static-title { font-weight: 700; padding: 10px 14px 6px; @@ -2627,7 +2627,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",ur),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 _r=4.166666666666667e-9;function gr(t){if(!Number.isFinite(t))return t;const e=Math.round(t/Hs)*Hs;return Math.abs(e-t)<=_r?t:e}const mr=(t,e,i,s)=>Math.hypot(i-t,s-e),fr=(t,e,i,s,n,o,r,a)=>Math.hypot(Math.max(Math.abs(n-t),Math.abs(n+r-(t+i))),Math.max(Math.abs(o-e),Math.abs(o+a-(e+s)))),vr=(t,e,i,s,n,o,r)=>{const a=Math.max(Number(s)||0,0)/2,l=Math.PI/180,c=Math.cos(i*l)*a,h=Math.sin(i*l)*a,d=Math.cos(r*l)*a,p=Math.sin(r*l)*a,u=Math.max(mr(t+c,e+h,n+d,o+p),mr(t-c,e-h,n-d,o-p)),_=Math.max(mr(t+c,e+h,n-d,o-p),mr(t-c,e-h,n+d,o+p));return Math.min(u,_)},br=t=>{const e=Number(t?.cell_cm);return e>0?e:5};const yr="1.59.0-beta.8",wr=1500,kr=new Map;let $r=0;const xr=t=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(t??{})}`;let Sr=1e4;const Mr="houseplan_card_layout_v1",Cr="houseplan_card_cfg_v1",Dr="houseplan_card_zoom_v1",Tr="houseplan_card_nav_v1",zr="houseplan_card_kiosk_v1",Pr=1e3,Ar=(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}},Rr=(t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)},Er=(t,e)=>{let i,s=null;const n=(...n)=>{clearTimeout(i),s=n,i=window.setTimeout(()=>{i=void 0;const e=s;s=null,e&&t(...e)},e)};return n.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const e=s;s=null,e&&t(...e)},n.pending=()=>void 0!==i,n},Nr=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}};class Or 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._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={color:"#607d8b",width:3,fill:!1},this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorTextDialog=null,this._furnPalette=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._tool="draw",this._wallDialog=null,this._drawWallField=null,this._rszSel=null,this._rszDrag=null,this._rszUndo=[],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._opShift=!1,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._warmRevivePending=!1,this._warmGen=++$r,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._senseRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._vacVisHandler=()=>{"visible"===document.visibilityState&&(this._vacJumpOnce=!0,this._skyElev=null,this.requestUpdate())},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._space=t,this._selId=null,this._restoreZoom(),this.requestUpdate())},this._drag=null,this._rlResize=null,this._holdFired=!1,this._cfgEpoch=0,this._modelCache=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 wo(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=Er(()=>{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(Mr,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._saveConfigDebounced=Er(()=>{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._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:vi(this._settings,{bgColor:null})||null,northDeg:Ss(this._settings,{}),bgMode:Ms(this._settings,{}),sunRays:Cs(this._settings,{}),weatherEntity:Ds(this._settings)||"",busy:!1}},this._openAlignDialog=()=>{if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[],e=function(t,e){const i=JSON.parse(JSON.stringify(t||[])),s=JSON.parse(JSON.stringify(e||{}));let n=0,o=0,r=0,a=0,l="",c=0;const h={};let d=5;for(const t of i){const e=br(t);null!=t?.id&&(h[String(t.id)]=e),e>d&&(d=e)}const p=(t,e,i,s=!1)=>{if(!(t>_r||s))return;n++,t>r&&(r=t);const o=t*Fs*e;o>a&&(a=o,l=i)};for(const t of i){const e=br(t),i=null!=t?.id?String(t.id):"";for(const s of t.rooms||[]){o++;let t=0;if(s.poly?.length)s.poly=s.poly.map(e=>{const i=[gr(e[0]),gr(e[1])];return t=Math.max(t,mr(e[0],e[1],i[0],i[1])),i});else if(null!=s.x&&null!=s.y){const e=s.x,i=s.y,n=s.w||0,o=s.h||0,r=gr(e+n),a=gr(i+o),l=gr(e),c=gr(i),h=Math.max(Hs,r-l),d=Math.max(Hs,a-c);t=fr(e,i,n,o,l,c,h,d),s.x=l,s.y=c,s.w=h,s.h=d}p(t,e,i)}for(const s of t.decor||[]){o++;let t=0;if("line"===s.kind){const e=[gr(s.x1),gr(s.y1)],i=[gr(s.x2),gr(s.y2)];t=Math.max(mr(s.x1,s.y1,e[0],e[1]),mr(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=gr(s.x),i=gr(s.y);if(null!=s.w&&null!=s.h){const n=gr(s.x+s.w),o=gr(s.y+s.h),r=Math.max(Hs,n-e),a=Math.max(Hs,o-i);t=fr(s.x,s.y,s.w,s.h,e,i,r,a),s.w=r,s.h=a}else t=mr(s.x,s.y,e,i);s.x=e,s.y=i}p(t,e,i)}for(const s of t.openings||[]){o++;const n=De([s.x,s.y],t.rooms||[],.025,{step:Hs,length:Number(s.length)||0});if(!n)continue;const r=Number(s.angle),a=!(Number.isFinite(r)&&r===n.angle),l=vr(s.x,s.y,Number.isFinite(r)?r:n.angle,Number(s.length)||0,n.x,n.y,n.angle);s.x=n.x,s.y=n.y,s.angle=n.angle,a&&c++,p(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;o++;const n=gr(i.x),r=gr(i.y),a=mr(i.x,i.y,n,r);s[t]={...i,x:n,y:r};const l="string"==typeof i.s?i.s:"";p(a,h[l]??d,l)}return{spaces:i,layout:s,report:{moved:n,total:o,maxShift:r,maxShiftCm:a,maxSpace:l,rotated:c},changed:n>0}}(t,this._layout||{}),i=Math.ceil(10*e.report.maxShiftCm)/10,s=t.find(t=>null!=t?.id&&String(t.id)===e.report.maxSpace),n=t.length>1&&s?String(s.title||s.id):"";this._alignDialog={report:e.report,spaces:e.spaces,layout:e.layout,cm:i,where:n,busy:!1}},this._openRulesDialog=()=>{if(!this._norm)return;const t=this._settings.icon_rules,e=(t&&t.length?t:dt).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)}_slideTo(t,e){if(t===this._space)return;const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;this._space=t,this._selId=null,this._restoreZoom(),i||(this._slide=e,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slide="",this.requestUpdate()},260),this.requestUpdate())}_cycleTick(){if(this._kiosk&&Number(this._config?.cycle)>0&&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,()=>Fi(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},wr)),!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))}disconnectedCallback(){document.removeEventListener("visibilitychange",this._vacVisHandler),this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._skySnapRaf&&(cancelAnimationFrame(this._skySnapRaf),this._skySnapRaf=0);for(const t of this._senseRt.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._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),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),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._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._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0)}if("decor"===this._mode)return"Delete"!==t.key&&"Backspace"!==t.key||!this._decorSel||t.target?.closest?.("input, textarea, select")?void("Escape"===t.key&&(t.preventDefault(),this._decorDraft?this._decorDraft=null: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)return;const e="Escape"===t.key||(t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase();if(e){if(this._roomDialog)return t.preventDefault(),void this._roomDialogCancel();if("draw"===this._tool&&this._path.length)return t.preventDefault(),void this._undoPoint();if(e)return"resize"===this._tool?(t.preventDefault(),this._rszDrag?void this._rszCancelDrag():(t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase()?void this._rszUndoPop():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?(t.preventDefault(),void(this._openWallAnchor?this._openWallAnchor=null:this._tool="draw")):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool||(t.preventDefault(),this._tool="draw"))}}_undoPoint(){this._path.length&&(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 kr.values())for(const e of t)clearTimeout(e.evict);kr.clear(),Sr=t&&t>0?t:1e4}static _warmBootStats(){let t=0,e=0;const i=[];for(const s of kr.values())for(const n of s)t++,n.dlg&&(e++,i.push(n.dlg.kind));return{keys:kr.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(Dr)||"{}")||{}}catch{this._zoomBySpace={}}try{const t=JSON.parse(localStorage.getItem(zr)||"null");this._kioskScale={icon:qi(t?.icon),font:qi(t?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(Cr)||"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()}_warmAdopt(){if(this._config?.kiosk)return;const t=xr(this._config);if(this._warmKey===t&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const e=this.parentNode,i=this._warmIdx(e),s=kr.get(t);if(!s||!s.length)return;const n=s.find(t=>t.owner===this._warmGen);if(n)return clearTimeout(n.evict),n.evict=0,n.freed=0,n.live=!0,this._warmSlot=n,void(this._warmKey=t);const{slot:o,sure:r}=((t,e,i,s)=>{const n=t=>{const e=!!i&&t.place?.deref()===i;return e&&t.idx===s?4:t.live?0:e?3:2};let o=null,r=0,a=0,l=null;for(const i of t){if(i.owner===e)continue;l=i;const t=n(i);t<=0||(t>r?(o=i,r=t,a=1):t===r&&a++)}return!o||a>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(s,this._warmGen,e,i);o&&(this._booting=!1,this._bootFading=!1,this._hdrH=o.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},wr)),this._warmKey=t,r?(clearTimeout(o.evict),o.evict=0,o.owner=this._warmGen,o.place=e?new WeakRef(e):null,o.idx=i,o.live=!0,this._warmSlot=o,this._warmVp=o.vp,this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,place:e?new WeakRef(e):null,idx:i,live:!0,hdrH:o.hdrH,stageH:o.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 n=kr.get(e);if(!t.live&&n&&n.length>1){const e=n.indexOf(t);e>=0&&n.splice(e,1)}},Sr+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=xr(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=kr.get(i)||[];for(s.push(this._warmSlot),kr.set(i,s),this._warmTrim(s);kr.size>8;){const t=kr.keys().next().value;if(void 0===t||t===i)break;for(const e of kr.get(t)||[])clearTimeout(e.evict);kr.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._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._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>Sr)&&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"decorText":this._decorTextDialog={...e.data};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(Cr,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.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 Ns(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:n,excluded:o,iconRules:r}=t,a=!1!==n.group_lights,l=jo(e,a),c=new Set(l.map(t=>t.area)),h=No(e),d=new Set(s.map(t=>t.binding)),p=[];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(d.has("device:"+t.id))continue;const n=h[t.id]||[],l=Oo(e,t,n);let u=o.has(l)||"Group"===t.model||/scene/i.test(t.model||"")||/bridge/i.test((t.model||"")+(t.name||""))||"myheat"===l&&!!t.via_device_id;!u&&a&&c.has(s)&&"mdi:lightbulb"===Vo(e,(t.name_by_user||t.name||"").trim(),t.model,n,r)&&(u=!0),u&&p.push("device:"+t.id)}return p}({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=pt(t)),this._rulesCompiled}get _fillColors(){return wi(this._settings)}get _excluded(){const t=this._settings.exclude_integrations;return t?new Set(t):ht}willUpdate(t){t?.has?.("hass")&&(this._vacTick(),this._senseTick()),this._skyPlan(),t.has("hass")&&this.hass&&(this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices())}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),n=Math.round(i+s);n>=0&&Math.abs(n-this._hdrH)>1&&(this._hdrH=n),n>=0&&!this._booting&&!this._config?.kiosk&&t.clientHeight>0&&this._warmPatch({hdrH:n,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._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null);const i=t?.config;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=>{(t?.data?.rev??-1)!==this._cfgRev&&this._reloadConfigOnly()},"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(),n=this._savedNav();!this._hashApplied&&s&&this._model.find(t=>t.id===s)?(this._space=s,this._hashApplied=!0):n?.space&&!this._navApplied&&!this._hashApplied&&this._model.find(t=>t.id===n.space)?(this._space=n.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(Mr)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1}this._regSignature="",this.requestUpdate()}async _reloadConfigOnly(t=!1){if(!t&&(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting))return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(),400));try{const t=await this.hass.callWS({type:"houseplan/config/get"}),e=t?.config;this._serverCfg=e&&Array.isArray(e.spaces)?e:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,"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,Fi(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._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")+":"+tr(t,this._config?.language);e===this._regSignature&&this._devices.length||(this._regSignature=e,this._devices=Ko({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())}_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:n}=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:n,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=Ho(this.hass,i.entities);null!=s&&e.push(s)}return Ye(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),n=Math.min(...i);return{x:s,y:n,w:Math.max(...e)-s,h:Math.max(...i)-n}}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*sn(i)*1.3;for(const e of i.rooms){if(!e.area)continue;const n=this._devices.filter(t=>t.area===e.area&&t.space===i.id);if(!n.length)continue;const o=this._roomBounds(e),r=.1*Math.min(o.w,o.h),a=o.w-2*r,l=o.h-2*r,c=Math.max(1,Math.round(Math.sqrt(n.length*a/Math.max(l,1)))),h=Math.ceil(n.length/c),d=a/c,p=l/Math.max(h,1),u=n.map((t,e)=>({x:o.x+r+d*(e%c+.5),y:o.y+r+p*(Math.floor(e/c)+.5)}));Ze(u,o,s,.5*r),n.forEach((e,i)=>t[e.id]=Us(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*Pr,y:i.y*Pr}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[t.id]?this._defPos[t.id]:Us(en(e))}_savePos(t,e,i,s=!1){if(this._norm){const n=this._gridPitch,o=s?e:Math.round(e/n)*n,r=s?i:Math.round(i/n)*n,a=this._layout[t.id]?.k;this._layout={...this._layout,[t.id]:{s:t.space,x:Bs(o/Pr),y:Bs(r/Pr),...a?{k:a}:{}}}}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?ri(t.entities):null}_actEntity(t){return this._coverIndicator(t)||t.primary}_stateClass(t){if(!this._config?.live_states)return"";const e=this._coverIndicator(t);if(e){const t=this.hass.states[e];return t?"unavailable"===t.state?"unavail":ai(t.state)?"covermove":"":""}const i=(t.marker?.controls||[]).filter(Ai);if(i.length)return i.some(t=>"on"===this.hass.states[t]?.state)?"on":"";if(Lo(this.hass,t))return"on";const s=this._actEntity(t),n=s?this.hass.states[s]:void 0;if(!n)return"";if("unavailable"===n.state)return"unavail";const o=s.split(".")[0];if(["light","switch","fan","humidifier"].includes(o))return"on"===n.state?"on":"";if("climate"===o){const t=n.attributes?.hvac_action;return null!=t?["heating","cooling","drying","fan"].includes(t)?"on":"":["off","unknown"].includes(n.state)?"":"on"}if("cover"===o)return ai(n.state)?"covermove":"";if("valve"===o)return["open","opening"].includes(n.state)?"open":"";if("lock"===o)return["unlocked","open"].includes(n.state)?"open":"";if("binary_sensor"===o){const e=n.attributes?.device_class;if(["door","window","garage_door","opening","gas","smoke","moisture","problem"].includes(e))return"on"===n.state?"open":"";if("motion"===e){const e=this._senseRt.get(t.id);return e&&e.flashTs&&Date.now()-e.flashTs<3300?e.gen%2==0?"senseflash sf2":"senseflash":""}if(["occupancy","presence"].includes(e))return"on"===n.state?"sensehold":""}return"media_player"===o?["playing","on"].includes(n.state)?"on":"":"vacuum"===o&&["cleaning","returning"].includes(n.state)?"on":""}_liveTemp(t){if(!this._config?.show_temperature)return null;if(!0===t.marker?.use_climate_temp){const e=Uo(this.hass,t.entities);if(null!=e)return e}return"mdi:thermometer"!==t.icon&&"mdi:air-filter"!==t.icon?null:qo(this.hass,t.entities)}_bindingHasClimate(t){if(t.startsWith("entity:"))return t.slice(7).startsWith("climate.");if(t.startsWith("device:")){const e=t.slice(7);for(const[t,i]of Object.entries(this.hass?.entities||{}))if(i?.device_id===e&&t.startsWith("climate."))return!0}return!1}_bindingCoverEntity(t){if(t.startsWith("entity:"))return ri([t.slice(7)]);if(t.startsWith("device:")){const e=t.slice(7);return ri(Object.entries(this.hass?.entities||{}).filter(([,t])=>t?.device_id===e).map(([t])=>t))}return null}_bindingCoverTap(t){const e=this._bindingCoverEntity(t);if(!e)return!1;const i=String(this.hass?.states?.[e]?.attributes?.device_class||"");return!oi.has(i)}_liveHum(t){return this._config?.show_temperature&&t.primary&&Wo(this.hass,t.primary)?Bo(this.hass,t.entities):null}_openMoreInfo(t){t?Rr(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),n=s?s.split(".")[0]:null,o=(t,i)=>{e.marker?.tap_confirm?this._tapConfirm={text:t,exec:i}:i()},r=(e.marker?.controls||[]).filter(Ai);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 o(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 n=t||e||("light"===i?"toggle":"info");return"more-info"===n?"more-info":"run"===n?"run"===t?"run":"info":"cover"===n?"cover"!==t||"cover"!==i||oi.has(String(s||""))?"info":"cover":"toggle"!==n||!i||ni.has(i)?"info":"toggle"===t?"toggle":si.has(i)?"cover"===i&&oi.has(String(s||""))?"info":"toggle":"info"}(e.tapAction,void 0,n,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 n=s.attributes?.friendly_name||t;return void o(this._t("confirm.tap_run",{name:n}),()=>{this.hass.callService(i.domain,i.service,{entity_id:t}).then(()=>this._showToast(this._t("toast.run_started",{name:n}))).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 o(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?o(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 er(tr(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,n=Math.sin(i)*t.rlen/2,o=Gs([[t.rx-s,t.ry-n],[t.rx+s,t.ry+n]]);o&&e.push(o)}const t=this._decorH;for(const i of this._decorList){const s=Gs("line"===i.kind?[[i.x1*Pr,i.y1*t],[i.x2*Pr,i.y2*t]]:[[i.x*Pr,i.y*t],[(i.x+(i.w||0))*Pr,(i.y+(i.h||0))*t]]);s&&e.push(s)}}return Ys(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=Qs(this._contentItems(t));let n=s.all||tn(t),o=this._showFar?n:s.core||tn(t);return e&&e.id===t.id&&i&&e.grow&&(o=Ar(e.rect,o),n=Ar(e.all,n)),this._frame={id:t.id,model:t,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:n,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._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?V:W`
+ `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",_r),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 gr=4.166666666666667e-9;function mr(t){if(!Number.isFinite(t))return t;const e=Math.round(t/qs)*qs;return Math.abs(e-t)<=gr?t:e}const fr=(t,e,i,s)=>Math.hypot(i-t,s-e),vr=(t,e,i,s,n,o,r,a)=>Math.hypot(Math.max(Math.abs(n-t),Math.abs(n+r-(t+i))),Math.max(Math.abs(o-e),Math.abs(o+a-(e+s)))),br=(t,e,i,s,n,o,r)=>{const a=Math.max(Number(s)||0,0)/2,l=Math.PI/180,c=Math.cos(i*l)*a,h=Math.sin(i*l)*a,d=Math.cos(r*l)*a,p=Math.sin(r*l)*a,u=Math.max(fr(t+c,e+h,n+d,o+p),fr(t-c,e-h,n-d,o-p)),_=Math.max(fr(t+c,e+h,n-d,o-p),fr(t-c,e-h,n+d,o+p));return Math.min(u,_)},yr=t=>{const e=Number(t?.cell_cm);return e>0?e:5};const wr="1.59.0-beta.9",kr=1500,$r=new Map;let xr=0;const Sr=t=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(t??{})}`;let Mr=1e4;const Cr="houseplan_card_layout_v1",Dr="houseplan_card_cfg_v1",Tr="houseplan_card_zoom_v1",zr="houseplan_card_nav_v1",Pr="houseplan_card_kiosk_v1",Ar=1e3,Rr=(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}},Er=(t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)},Nr=(t,e)=>{let i,s=null;const n=(...n)=>{clearTimeout(i),s=n,i=window.setTimeout(()=>{i=void 0;const e=s;s=null,e&&t(...e)},e)};return n.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const e=s;s=null,e&&t(...e)},n.pending=()=>void 0!==i,n},Or=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}};class Ir 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._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={color:"#607d8b",width:3,fill:!1},this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorTextDialog=null,this._furnPalette=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._tool="draw",this._wallDialog=null,this._drawWallField=null,this._rszSel=null,this._rszDrag=null,this._rszUndo=[],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._opShift=!1,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._warmRevivePending=!1,this._warmGen=++xr,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._senseRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._vacVisHandler=()=>{"visible"===document.visibilityState&&(this._vacJumpOnce=!0,this._skyElev=null,this.requestUpdate())},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._space=t,this._selId=null,this._restoreZoom(),this.requestUpdate())},this._drag=null,this._rlResize=null,this._holdFired=!1,this._cfgEpoch=0,this._modelCache=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 ko(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=Nr(()=>{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(Cr,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._saveConfigDebounced=Nr(()=>{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._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:vi(this._settings,{bgColor:null})||null,northDeg:Ss(this._settings,{}),bgMode:Ms(this._settings,{}),sunRays:Cs(this._settings,{}),weatherEntity:Ds(this._settings)||"",busy:!1}},this._openAlignDialog=()=>{if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[],e=function(t,e){const i=JSON.parse(JSON.stringify(t||[])),s=JSON.parse(JSON.stringify(e||{}));let n=0,o=0,r=0,a=0,l="",c=0;const h={};let d=5;for(const t of i){const e=yr(t);null!=t?.id&&(h[String(t.id)]=e),e>d&&(d=e)}const p=(t,e,i,s=!1)=>{if(!(t>gr||s))return;n++,t>r&&(r=t);const o=t*Fs*e;o>a&&(a=o,l=i)};for(const t of i){const e=yr(t),i=null!=t?.id?String(t.id):"";for(const s of t.rooms||[]){o++;let t=0;if(s.poly?.length)s.poly=s.poly.map(e=>{const i=[mr(e[0]),mr(e[1])];return t=Math.max(t,fr(e[0],e[1],i[0],i[1])),i});else if(null!=s.x&&null!=s.y){const e=s.x,i=s.y,n=s.w||0,o=s.h||0,r=mr(e+n),a=mr(i+o),l=mr(e),c=mr(i),h=Math.max(qs,r-l),d=Math.max(qs,a-c);t=vr(e,i,n,o,l,c,h,d),s.x=l,s.y=c,s.w=h,s.h=d}p(t,e,i)}for(const s of t.decor||[]){o++;let t=0;if("line"===s.kind){const e=[mr(s.x1),mr(s.y1)],i=[mr(s.x2),mr(s.y2)];t=Math.max(fr(s.x1,s.y1,e[0],e[1]),fr(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=mr(s.x),i=mr(s.y);if(null!=s.w&&null!=s.h){const n=mr(s.x+s.w),o=mr(s.y+s.h),r=Math.max(qs,n-e),a=Math.max(qs,o-i);t=vr(s.x,s.y,s.w,s.h,e,i,r,a),s.w=r,s.h=a}else t=fr(s.x,s.y,e,i);s.x=e,s.y=i}p(t,e,i)}for(const s of t.openings||[]){o++;const n=De([s.x,s.y],t.rooms||[],.025,{step:qs,length:Number(s.length)||0});if(!n)continue;const r=Number(s.angle),a=!(Number.isFinite(r)&&r===n.angle),l=br(s.x,s.y,Number.isFinite(r)?r:n.angle,Number(s.length)||0,n.x,n.y,n.angle);s.x=n.x,s.y=n.y,s.angle=n.angle,a&&c++,p(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;o++;const n=mr(i.x),r=mr(i.y),a=fr(i.x,i.y,n,r);s[t]={...i,x:n,y:r};const l="string"==typeof i.s?i.s:"";p(a,h[l]??d,l)}return{spaces:i,layout:s,report:{moved:n,total:o,maxShift:r,maxShiftCm:a,maxSpace:l,rotated:c},changed:n>0}}(t,this._layout||{}),i=Math.ceil(10*e.report.maxShiftCm)/10,s=t.find(t=>null!=t?.id&&String(t.id)===e.report.maxSpace),n=t.length>1&&s?String(s.title||s.id):"";this._alignDialog={report:e.report,spaces:e.spaces,layout:e.layout,cm:i,where:n,busy:!1}},this._openRulesDialog=()=>{if(!this._norm)return;const t=this._settings.icon_rules,e=(t&&t.length?t:dt).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)}_slideTo(t,e){if(t===this._space)return;const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;this._space=t,this._selId=null,this._restoreZoom(),i||(this._slide=e,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slide="",this.requestUpdate()},260),this.requestUpdate())}_cycleTick(){if(this._kiosk&&Number(this._config?.cycle)>0&&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,()=>Fi(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},kr)),!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))}disconnectedCallback(){document.removeEventListener("visibilitychange",this._vacVisHandler),this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._skySnapRaf&&(cancelAnimationFrame(this._skySnapRaf),this._skySnapRaf=0);for(const t of this._senseRt.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._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),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),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._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._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0)}if("decor"===this._mode)return"Delete"!==t.key&&"Backspace"!==t.key||!this._decorSel||t.target?.closest?.("input, textarea, select")?void("Escape"===t.key&&(t.preventDefault(),this._decorDraft?this._decorDraft=null: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)return;const e="Escape"===t.key||(t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase();if(e){if(this._roomDialog)return t.preventDefault(),void this._roomDialogCancel();if("draw"===this._tool&&this._path.length)return t.preventDefault(),void this._undoPoint();if(e)return"resize"===this._tool?(t.preventDefault(),this._rszDrag?void this._rszCancelDrag():(t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase()?void this._rszUndoPop():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?(t.preventDefault(),void(this._openWallAnchor?this._openWallAnchor=null:this._tool="draw")):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool||(t.preventDefault(),this._tool="draw"))}}_undoPoint(){this._path.length&&(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 $r.values())for(const e of t)clearTimeout(e.evict);$r.clear(),Mr=t&&t>0?t:1e4}static _warmBootStats(){let t=0,e=0;const i=[];for(const s of $r.values())for(const n of s)t++,n.dlg&&(e++,i.push(n.dlg.kind));return{keys:$r.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(Tr)||"{}")||{}}catch{this._zoomBySpace={}}try{const t=JSON.parse(localStorage.getItem(Pr)||"null");this._kioskScale={icon:Hi(t?.icon),font:Hi(t?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(Dr)||"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()}_warmAdopt(){if(this._config?.kiosk)return;const t=Sr(this._config);if(this._warmKey===t&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const e=this.parentNode,i=this._warmIdx(e),s=$r.get(t);if(!s||!s.length)return;const n=s.find(t=>t.owner===this._warmGen);if(n)return clearTimeout(n.evict),n.evict=0,n.freed=0,n.live=!0,this._warmSlot=n,void(this._warmKey=t);const{slot:o,sure:r}=((t,e,i,s)=>{const n=t=>{const e=!!i&&t.place?.deref()===i;return e&&t.idx===s?4:t.live?0:e?3:2};let o=null,r=0,a=0,l=null;for(const i of t){if(i.owner===e)continue;l=i;const t=n(i);t<=0||(t>r?(o=i,r=t,a=1):t===r&&a++)}return!o||a>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(s,this._warmGen,e,i);o&&(this._booting=!1,this._bootFading=!1,this._hdrH=o.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},kr)),this._warmKey=t,r?(clearTimeout(o.evict),o.evict=0,o.owner=this._warmGen,o.place=e?new WeakRef(e):null,o.idx=i,o.live=!0,this._warmSlot=o,this._warmVp=o.vp,this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,place:e?new WeakRef(e):null,idx:i,live:!0,hdrH:o.hdrH,stageH:o.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 n=$r.get(e);if(!t.live&&n&&n.length>1){const e=n.indexOf(t);e>=0&&n.splice(e,1)}},Mr+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=Sr(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=$r.get(i)||[];for(s.push(this._warmSlot),$r.set(i,s),this._warmTrim(s);$r.size>8;){const t=$r.keys().next().value;if(void 0===t||t===i)break;for(const e of $r.get(t)||[])clearTimeout(e.evict);$r.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._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._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>Mr)&&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"decorText":this._decorTextDialog={...e.data};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(Dr,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.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 Ns(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:n,excluded:o,iconRules:r}=t,a=!1!==n.group_lights,l=Vo(e,a),c=new Set(l.map(t=>t.area)),h=Oo(e),d=new Set(s.map(t=>t.binding)),p=[];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(d.has("device:"+t.id))continue;const n=h[t.id]||[],l=Io(e,t,n);let u=o.has(l)||"Group"===t.model||/scene/i.test(t.model||"")||/bridge/i.test((t.model||"")+(t.name||""))||"myheat"===l&&!!t.via_device_id;!u&&a&&c.has(s)&&"mdi:lightbulb"===Go(e,(t.name_by_user||t.name||"").trim(),t.model,n,r)&&(u=!0),u&&p.push("device:"+t.id)}return p}({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=pt(t)),this._rulesCompiled}get _fillColors(){return wi(this._settings)}get _excluded(){const t=this._settings.exclude_integrations;return t?new Set(t):ht}willUpdate(t){t?.has?.("hass")&&(this._vacTick(),this._senseTick()),this._skyPlan(),t.has("hass")&&this.hass&&(this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices())}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),n=Math.round(i+s);n>=0&&Math.abs(n-this._hdrH)>1&&(this._hdrH=n),n>=0&&!this._booting&&!this._config?.kiosk&&t.clientHeight>0&&this._warmPatch({hdrH:n,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._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null);const i=t?.config;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=>{(t?.data?.rev??-1)!==this._cfgRev&&this._reloadConfigOnly()},"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(),n=this._savedNav();!this._hashApplied&&s&&this._model.find(t=>t.id===s)?(this._space=s,this._hashApplied=!0):n?.space&&!this._navApplied&&!this._hashApplied&&this._model.find(t=>t.id===n.space)?(this._space=n.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(Cr)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1}this._regSignature="",this.requestUpdate()}async _reloadConfigOnly(t=!1){if(!t&&(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting))return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(),400));try{const t=await this.hass.callWS({type:"houseplan/config/get"}),e=t?.config;this._serverCfg=e&&Array.isArray(e.spaces)?e:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,"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,Fi(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._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")+":"+er(t,this._config?.language);e===this._regSignature&&this._devices.length||(this._regSignature=e,this._devices=Yo({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())}_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:n}=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:n,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=Ho(this.hass,i.entities);null!=s&&e.push(s)}return Ye(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),n=Math.min(...i);return{x:s,y:n,w:Math.max(...e)-s,h:Math.max(...i)-n}}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*sn(i)*1.3;for(const e of i.rooms){if(!e.area)continue;const n=this._devices.filter(t=>t.area===e.area&&t.space===i.id);if(!n.length)continue;const o=this._roomBounds(e),r=.1*Math.min(o.w,o.h),a=o.w-2*r,l=o.h-2*r,c=Math.max(1,Math.round(Math.sqrt(n.length*a/Math.max(l,1)))),h=Math.ceil(n.length/c),d=a/c,p=l/Math.max(h,1),u=n.map((t,e)=>({x:o.x+r+d*(e%c+.5),y:o.y+r+p*(Math.floor(e/c)+.5)}));Ze(u,o,s,.5*r),n.forEach((e,i)=>t[e.id]=Us(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*Ar,y:i.y*Ar}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[t.id]?this._defPos[t.id]:Us(en(e))}_savePos(t,e,i,s=!1){if(this._norm){const n=this._gridPitch,o=s?e:Math.round(e/n)*n,r=s?i:Math.round(i/n)*n,a=this._layout[t.id]?.k;this._layout={...this._layout,[t.id]:{s:t.space,x:Bs(o/Ar),y:Bs(r/Ar),...a?{k:a}:{}}}}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?ri(t.entities):null}_actEntity(t){return this._coverIndicator(t)||t.primary}_stateClass(t){if(!this._config?.live_states)return"";const e=this._coverIndicator(t);if(e){const t=this.hass.states[e];return t?"unavailable"===t.state?"unavail":ai(t.state)?"covermove":"":""}const i=(t.marker?.controls||[]).filter(Ai);if(i.length)return i.some(t=>"on"===this.hass.states[t]?.state)?"on":"";if(qo(this.hass,t))return"on";const s=this._actEntity(t),n=s?this.hass.states[s]:void 0;if(!n)return"";if("unavailable"===n.state)return"unavail";const o=s.split(".")[0];if(["light","switch","fan","humidifier"].includes(o))return"on"===n.state?"on":"";if("climate"===o){const t=n.attributes?.hvac_action;return null!=t?["heating","cooling","drying","fan"].includes(t)?"on":"":["off","unknown"].includes(n.state)?"":"on"}if("cover"===o)return ai(n.state)?"covermove":"";if("valve"===o)return["open","opening"].includes(n.state)?"open":"";if("lock"===o)return["unlocked","open"].includes(n.state)?"open":"";if("binary_sensor"===o){const e=n.attributes?.device_class;if(["door","window","garage_door","opening","gas","smoke","moisture","problem"].includes(e))return"on"===n.state?"open":"";if("motion"===e){const e=this._senseRt.get(t.id);return e&&e.flashTs&&Date.now()-e.flashTs<3300?e.gen%2==0?"senseflash sf2":"senseflash":""}if(["occupancy","presence"].includes(e))return"on"===n.state?"sensehold":""}return"media_player"===o?["playing","on"].includes(n.state)?"on":"":"vacuum"===o&&["cleaning","returning"].includes(n.state)?"on":""}_liveTemp(t){if(!this._config?.show_temperature)return null;if(!0===t.marker?.use_climate_temp){const e=Wo(this.hass,t.entities);if(null!=e)return e}return"mdi:thermometer"!==t.icon&&"mdi:air-filter"!==t.icon?null:Uo(this.hass,t.entities)}_bindingHasClimate(t){if(t.startsWith("entity:"))return t.slice(7).startsWith("climate.");if(t.startsWith("device:")){const e=t.slice(7);for(const[t,i]of Object.entries(this.hass?.entities||{}))if(i?.device_id===e&&t.startsWith("climate."))return!0}return!1}_bindingCoverEntity(t){if(t.startsWith("entity:"))return ri([t.slice(7)]);if(t.startsWith("device:")){const e=t.slice(7);return ri(Object.entries(this.hass?.entities||{}).filter(([,t])=>t?.device_id===e).map(([t])=>t))}return null}_bindingCoverTap(t){const e=this._bindingCoverEntity(t);if(!e)return!1;const i=String(this.hass?.states?.[e]?.attributes?.device_class||"");return!oi.has(i)}_liveHum(t){return this._config?.show_temperature&&t.primary&&Bo(this.hass,t.primary)?jo(this.hass,t.entities):null}_openMoreInfo(t){t?Er(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),n=s?s.split(".")[0]:null,o=(t,i)=>{e.marker?.tap_confirm?this._tapConfirm={text:t,exec:i}:i()},r=(e.marker?.controls||[]).filter(Ai);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 o(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 n=t||e||("light"===i?"toggle":"info");return"more-info"===n?"more-info":"run"===n?"run"===t?"run":"info":"cover"===n?"cover"!==t||"cover"!==i||oi.has(String(s||""))?"info":"cover":"toggle"!==n||!i||ni.has(i)?"info":"toggle"===t?"toggle":si.has(i)?"cover"===i&&oi.has(String(s||""))?"info":"toggle":"info"}(e.tapAction,void 0,n,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 n=s.attributes?.friendly_name||t;return void o(this._t("confirm.tap_run",{name:n}),()=>{this.hass.callService(i.domain,i.service,{entity_id:t}).then(()=>this._showToast(this._t("toast.run_started",{name:n}))).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 o(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?o(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 ir(er(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,n=Math.sin(i)*t.rlen/2,o=Gs([[t.rx-s,t.ry-n],[t.rx+s,t.ry+n]]);o&&e.push(o)}const t=this._decorH;for(const i of this._decorList){const s=Gs("line"===i.kind?[[i.x1*Ar,i.y1*t],[i.x2*Ar,i.y2*t]]:[[i.x*Ar,i.y*t],[(i.x+(i.w||0))*Ar,(i.y+(i.h||0))*t]]);s&&e.push(s)}}return Ys(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=Qs(this._contentItems(t));let n=s.all||tn(t),o=this._showFar?n:s.core||tn(t);return e&&e.id===t.id&&i&&e.grow&&(o=Rr(e.rect,o),n=Rr(e.all,n)),this._frame={id:t.id,model:t,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:n,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._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?V:W`
${this._t("canvas.far_objects",{n:this._outliers})} @@ -2635,7 +2635,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow style="left:${s.toFixed(1)}%;top:${n.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:Xe(t,this._stageAspect())}_screenToVb(t,e){const i=this._stageEl,s=this._viewOr(this._baseVb()),n=i?.clientWidth||1,o=i?.clientHeight||1;return[s.x+t/n*s.w,s.y+e/o*s.h]}_clampView(t,e){const i=(t,e,i,s)=>{const n=1*Math.max(e,s),o=i-n,r=i+s-e+n;return Math.max(Math.min(o,r),Math.min(Math.max(o,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(),n=Xe(s,this._stageAspect()),o=Math.min(Or.ZOOM_MAX,Math.max(Or.ZOOM_MIN,t)),r=n.w/o,a=n.h/o,l=this._viewOr(s),c=e??l.x+l.w/2,h=i??l.y+l.h/2;this._zoom=o,this._view=this._clampView({x:c-r/2,y:h-a/2,w:r,h:a},n)}_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},wr)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_refitView(){if(!this._stageEl)return;const t=this._view;this._applyView(this._zoom,t?t.x+t.w/2:void 0,t?t.y+t.h/2:void 0),this.requestUpdate()}_zoomAt(t,e,i){const s=this._stageEl;if(!s)return;const n=Xe(this._baseVb(),this._stageAspect()),o=Math.min(Or.ZOOM_MAX,Math.max(Or.ZOOM_MIN,i)),r=s.clientWidth,a=s.clientHeight,l=this._screenToVb(t,e),c=n.w/o,h=n.h/o;this._zoom=o,this._view=this._clampView({x:l[0]-t/r*c,y:l[1]-e/a*h,w:c,h:h},n)}_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=Xe(t,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(Dr,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._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId)if(this._decorDraft?.pid!==t.pointerId)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,n=(t[0].y+t[1].y)/2-i.top;this._zoomAt(s,n,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(),n=this._viewOr(t),o=Xe(t,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-e/(s.clientWidth||1)*n.w,y:this._panStart.vy-i/(s.clientHeight||1)*n.h,w:n.w,h:n.h},o)}}}else this._markupMove(t);else this._decorMoveUpdate(t);else this._decorDraft={...this._decorDraft,b:this._snap(this._svgPoint(t),t)};else this._bdMove(t);else this._dtMove(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 n="pan"===this._panLock?null:function(t,e,i,s,n,o=60){if(i>1.001||s.length<2)return null;if(Math.abs(t)t.id),this._space);n&&(this._slideTo(n,i<0?"left":"right"),this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}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._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()}_clickRoom(t){var e;!this._suppressClick&&t.area&&(e="/config/areas/area/"+t.area,history.pushState(null,"",e),Rr(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},Nr(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(),n=i.getBoundingClientRect(),o=this._viewOr(s),r=(t.clientX-this._drag.sx)/n.width*o.w,a=(t.clientY-this._drag.sy)/n.height*o.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=Ws(this._drag.ox+r),c=Ws(this._drag.oy+a);this._savePos(e,l,c,t.shiftKey)}_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 Or._noHoverMq||Or._touchSeen}_notePointer(t){"touch"!==t.pointerType&&"pen"!==t.pointerType||(Or._touchSeen=!0,this._tip&&(this._tip=null))}_showTip(t,e,i,s,n){this._noHover||this._drag||(this._tip={x:t.clientX,y:t.clientY,title:e,meta:i,lqi:s,temp:n})}get _gridPitch(){return Ls}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 $e(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,Pr}get _segments(){const t=this._curSpaceCfg,e=this._spaceH;return Ce(t?.rooms||[]).map(t=>[t[0]*Pr,t[1]*e,t[2]*Pr,t[3]*e])}_savedNav(){try{return JSON.parse(localStorage.getItem(Tr)||"null")}catch{return null}}_saveNav(){try{localStorage.setItem(Tr,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._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(this._mode=t,e&&(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._rszSel=null,this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._rszUndo=[],this._tip=null,this._decorDraft=null,this._decorSel=null,this._decorTool="decor"===t&&this._curSpaceCfg?.plan_url?"backdrop":"select",this._bdDrag=null,this._dtDrag=null,this._dtBox=null,"plan"===t&&this._primeDrawWallField(),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=kn(15,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?kn(15,this._imperial):this._drawWallField}get _drawWallCm(){return $n(this._drawWallFieldValue,this._imperial)}_svgPoint(t){const e=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t,e=!1){const i="boolean"==typeof e?e:!!e?.shiftKey,s=this._gridPitch;return i?[Ws(t[0]),Ws(t[1])]:[Ws(ke(t[0],s)),Ws(ke(t[1],s))]}_samePt(t,e){return Pe(t,e)}_dropLegacySegments(){for(const t of this._serverCfg?.spaces||[])if(delete t.segments,Array.isArray(t.walls)){const e=lo(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=En(t.walls,t.rooms||[],Hs,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()}_roomAt(t){return this._spaceModel().rooms.find(e=>{const i=Se(e);return!!i&&Oe(t,i)})}_overlapRoom(t){return this._spaceModel().rooms.find(e=>{const i=Se(e);return!!i&&We(t,i)})}_pointInRoom(t,e){return e.poly?Ae(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}_markupClick(t){if(this._vacFit)return;if(!this._markup)return;if(this._suppressClick)return;if(this._drag||this._rlResize)return;const e=t.composedPath?.()||[];if(e.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;const i=this._svgPoint(t);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._deleteWallClick(i);if("opening"===this._tool)return void this._openingClick(i,t.shiftKey);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("split"===this._tool)return void this._splitClick(i,t.shiftKey);const s=this._snap(i,t),n=this._path.length>=3&&this._samePt(s,this._path[0]);if(!this._path.length)return void(this._path=[s]);const o=this._path[this._path.length-1];if(!this._samePt(s,o)){if(n){const t=this._overlapRoom(this._path);return t?void this._showToast(this._t("toast.room_overlap",{name:t.name||""})):(this._path=[...this._path,s],this._cursorPt=null,this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),void(this._roomDialog=!0))}this._path=[...this._path,s]}}_rszRooms(){const t=[];for(const e of this._spaceModel().rooms){const i=e.id?Se(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(){const t=this._curSpaceCfg;return JSON.stringify({rooms:t?.rooms||[],openings:t?.openings||[],walls:t?.walls||[],open_spans:t?.open_spans||[]})}_rszRestore(t){const e=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!e)return;const i=JSON.parse(t);e.rooms=i.rooms,e.openings=i.openings,Array.isArray(i.walls)&&i.walls.length?e.walls=i.walls:delete e.walls,Array.isArray(i.open_spans)&&i.open_spans.length?e.open_spans=i.open_spans:delete e.open_spans,this._cfgEpoch++,this.requestUpdate()}_rszApplyPreview(t,e){const i=this._rszDrag,s=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!i||!s)return;const n=JSON.parse(i.snap),o={...s,rooms:n.rooms,openings:n.openings,walls:n.walls},r=this._spaceH;for(const[e,i]of Object.entries(t)){const t=o.rooms.find(t=>t.id===e);t&&(t.poly=i.map(t=>[t[0]/Pr,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=(o.openings||[]).find(e=>e.id===t);e&&(e.x=i[0]/Pr,e.y=i[1]/r)}this._rszPreview={space:this._space,sp:o},this._cfgEpoch++}_rszEdgeDown(t,e,i){if("resize"!==this._tool||this._rszDrag)return;t.stopPropagation(),t.preventDefault(),Nr(t);const s=this._rszRooms(),n=function(t,e,i){const s=t.find(t=>t.id===e);if(!s||!s.poly||s.poly.length<3)return null;if(i<0||i>=s.poly.length)return null;const n=[...s.poly[i]],o=[...s.poly[(i+1)%s.poly.length]];return{roomId:e,edge:i,a:n,b:o,n:Ji(s.poly,i)}}(s,e,i);n&&(this._rszDrag={kind:"edge",pid:t.pointerId,roomId:e,plan:n,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(),Nr(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 s=e.plan,n=(i[0]-s.a[0])*s.n[0]+(i[1]-s.a[1])*s.n[1],o=this._snap([s.a[0]+s.n[0]*n,s.a[1]+s.n[1]*n],t);let r=(o[0]-s.a[0])*s.n[0]+(o[1]-s.a[1])*s.n[1];if(r=function(t,e,i,s,n,o){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(n,1e-6);for(let s=0;s<4096&&a>1e-9;s++,a-=l){const s=r*a;if(cs(t,e,i,s,o))return s}return 0}(e.rooms,e.openings,s,r,this._gridPitch,this._rszOpts()),r===e.d&&e.moved)return;e.d=r,e.moved=!0;const a=rs(e.rooms,e.openings,s,r,this._rszOpts().eps);e.changed=Object.keys(a.polys),this._rszApplyPreview(a.polys,a.openings),this._rszLive=this._rszEdgeLabels(a,s)}else{const s=e.fixed,n=this._snap(i,t);let o=Math.hypot(n[0]-s[0],n[1]-s[1])/(e.span0||1);if(o=Math.max(.05,Math.min(20,o)),o=function(t,e,i,s,n,o){if(!Number.isFinite(n)||n<=0)return 1;if(ds(t,e,i,s,n,o))return n;let r=1,a=n;for(let n=0;n<28;n++){const n=(r+a)/2;ds(t,e,i,s,n,o)?r=n:a=n}return r}(e.rooms,e.openings,e.roomId,s,o,this._rszOpts()),o===e.k&&e.moved)return;e.k=o,e.moved=!0;const r=e.rooms.find(t=>t.id===e.roomId),a=e.rooms.filter(t=>t.id!==e.roomId).map(t=>t.poly),l=hs(r,e.openings,a,s,o,2*this._rszOpts().eps);e.changed=[e.roomId],this._rszApplyPreview({[e.roomId]:l.poly},l.openings),this._rszLive=this._rszScaleLabels(l.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=this._curSpaceCfg;if(s){s.rooms=i.sp.rooms,s.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?s.walls=i.sp.walls:delete s.walls);for(const t of e.changed){const e=s.rooms.find(e=>e.id===t);e?.poly&&(e.poly=is(e.poly,1e-9))}const t=this._spaceH,n=[],o=[];for(const i of e.changed){const r=e.rooms.find(t=>t.id===i),a=s.rooms.find(t=>t.id===i);if(!r||!a?.poly)continue;const l=a.poly.map(e=>[e[0]*Pr,e[1]*t]);if(r.poly.length===l.length)for(let t=0;t30&&this._rszUndo.shift(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),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())}_rszUndoPop(){for(let t=this._rszUndo.length-1;t>=0;t--){if(this._rszUndo[t].space!==this._space)continue;const[e]=this._rszUndo.splice(t,1);return this._rszRestore(e.snap),void this._saveConfig()}}_rszEdgeLabels(t,e){const i=this._rszDrag,s=[],n=t.polys[e.roomId]||i.rooms.find(t=>t.id===e.roomId).poly,o=n.length,r=e.edge,a=(r+1)%o;for(const[t,e]of[[n[(r-1+o)%o],n[r]],[n[r],n[a]],[n[a],n[(a+1)%o]]])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,c=Object.keys(t.polys).length?Object.keys(t.polys):[e.roomId],h=this._spaceWalls,d=this._openPairs().flatMap(t=>t.segs);for(const e of c){const n=t.polys[e]||i.rooms.find(t=>t.id===e).poly,o=h.length&&Qn(Object.entries(t.polys).map(([t,e])=>({id:t,poly:e})),e,h,d,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)||n,r=Le(o);s.push({x:r[0],y:r[1],text:us(ps(o,this._gridPitch,this._cellCm),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]),n=Math.max(...i)-Math.min(...i),o=Math.max(...s)-Math.min(...s),r=this._spaceWalls,a=r.length&&this._rszSel&&Qn([{id:this._rszSel,poly:t}],this._rszSel,r,[],this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)||t,l=Le(a);return[{x:Math.min(...i),y:Math.min(...s),text:`${this._fmtLen([0,0],[n,0])} × ${this._fmtLen([0,0],[o,0])}`},{x:l[0],y:l[1],text:us(ps(a,this._gridPitch,this._cellCm),e),area:!0}]}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),i=e/2,s=t=>t.toFixed(1),n=`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)}`,o=[],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:Xe(t,this._stageAspect())}_screenToVb(t,e){const i=this._stageEl,s=this._viewOr(this._baseVb()),n=i?.clientWidth||1,o=i?.clientHeight||1;return[s.x+t/n*s.w,s.y+e/o*s.h]}_clampView(t,e){const i=(t,e,i,s)=>{const n=1*Math.max(e,s),o=i-n,r=i+s-e+n;return Math.max(Math.min(o,r),Math.min(Math.max(o,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(),n=Xe(s,this._stageAspect()),o=Math.min(Ir.ZOOM_MAX,Math.max(Ir.ZOOM_MIN,t)),r=n.w/o,a=n.h/o,l=this._viewOr(s),c=e??l.x+l.w/2,h=i??l.y+l.h/2;this._zoom=o,this._view=this._clampView({x:c-r/2,y:h-a/2,w:r,h:a},n)}_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},kr)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_refitView(){if(!this._stageEl)return;const t=this._view;this._applyView(this._zoom,t?t.x+t.w/2:void 0,t?t.y+t.h/2:void 0),this.requestUpdate()}_zoomAt(t,e,i){const s=this._stageEl;if(!s)return;const n=Xe(this._baseVb(),this._stageAspect()),o=Math.min(Ir.ZOOM_MAX,Math.max(Ir.ZOOM_MIN,i)),r=s.clientWidth,a=s.clientHeight,l=this._screenToVb(t,e),c=n.w/o,h=n.h/o;this._zoom=o,this._view=this._clampView({x:l[0]-t/r*c,y:l[1]-e/a*h,w:c,h:h},n)}_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=Xe(t,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(Tr,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._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId)if(this._decorDraft?.pid!==t.pointerId)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,n=(t[0].y+t[1].y)/2-i.top;this._zoomAt(s,n,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(),n=this._viewOr(t),o=Xe(t,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-e/(s.clientWidth||1)*n.w,y:this._panStart.vy-i/(s.clientHeight||1)*n.h,w:n.w,h:n.h},o)}}}else this._markupMove(t);else this._decorMoveUpdate(t);else this._decorDraft={...this._decorDraft,b:this._snap(this._svgPoint(t),t)};else this._bdMove(t);else this._dtMove(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 n="pan"===this._panLock?null:function(t,e,i,s,n,o=60){if(i>1.001||s.length<2)return null;if(Math.abs(t)t.id),this._space);n&&(this._slideTo(n,i<0?"left":"right"),this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}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._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()}_clickRoom(t){var e;!this._suppressClick&&t.area&&(e="/config/areas/area/"+t.area,history.pushState(null,"",e),Er(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},Or(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(),n=i.getBoundingClientRect(),o=this._viewOr(s),r=(t.clientX-this._drag.sx)/n.width*o.w,a=(t.clientY-this._drag.sy)/n.height*o.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=Ws(this._drag.ox+r),c=Ws(this._drag.oy+a);this._savePos(e,l,c,t.shiftKey)}_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 Ir._noHoverMq||Ir._touchSeen}_notePointer(t){"touch"!==t.pointerType&&"pen"!==t.pointerType||(Ir._touchSeen=!0,this._tip&&(this._tip=null))}_showTip(t,e,i,s,n){this._noHover||this._drag||(this._tip={x:t.clientX,y:t.clientY,title:e,meta:i,lqi:s,temp:n})}get _gridPitch(){return Ls}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 $e(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,Ar}get _segments(){const t=this._curSpaceCfg,e=this._spaceH;return Ce(t?.rooms||[]).map(t=>[t[0]*Ar,t[1]*e,t[2]*Ar,t[3]*e])}_savedNav(){try{return JSON.parse(localStorage.getItem(zr)||"null")}catch{return null}}_saveNav(){try{localStorage.setItem(zr,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._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(this._mode=t,e&&(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._rszSel=null,this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._rszUndo=[],this._tip=null,this._decorDraft=null,this._decorSel=null,this._decorTool="decor"===t&&this._curSpaceCfg?.plan_url?"backdrop":"select",this._bdDrag=null,this._dtDrag=null,this._dtBox=null,"plan"===t&&this._primeDrawWallField(),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=kn(15,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?kn(15,this._imperial):this._drawWallField}get _drawWallCm(){return $n(this._drawWallFieldValue,this._imperial)}_svgPoint(t){const e=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t,e=!1){const i="boolean"==typeof e?e:!!e?.shiftKey,s=this._gridPitch;return i?[Ws(t[0]),Ws(t[1])]:[Ws(ke(t[0],s)),Ws(ke(t[1],s))]}_samePt(t,e){return Pe(t,e)}_dropLegacySegments(){for(const t of this._serverCfg?.spaces||[])if(delete t.segments,Array.isArray(t.walls)){const e=lo(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=En(t.walls,t.rooms||[],qs,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()}_roomAt(t){return this._spaceModel().rooms.find(e=>{const i=Se(e);return!!i&&Oe(t,i)})}_overlapRoom(t){return this._spaceModel().rooms.find(e=>{const i=Se(e);return!!i&&We(t,i)})}_pointInRoom(t,e){return e.poly?Ae(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}_markupClick(t){if(this._vacFit)return;if(!this._markup)return;if(this._suppressClick)return;if(this._drag||this._rlResize)return;const e=t.composedPath?.()||[];if(e.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;const i=this._svgPoint(t);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._deleteWallClick(i);if("opening"===this._tool)return void this._openingClick(i,t.shiftKey);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("split"===this._tool)return void this._splitClick(i,t.shiftKey);const s=this._snap(i,t),n=this._path.length>=3&&this._samePt(s,this._path[0]);if(!this._path.length)return void(this._path=[s]);const o=this._path[this._path.length-1];if(!this._samePt(s,o)){if(n){const t=this._overlapRoom(this._path);return t?void this._showToast(this._t("toast.room_overlap",{name:t.name||""})):(this._path=[...this._path,s],this._cursorPt=null,this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),void(this._roomDialog=!0))}this._path=[...this._path,s]}}_rszRooms(){const t=[];for(const e of this._spaceModel().rooms){const i=e.id?Se(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(){const t=this._curSpaceCfg;return JSON.stringify({rooms:t?.rooms||[],openings:t?.openings||[],walls:t?.walls||[],open_spans:t?.open_spans||[]})}_rszRestore(t){const e=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!e)return;const i=JSON.parse(t);e.rooms=i.rooms,e.openings=i.openings,Array.isArray(i.walls)&&i.walls.length?e.walls=i.walls:delete e.walls,Array.isArray(i.open_spans)&&i.open_spans.length?e.open_spans=i.open_spans:delete e.open_spans,this._cfgEpoch++,this.requestUpdate()}_rszApplyPreview(t,e){const i=this._rszDrag,s=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!i||!s)return;const n=JSON.parse(i.snap),o={...s,rooms:n.rooms,openings:n.openings,walls:n.walls,open_spans:n.open_spans};Array.isArray(n.open_spans)&&n.open_spans.length||delete o.open_spans;const r=this._spaceH;for(const[e,i]of Object.entries(t)){const t=o.rooms.find(t=>t.id===e);t&&(t.poly=i.map(t=>[t[0]/Ar,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=(o.openings||[]).find(e=>e.id===t);e&&(e.x=i[0]/Ar,e.y=i[1]/r)}const a=[],l=[];for(const t of i.changed){const e=i.rooms.find(e=>e.id===t),s=o.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const n=s.poly.map(t=>[t[0]*Ar,t[1]*r]);if(e.poly.length===n.length)for(let t=0;t0?n:1,a=Math.max(.5*s,1e-9)*r,l=new Set,c=[];for(const n of t){let t=o.get(n.key)||"";if(!t){const o=Tn([n],r)[0];if(o)for(let n=0;n1.000001)continue;if(Jn(o.x,o.y,l[0],l[1],c[0],c[1])>a)continue;const m=h[0]+(d[0]-h[0])*Math.max(0,Math.min(1,g)),f=h[1]+(d[1]-h[1])*Math.max(0,Math.min(1,g)),[v,b]=Mn(h,d),y=Math.max(s*r,1e-6);t=Dn([m-v*y,f-b*y],[m+v*y,f+b*y],s,r);break}}t||(t=n.key),l.has(t)||(l.add(t),c.push({key:t,cm:n.cm}))}return c}(o.walls,a,l,this._wallKeyPitch,Ar))}this._rszPreview={space:this._space,sp:o},this._cfgEpoch++}_rszEdgeDown(t,e,i){if("resize"!==this._tool||this._rszDrag)return;t.stopPropagation(),t.preventDefault(),Or(t);const s=this._rszRooms(),n=function(t,e,i){const s=t.find(t=>t.id===e);if(!s||!s.poly||s.poly.length<3)return null;if(i<0||i>=s.poly.length)return null;const n=[...s.poly[i]],o=[...s.poly[(i+1)%s.poly.length]];return{roomId:e,edge:i,a:n,b:o,n:Ji(s.poly,i)}}(s,e,i);n&&(this._rszDrag={kind:"edge",pid:t.pointerId,roomId:e,plan:n,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(),Or(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 s=e.plan,n=(i[0]-s.a[0])*s.n[0]+(i[1]-s.a[1])*s.n[1],o=this._snap([s.a[0]+s.n[0]*n,s.a[1]+s.n[1]*n],t);let r=(o[0]-s.a[0])*s.n[0]+(o[1]-s.a[1])*s.n[1];if(r=function(t,e,i,s,n,o){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(n,1e-6);for(let s=0;s<4096&&a>1e-9;s++,a-=l){const s=r*a;if(cs(t,e,i,s,o))return s}return 0}(e.rooms,e.openings,s,r,this._gridPitch,this._rszOpts()),r===e.d&&e.moved)return;e.d=r,e.moved=!0;const a=rs(e.rooms,e.openings,s,r,this._rszOpts().eps);e.changed=Object.keys(a.polys),this._rszApplyPreview(a.polys,a.openings),this._rszLive=this._rszEdgeLabels(a,s)}else{const s=e.fixed,n=this._snap(i,t);let o=Math.hypot(n[0]-s[0],n[1]-s[1])/(e.span0||1);if(o=Math.max(.05,Math.min(20,o)),o=function(t,e,i,s,n,o){if(!Number.isFinite(n)||n<=0)return 1;if(ds(t,e,i,s,n,o))return n;let r=1,a=n;for(let n=0;n<28;n++){const n=(r+a)/2;ds(t,e,i,s,n,o)?r=n:a=n}return r}(e.rooms,e.openings,e.roomId,s,o,this._rszOpts()),o===e.k&&e.moved)return;e.k=o,e.moved=!0;const r=e.rooms.find(t=>t.id===e.roomId),a=e.rooms.filter(t=>t.id!==e.roomId).map(t=>t.poly),l=hs(r,e.openings,a,s,o,2*this._rszOpts().eps);e.changed=[e.roomId],this._rszApplyPreview({[e.roomId]:l.poly},l.openings),this._rszLive=this._rszScaleLabels(l.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=this._curSpaceCfg;if(s){s.rooms=i.sp.rooms,s.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?s.walls=i.sp.walls:delete s.walls),Array.isArray(i.sp.open_spans)&&i.sp.open_spans.length?s.open_spans=i.sp.open_spans:delete s.open_spans;for(const t of e.changed){const e=s.rooms.find(e=>e.id===t);e?.poly&&(e.poly=is(e.poly,1e-9))}this._commitOpenSpans(),Array.isArray(s.walls)&&s.walls.length&&(s.walls=En(s.walls,s.rooms||[],qs,1,this._cfgOpenCuts()),s.walls.length||delete s.walls)}this._rszUndo.push({space:this._space,snap:e.snap}),this._rszUndo.length>30&&this._rszUndo.shift(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),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())}_rszUndoPop(){for(let t=this._rszUndo.length-1;t>=0;t--){if(this._rszUndo[t].space!==this._space)continue;const[e]=this._rszUndo.splice(t,1);return this._rszRestore(e.snap),void this._saveConfig()}}_rszEdgeLabels(t,e){const i=this._rszDrag,s=[],n=t.polys[e.roomId]||i.rooms.find(t=>t.id===e.roomId).poly,o=n.length,r=e.edge,a=(r+1)%o;for(const[t,e]of[[n[(r-1+o)%o],n[r]],[n[r],n[a]],[n[a],n[(a+1)%o]]])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,c=Object.keys(t.polys).length?Object.keys(t.polys):[e.roomId],h=this._spaceWalls,d=this._openPairs().flatMap(t=>t.segs);for(const e of c){const n=t.polys[e]||i.rooms.find(t=>t.id===e).poly,o=h.length&&Qn(Object.entries(t.polys).map(([t,e])=>({id:t,poly:e})),e,h,d,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)||n,r=Le(o);s.push({x:r[0],y:r[1],text:us(ps(o,this._gridPitch,this._cellCm),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]),n=Math.max(...i)-Math.min(...i),o=Math.max(...s)-Math.min(...s),r=this._spaceWalls,a=r.length&&this._rszSel&&Qn([{id:this._rszSel,poly:t}],this._rszSel,r,[],this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)||t,l=Le(a);return[{x:Math.min(...i),y:Math.min(...s),text:`${this._fmtLen([0,0],[n,0])} × ${this._fmtLen([0,0],[o,0])}`},{x:l[0],y:l[1],text:us(ps(a,this._gridPitch,this._cellCm),e),area:!0}]}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),i=e/2,s=t=>t.toFixed(1),n=`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)}`,o=[],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)} @@ -2645,7 +2645,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)}>`),o.push(B``)}return B`${o}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH;return(t?.openings||[]).map(t=>({...t,rx:t.x*Pr,ry:t.y*e,rlen:t.length*Pr}))}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return Pr}_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._snap(this._svgPoint(t),t);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},Nr(t),!0}if("text"===e){const e=this._snap(this._svgPoint(t),t);return this._decorTextDialog={x:Bs(e[0]/Pr),y:Bs(e[1]/this._decorH),text:"",color:this._decorStyle.color},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t),!0);if(this._decorSel=null,this._bdMovable){const e=this._bdRect,i=this._svgPoint(t);if(i[0]>=e.x&&i[0]<=e.x+e.w&&i[1]>=e.y&&i[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(Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])t.id!==e.id),this._decorSel===e.id&&(this._decorSel=null),this._saveConfig(),void this.requestUpdate()}this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1},Nr(t)}}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,n=("line"===s.kind?s.x1:s.x)*Pr,o=("line"===s.kind?s.y1:s.y)*this._decorH,r=this._snap([n+(i[0]-e.start[0]),o+(i[1]-e.start[1])],t);let a=(r[0]-n)/Pr,l=(r[1]-o)/this._decorH;const c=e.orig,h="line"===c.kind?Math.min(c.x1,c.x2):c.x,d="line"===c.kind?Math.min(c.y1,c.y2):c.y,p="line"===c.kind?Math.abs(c.x2-c.x1):c.w||0,u="line"===c.kind?Math.abs(c.y2-c.y1):c.h||0,_=Os;a=Math.max(-_-h,Math.min(_-h-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){"decor"===this._mode&&"select"===this._decorTool&&"text"===t.kind&&this._decorOpenText(t)}_decorOpenText(t){this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:t.text,color:t.color,entity:t.entity||"",attr:t.attr||"",unit:t.unit||""}}_decorTextLink(t){const e={},i=(t.entity||"").trim();if(!i)return e;e.entity=i;const s=(t.attr||"").trim();s&&(e.attr=s);const n=(t.unit||"").trim();return n&&(e.unit=n),e}_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._curSpaceCfg,s=this._decorTextLink(t);if(t.id)i.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;const{entity:n,attr:o,unit:r,...a}=i;return{...a,text:e,color:t.color,...s}});else{const n="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);i.decor=[...this._decorList,{id:n,kind:"text",x:t.x,y:t.y,text:e,color:t.color,...s}],this._decorSel=n}this._decorTextDialog=null,this._saveConfig(),this.requestUpdate()}get _dtSel(){if("decor"!==this._mode||"select"!==this._decorTool||!this._decorSel)return null;const t=this._decorList.find(t=>t.id===this._decorSel);return!t||"text"!==t.kind&&"furniture"!==t.kind?null:t}_dtPivot(t){return"furniture"===t.kind?[(t.x+t.w/2)*Pr,(t.y+t.h/2)*this._decorH]:[t.x*Pr,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{size:s,...n}=i,o={...n};return void 0!==e.scale?o.scale=Number(e.scale.toFixed(4)):void 0===i.scale&&void 0!==s&&(o.scale=pi(i)),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){const s=this._dtSel;if(!s)return;t.stopPropagation(),t.preventDefault();const[n,o]=this._dtPivot(s),r=this._svgPoint(t),a="furniture"===s.kind;this._dtDrag={id:s.id,kind:e,pid:t.pointerId,ax:n,ay:o,r0:Math.hypot(r[0]-n,r[1]-o),a0:180*Math.atan2(r[1]-o,r[0]-n)/Math.PI,scale0:a?1:pi(s),angle0:Number(s.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:a?{x:s.x*Pr,y:s.y*this._decorH,w:s.w*Pr,h:s.h*this._decorH,angle:Number(s.angle)||0}:void 0,moved:!1},Nr(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if("scale"===e.kind&&e.orig){const s=t.shiftKey?0:this._gridPitch,n=s>0?s:.25*this._gridPitch,o=function(t,e,i,s,n,o=0,r=1e-6){const a=(Number(t.angle)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=-Math.sin(a),d=Math.cos(a),p=t.x+t.w/2,u=t.y+t.h/2,_=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,m=p+_*l+g*h,f=u+_*c+g*d,v=s-m,b=n-f;let y=(v*l+b*c)*(e>0?1:-1),w=(v*h+b*d)*(i>0?1:-1);return o>0&&(y=Math.round(y/o)*o,w=Math.round(w/o)*o),y=Math.max(r,y),w=Math.max(r,w),{x:m+(e>0?y/2:-y/2)*l+(i>0?w/2:-w/2)*h-y/2,y:f+(e>0?y/2:-y/2)*c+(i>0?w/2:-w/2)*d-w/2,w:y,h:w}}(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],s,n);return(Math.abs(o.w-e.orig.w)>1e-6||Math.abs(o.h-e.orig.h)>1e-6)&&(e.moved=!0),void this._furnApplyBox(e.id,o)}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.min(20,Math.max(.15,e.scale0*(t/e.r0)));return Math.abs(s-e.scale0)>1e-6&&(e.moved=!0),void this._dtApply(e.id,{scale:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let n=e.angle0+(s-e.a0);t.shiftKey||(n=5*Math.round(n/5)),n=(n%360+360)%360,n>180&&(n-=360),Math.abs(n-e.angle0)>1e-6&&(e.moved=!0),this._dtApply(e.id,{angle:n})}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&this._saveConfig(),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("furniture"===t.kind)e={id:t.id,x:t.x*Pr,y:t.y*this._decorH,w:t.w*Pr,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())}_decorDeleteSel(){if(!this._decorSel)return;this._curSpaceCfg.decor=this._decorList.filter(t=>t.id!==this._decorSel),this._decorSel=null,this._saveConfig(),this.requestUpdate()}get _furnWalls(){return this._segments}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=dn(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){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const n=Pr,o=this._decorH,r=gn(pn(i.w,this._cellCm,this._gridPitch,n)),a=gn(pn(i.h,this._cellCm,this._gridPitch,n)),l=this._snap(t,e);let c=l[0],h=l[1],d=0;if(!e.shiftKey){const t=bn(c,h,a*o,this._furnWalls,this._furnWallReach,this._gridPitch);t&&(c=t.cx,h=t.cy,d=t.angle)}const p="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),u={id:p,kind:"furniture",symbol:i.symbol,x:Bs(c/n-r/2),y:Bs(h/o-a/2),w:r,h:a,color:this._decorStyle.color,width:this._decorStyle.width};d&&(u.angle=Number(d.toFixed(2))),s.decor=[...this._decorList,u],this._decorSel=p,this._decorTool="select",this._furnPalette=null,this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(t){const e=this._decorMove,i=e.orig,s=this._curSpaceCfg;if(!s)return;const n=Pr,o=this._decorH,r=this._svgPoint(t),a=(i.x+i.w/2)*n+(r[0]-e.start[0]),l=(i.y+i.h/2)*o+(r[1]-e.start[1]);let c,h,d=Number(i.angle)||0;const p=t.shiftKey?null:bn(a,l,i.h*o,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)c=p.cx/n-i.w/2,h=p.cy/o-i.h/2,d=p.angle;else{const e=this._snap([a-i.w/2*n,l-i.h/2*o],t);c=e[0]/n,h=e[1]/o}c=Bs(c),h=Bs(h),(Math.abs(c-i.x)>1e-9||Math.abs(h-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:c,y:h};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_furnApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=Pr,n=this._decorH;i.decor=this._decorList.map(i=>i.id===t?{...i,x:Bs(e.x/s),y:Bs(e.y/n),w:gn(e.w/s),h:gn(e.h/n)}:i),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=Pr,s=this._decorH,n=e.w*i,o=e.h*s,r=function(t,e,i,s,n){const o=t+i/2,r=e+s/2,a=(Number(n)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=(t,e)=>{const i=t-o,s=e-r;return[o+i*l-s*c,r+i*c+s*l]};return[h(t,e),h(t+i,e),h(t+i,e+s),h(t,e+s)]}(e.x*i,e.y*s,n,o,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],l=a(r[0],r[1]),c=a(r[0],r[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[n,0])},{x:c[0],y:c[1],text:this._fmtLen([0,0],[0,o])}]}_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)}>`),o.push(B``)}return B`${o}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH;return(t?.openings||[]).map(t=>({...t,rx:t.x*Ar,ry:t.y*e,rlen:t.length*Ar}))}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return Ar}_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._snap(this._svgPoint(t),t);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},Or(t),!0}if("text"===e){const e=this._snap(this._svgPoint(t),t);return this._decorTextDialog={x:Bs(e[0]/Ar),y:Bs(e[1]/this._decorH),text:"",color:this._decorStyle.color},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t),!0);if(this._decorSel=null,this._bdMovable){const e=this._bdRect,i=this._svgPoint(t);if(i[0]>=e.x&&i[0]<=e.x+e.w&&i[1]>=e.y&&i[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(Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])t.id!==e.id),this._decorSel===e.id&&(this._decorSel=null),this._saveConfig(),void this.requestUpdate()}this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1},Or(t)}}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,n=("line"===s.kind?s.x1:s.x)*Ar,o=("line"===s.kind?s.y1:s.y)*this._decorH,r=this._snap([n+(i[0]-e.start[0]),o+(i[1]-e.start[1])],t);let a=(r[0]-n)/Ar,l=(r[1]-o)/this._decorH;const c=e.orig,h="line"===c.kind?Math.min(c.x1,c.x2):c.x,d="line"===c.kind?Math.min(c.y1,c.y2):c.y,p="line"===c.kind?Math.abs(c.x2-c.x1):c.w||0,u="line"===c.kind?Math.abs(c.y2-c.y1):c.h||0,_=Os;a=Math.max(-_-h,Math.min(_-h-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){"decor"===this._mode&&"select"===this._decorTool&&"text"===t.kind&&this._decorOpenText(t)}_decorOpenText(t){this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:t.text,color:t.color,entity:t.entity||"",attr:t.attr||"",unit:t.unit||""}}_decorTextLink(t){const e={},i=(t.entity||"").trim();if(!i)return e;e.entity=i;const s=(t.attr||"").trim();s&&(e.attr=s);const n=(t.unit||"").trim();return n&&(e.unit=n),e}_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._curSpaceCfg,s=this._decorTextLink(t);if(t.id)i.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;const{entity:n,attr:o,unit:r,...a}=i;return{...a,text:e,color:t.color,...s}});else{const n="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);i.decor=[...this._decorList,{id:n,kind:"text",x:t.x,y:t.y,text:e,color:t.color,...s}],this._decorSel=n}this._decorTextDialog=null,this._saveConfig(),this.requestUpdate()}get _dtSel(){if("decor"!==this._mode||"select"!==this._decorTool||!this._decorSel)return null;const t=this._decorList.find(t=>t.id===this._decorSel);return!t||"text"!==t.kind&&"furniture"!==t.kind?null:t}_dtPivot(t){return"furniture"===t.kind?[(t.x+t.w/2)*Ar,(t.y+t.h/2)*this._decorH]:[t.x*Ar,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{size:s,...n}=i,o={...n};return void 0!==e.scale?o.scale=Number(e.scale.toFixed(4)):void 0===i.scale&&void 0!==s&&(o.scale=pi(i)),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){const s=this._dtSel;if(!s)return;t.stopPropagation(),t.preventDefault();const[n,o]=this._dtPivot(s),r=this._svgPoint(t),a="furniture"===s.kind;this._dtDrag={id:s.id,kind:e,pid:t.pointerId,ax:n,ay:o,r0:Math.hypot(r[0]-n,r[1]-o),a0:180*Math.atan2(r[1]-o,r[0]-n)/Math.PI,scale0:a?1:pi(s),angle0:Number(s.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:a?{x:s.x*Ar,y:s.y*this._decorH,w:s.w*Ar,h:s.h*this._decorH,angle:Number(s.angle)||0}:void 0,moved:!1},Or(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if("scale"===e.kind&&e.orig){const s=t.shiftKey?0:this._gridPitch,n=s>0?s:.25*this._gridPitch,o=function(t,e,i,s,n,o=0,r=1e-6){const a=(Number(t.angle)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=-Math.sin(a),d=Math.cos(a),p=t.x+t.w/2,u=t.y+t.h/2,_=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,m=p+_*l+g*h,f=u+_*c+g*d,v=s-m,b=n-f;let y=(v*l+b*c)*(e>0?1:-1),w=(v*h+b*d)*(i>0?1:-1);return o>0&&(y=Math.round(y/o)*o,w=Math.round(w/o)*o),y=Math.max(r,y),w=Math.max(r,w),{x:m+(e>0?y/2:-y/2)*l+(i>0?w/2:-w/2)*h-y/2,y:f+(e>0?y/2:-y/2)*c+(i>0?w/2:-w/2)*d-w/2,w:y,h:w}}(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],s,n);return(Math.abs(o.w-e.orig.w)>1e-6||Math.abs(o.h-e.orig.h)>1e-6)&&(e.moved=!0),void this._furnApplyBox(e.id,o)}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.min(20,Math.max(.15,e.scale0*(t/e.r0)));return Math.abs(s-e.scale0)>1e-6&&(e.moved=!0),void this._dtApply(e.id,{scale:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let n=e.angle0+(s-e.a0);t.shiftKey||(n=5*Math.round(n/5)),n=(n%360+360)%360,n>180&&(n-=360),Math.abs(n-e.angle0)>1e-6&&(e.moved=!0),this._dtApply(e.id,{angle:n})}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&this._saveConfig(),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("furniture"===t.kind)e={id:t.id,x:t.x*Ar,y:t.y*this._decorH,w:t.w*Ar,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())}_decorDeleteSel(){if(!this._decorSel)return;this._curSpaceCfg.decor=this._decorList.filter(t=>t.id!==this._decorSel),this._decorSel=null,this._saveConfig(),this.requestUpdate()}get _furnWalls(){return this._segments}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=dn(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){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const n=Ar,o=this._decorH,r=gn(pn(i.w,this._cellCm,this._gridPitch,n)),a=gn(pn(i.h,this._cellCm,this._gridPitch,n)),l=this._snap(t,e);let c=l[0],h=l[1],d=0;if(!e.shiftKey){const t=bn(c,h,a*o,this._furnWalls,this._furnWallReach,this._gridPitch);t&&(c=t.cx,h=t.cy,d=t.angle)}const p="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),u={id:p,kind:"furniture",symbol:i.symbol,x:Bs(c/n-r/2),y:Bs(h/o-a/2),w:r,h:a,color:this._decorStyle.color,width:this._decorStyle.width};d&&(u.angle=Number(d.toFixed(2))),s.decor=[...this._decorList,u],this._decorSel=p,this._decorTool="select",this._furnPalette=null,this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(t){const e=this._decorMove,i=e.orig,s=this._curSpaceCfg;if(!s)return;const n=Ar,o=this._decorH,r=this._svgPoint(t),a=(i.x+i.w/2)*n+(r[0]-e.start[0]),l=(i.y+i.h/2)*o+(r[1]-e.start[1]);let c,h,d=Number(i.angle)||0;const p=t.shiftKey?null:bn(a,l,i.h*o,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)c=p.cx/n-i.w/2,h=p.cy/o-i.h/2,d=p.angle;else{const e=this._snap([a-i.w/2*n,l-i.h/2*o],t);c=e[0]/n,h=e[1]/o}c=Bs(c),h=Bs(h),(Math.abs(c-i.x)>1e-9||Math.abs(h-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:c,y:h};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_furnApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=Ar,n=this._decorH;i.decor=this._decorList.map(i=>i.id===t?{...i,x:Bs(e.x/s),y:Bs(e.y/n),w:gn(e.w/s),h:gn(e.h/n)}:i),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=Ar,s=this._decorH,n=e.w*i,o=e.h*s,r=function(t,e,i,s,n){const o=t+i/2,r=e+s/2,a=(Number(n)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=(t,e)=>{const i=t-o,s=e-r;return[o+i*l-s*c,r+i*c+s*l]};return[h(t,e),h(t+i,e),h(t+i,e+s),h(t,e+s)]}(e.x*i,e.y*s,n,o,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],l=a(r[0],r[1]),c=a(r[0],r[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[n,0])},{x:c[0],y:c[1],text:this._fmtLen([0,0],[0,o])}]}_renderFurnPalette(){const t=this._furnPalette,e=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return W`
t.stopPropagation()}>
${this._t("furn.title")} @@ -2680,7 +2680,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?{...Ps(t.plan_aspect,Pr)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?Rs(t,Pr):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale);return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,k:Number.isFinite(s)&&s>0?s:1}}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&("select"===this._decorTool||"backdrop"===this._decorTool)}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(t,e,i){const s=this._curSpaceCfg;s&&(s.plan_x=Number(Bs(t).toFixed(6)),s.plan_y=Number(Bs(e).toFixed(6)),s.plan_scale=Number(Math.min(100,Math.max(As,i)).toFixed(6)),this._cfgEpoch++,this.requestUpdate())}_bdStart(t,e){const i=this._bdBase,s=this._bdRect;if(!i||!s)return!1;const n=this._svgPoint(t),o=e?e[0]:0,r=e?e[1]:0,a=o>0?s.x:s.x+s.w,l=r>0?s.y:s.y+s.h;return this._bdDrag={kind:e?"scale":"move",pid:t.pointerId,sx:n[0],sy:n[1],base:i,p0:this._bdParams,fx:a,fy:l,sgx:o,sgy:r,moved:!1},Nr(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const n=s.x+e.p0.dx*Pr,o=s.y+e.p0.dy*Pr,r=this._snap([n+(i[0]-e.sx),o+(i[1]-e.sy)],t);return(Math.abs(r[0]-n)>1e-9||Math.abs(r[1]-o)>1e-9)&&(e.moved=!0),void this._bdApply((r[0]-s.x)/Pr,(r[1]-s.y)/Pr,e.p0.k)}const n=s.w||1,o=s.h||1;let r=Math.max(Math.abs(i[0]-e.fx)/n,Math.abs(i[1]-e.fy)/o);if(!t.shiftKey){const t=n>=o,i=ke(t?e.fx+e.sgx*r*n:e.fy+e.sgy*r*o,this._gridPitch),s=Math.abs(i-(t?e.fx:e.fy))/(t?n:o);s>0&&(r=s)}r=Math.min(100,Math.max(As,r)),Math.abs(r-e.p0.k)>1e-9&&(e.moved=!0);const a=e.sgx>0?e.fx:e.fx-r*n,l=e.sgy>0?e.fy:e.fy-r*o;this._bdApply((a-s.x)/Pr,(l-s.y)/Pr,r)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.k}_bdReset(){const t=this._curSpaceCfg;t&&(delete t.plan_x,delete t.plan_y,delete t.plan_scale,this._bdDrag=null,this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate())}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&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 V;const i=.02*Math.max(t.w,t.h),s=i/4;return B` +
`}get _bdBase(){const t=this._curSpaceCfg;return t?.plan_url?{...Ps(t.plan_aspect,Ar)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?Rs(t,Ar):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale);return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,k:Number.isFinite(s)&&s>0?s:1}}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&("select"===this._decorTool||"backdrop"===this._decorTool)}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(t,e,i){const s=this._curSpaceCfg;s&&(s.plan_x=Number(Bs(t).toFixed(6)),s.plan_y=Number(Bs(e).toFixed(6)),s.plan_scale=Number(Math.min(100,Math.max(As,i)).toFixed(6)),this._cfgEpoch++,this.requestUpdate())}_bdStart(t,e){const i=this._bdBase,s=this._bdRect;if(!i||!s)return!1;const n=this._svgPoint(t),o=e?e[0]:0,r=e?e[1]:0,a=o>0?s.x:s.x+s.w,l=r>0?s.y:s.y+s.h;return this._bdDrag={kind:e?"scale":"move",pid:t.pointerId,sx:n[0],sy:n[1],base:i,p0:this._bdParams,fx:a,fy:l,sgx:o,sgy:r,moved:!1},Or(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const n=s.x+e.p0.dx*Ar,o=s.y+e.p0.dy*Ar,r=this._snap([n+(i[0]-e.sx),o+(i[1]-e.sy)],t);return(Math.abs(r[0]-n)>1e-9||Math.abs(r[1]-o)>1e-9)&&(e.moved=!0),void this._bdApply((r[0]-s.x)/Ar,(r[1]-s.y)/Ar,e.p0.k)}const n=s.w||1,o=s.h||1;let r=Math.max(Math.abs(i[0]-e.fx)/n,Math.abs(i[1]-e.fy)/o);if(!t.shiftKey){const t=n>=o,i=ke(t?e.fx+e.sgx*r*n:e.fy+e.sgy*r*o,this._gridPitch),s=Math.abs(i-(t?e.fx:e.fy))/(t?n:o);s>0&&(r=s)}r=Math.min(100,Math.max(As,r)),Math.abs(r-e.p0.k)>1e-9&&(e.moved=!0);const a=e.sgx>0?e.fx:e.fx-r*n,l=e.sgy>0?e.fy:e.fy-r*o;this._bdApply((a-s.x)/Ar,(l-s.y)/Ar,r)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.k}_bdReset(){const t=this._curSpaceCfg;t&&(delete t.plan_x,delete t.plan_y,delete t.plan_scale,this._bdDrag=null,this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate())}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&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 V;const i=.02*Math.max(t.w,t.h),s=i/4;return B` ${[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]].map(([t,n,o])=>{const r=t<0?e.x:e.x+e.w,a=n<0?e.y:e.y+e.h;return B`this._dtStart(i,"scale",[t,e])}>`)} - `}_renderDecorLayer(){const t=Pr,e=this._decorH,i="decor"===this._mode,s=this._decorList.map(s=>{const n="dshape"+(i&&this._decorSel===s.id?" dsel":""),o=t=>this._decorShapeDown(t,s),r=()=>this._decorShapeDbl(s);if("line"===s.kind)return B``}_renderDecorLayer(){const t=Ar,e=this._decorH,i="decor"===this._mode,s=this._decorList.map(s=>{const n="dshape"+(i&&this._decorSel===s.id?" dsel":""),o=t=>this._decorShapeDown(t,s),r=()=>this._decorShapeDbl(s);if("line"===s.kind)return B``;if("rect"===s.kind)return B``)} ${i?i.segs.map(t=>B``):V} - `}_openCuts(){const t=this._curSpaceCfg;return mo(this._spaceModel().rooms,t?.open_spans,Pr,.02*this._gridPitch)}_openPairs(){const t=this._openCuts();if(!t.length)return[];const e=this._spaceModel().rooms.filter(t=>t.id),i=.02*this._gridPitch,s=[];for(let n=0;n{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return l.some(t=>Bi(e,t)<4*i)});c.length&&s.push({a:e[n],b:e[o],segs:c})}return s}_commitOpenSpans(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch;this._cfgEpoch++;const s=this._spaceModel().rooms;let n=lo(e.open_spans);n.length&&t?.old.length?n=function(t,e,i,s){const n=lo(t);if(!n.length)return[];if(e.length!==i.length)return n;const o=[];for(const t of n){const n=ro(t,s),r=[n[0],n[1]],a=[n[2],n[3]];let l=r,c=a;for(let t=0;t{const e=co(t,[s[0],s[1],n[0],n[1]]);if(e.d>.001)return t;const i=h[0]-o[0],r=h[1]-o[1];return[o[0]+i*e.t,o[1]+r*e.t]};if(co([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],n[0],n[1]]).d<.01){l=d(r),c=d(a);break}}o.push(oo(l,c,s))}return o}(n,t.old,t.next,Pr):n.length||(n=fo(mo(s,null,Pr,i),Pr)),n=function(t,e,i,s){const n=lo(t);if(!n.length)return[];const o=uo(e,s);if(!o.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of n){const e=ro(t,i),n=e[0],l=e[1],c=e[2]-n,h=e[3]-l,d=Math.hypot(c,h);if(d4*s||o>4*s)continue;const r=(t[0]-n)*p+(t[1]-l)*u,c=(t[2]-n)*p+(t[3]-l)*u,h=Math.max(0,Math.min(r,c)),g=Math.min(d,Math.max(r,c));if(g-ht.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t){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 t of e){const e=[n+p*t.lo,l+u*t.lo],s=[n+p*t.hi,l+u*t.hi];Math.hypot(s[0]-e[0],s[1]-e[1])ro(t,Pr)))}_persistOpenCuts(t){const e=this._curSpaceCfg;if(!e)return;const i=fo(t,Pr);i.length?e.open_spans=i:delete e.open_spans,function(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const n=(e||[]).filter(t=>t?.id);new Map(n.map(t=>[t.id,t]));const o=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=o.get(t),s=o.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;tBi(i,t)<4*s)){r(n[t].id,n[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}(e.rooms||[],this._spaceModel().rooms,t,.02*this._gridPitch)}_closeOpenSpan(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch,s=function(t,e,i){const s=[(e[0]+e[2])/2,(e[1]+e[3])/2];return t.filter(t=>{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return Math.hypot(e[0]-s[0],e[1]-s[1])>4*i})}(this._openCuts(),t,i);let n=this._normalizeWalls(e.walls,s);if(!(Zn(this._spaceModel().rooms,n,s,t,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)>0)){const e=[];for(const t of Xn(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr))t.open||e.push([t.a[0],t.a[1],t.b[0],t.b[1]]);n=bo(n,t,e,this._wallKeyPitch,Pr,15),n=this._normalizeWalls(n,s)}n.length?e.walls=n:delete e.walls,this._persistOpenCuts(s),this._showToast(this._t("toast.openwall_closed_span")),this._saveConfig(),this.requestUpdate()}_openWallClick(t){const e=6*this._gridPitch,i=.02*this._gridPitch,s=this._openCuts();if(this._openWallAnchor){const{p:n,edge:o}=this._openWallAnchor,r=ho(po(t,o,yo(o,s,i),this._gridPitch,1.5*this._gridPitch),o),a=Math.hypot(r[0]-n[0],r[1]-n[1]);if(this._openWallAnchor=null,a<.5*this._gridPitch)return void this._showToast(this._t("toast.openwall_short"));if(Bi(t,o)>e)return void this._showToast(this._t("toast.openwall_pick"));const l=[n[0],n[1],r[0],r[1]],c=[...s,l],h=this._curSpaceCfg;if(!h)return;const d=this._normalizeWalls(h.walls,c);d.length?h.walls=d:delete h.walls;const p=(h.openings||[]).length;return h.openings=function(t,e,i,s){return t?.length?t.filter(t=>Bi([Number(t.x)*i,Number(t.y)*i],e)>s||!Nn([e[0],e[1]],[e[2],e[3]],Number(t.angle)||0)):t?t.slice():[]}(h.openings,l,Pr,e),(h.openings||[]).lengththis._pointInRoom(t,e));if(!a)return;if(!confirm(this._t("confirm.delete_room",{name:a.name})))return;const l=this._curSpaceCfg;l&&(l.rooms=l.rooms.filter(t=>t.id!==a.id),this._commitOpenSpans(),this._saveConfig(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate())}get _wallKeyPitch(){return Hs}get _spaceWalls(){const t=this._curSpaceCfg?.walls;return Array.isArray(t)?t:[]}_cfgOpenCuts(){return lo(this._curSpaceCfg?.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_intervalCm(t){return Zn(this._spaceModel().rooms,this._spaceWalls,this._openCuts(),t,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)}_normalizeWalls(t,e){const i=function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=[],l=new Set;for(const c of Xn(t,e,i,s,n,o,r))!c.open&&c.cm>0&&!l.has(c.key)&&(l.add(c.key),a.push({key:c.key,cm:c.cm}));return a}(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr);return En(i,this._curSpaceCfg?.rooms||[],Hs,1,e.map(t=>[t[0]/Pr,t[1]/Pr,t[2]/Pr,t[3]/Pr]))}_paperShapes(t){const e=this._spaceWalls;if(!e.length)return Me(t);const i=this._openPairs().flatMap(t=>t.segs);return so(t,e,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)}_thickWallCuts(){const t=this._spaceWalls;if(!t.length)return[];const e=this._openPairs().flatMap(t=>t.segs);return function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=new Set,l=[];for(const c of t||[]){if(!c?.id)continue;const h=Yn(t,c.id,e,i,s,n,o,r);if(!h)continue;const d=h.poly;for(let t=0;t0))continue;const u=Dn(e,i,s,r);if(a.has(u))continue;a.add(u);const _=xn(p,n,o),[g,m]=qn(d,t),f=-g,v=-m,b=_/2,y=[[e[0]+f*b,e[1]+v*b],[i[0]+f*b,i[1]+v*b],[i[0]+g*b,i[1]+m*b],[e[0]+g*b,e[1]+m*b]];l.push({key:u,kind:c,cm:p,quad:y,a:[e[0],e[1]],b:[i[0],i[1]],depthUnits:_})}}return l}(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_wallThickHit(t){const e=6*this._gridPitch,i=this._openCuts();let s=null;for(const n of Xn(this._spaceModel().rooms,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)){const i=Bi(t,[n.a[0],n.a[1],n.b[0],n.b[1]]);i<=e&&(!s||i0?xn(e,this._cellCm,this._gridPitch):3*this._gridPitch,s=Math.max(i/2,1.25*this._gridPitch);let n="";for(const e of t.segs)n+=(n?" ":"")+Fn([[e[0],e[1]],[e[2],e[3]]],s,!1);return{segs:t.segs,open:t.open,d:n}}_wallThickClick(t){const e=this._wallThickHit(t);if(!e)return void this._showToast(this._t("toast.wallthick_pick"));if(e.open)return void this._showToast(this._t("toast.wallthick_open"));const i=e.cm,s=this._viewOr(this._baseVb()),n=(e.a[0]+e.b[0])/2,o=(e.a[1]+e.b[1])/2;this._wallDialog={a:e.a,b:e.b,value:kn(i,this._imperial),roomId:e.roomId,sx:(n-s.x)/s.w*100,sy:(o-s.y)/s.h*100}}_wallThickApply(t){const e=this._wallDialog;if(!e)return;const i=this._curSpaceCfg;if(!i)return;const s=$n(e.value,this._imperial),n=this._openCuts();let o;o=t&&e.roomId?In(i.walls,this._spaceModel().rooms,e.roomId,s,this._wallKeyPitch,n,Pr):On(i.walls,e.a,e.b,s,this._wallKeyPitch,Pr),o=this._normalizeWalls(o,n),o.length?i.walls=o:delete i.walls,this._wallDialog=null,this._showToast(this._t(null==s?"toast.wallthick_cleared":"toast.wallthick_set")),this._saveConfig(),this.requestUpdate()}_wallHatchDefs(t){if(!this._spaceWalls.length&&!this._markup)return B``;const e=Math.max(.4,1/Math.max(this._zoom,.4)),i=t||"var(--hp-muted, #607d8b)";return B` + `}_openCuts(){const t=this._curSpaceCfg;return mo(this._spaceModel().rooms,t?.open_spans,Ar,.02*this._gridPitch)}_openPairs(){const t=this._openCuts();if(!t.length)return[];const e=this._spaceModel().rooms.filter(t=>t.id),i=.02*this._gridPitch,s=[];for(let n=0;n{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return l.some(t=>Bi(e,t)<4*i)});c.length&&s.push({a:e[n],b:e[o],segs:c})}return s}_commitOpenSpans(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch;this._cfgEpoch++;const s=this._spaceModel().rooms;let n=lo(e.open_spans);n.length&&t?.old.length?n=wo(n,t.old,t.next,Ar):n.length||(n=fo(mo(s,null,Ar,i),Ar)),n=function(t,e,i,s){const n=lo(t);if(!n.length)return[];const o=uo(e,s);if(!o.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of n){const e=ro(t,i),n=e[0],l=e[1],c=e[2]-n,h=e[3]-l,d=Math.hypot(c,h);if(d4*s||o>4*s)continue;const r=(t[0]-n)*p+(t[1]-l)*u,c=(t[2]-n)*p+(t[3]-l)*u,h=Math.max(0,Math.min(r,c)),g=Math.min(d,Math.max(r,c));if(g-ht.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t){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 t of e){const e=[n+p*t.lo,l+u*t.lo],s=[n+p*t.hi,l+u*t.hi];Math.hypot(s[0]-e[0],s[1]-e[1])ro(t,Ar)))}_persistOpenCuts(t){const e=this._curSpaceCfg;if(!e)return;const i=fo(t,Ar);i.length?e.open_spans=i:delete e.open_spans,function(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const n=(e||[]).filter(t=>t?.id);new Map(n.map(t=>[t.id,t]));const o=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=o.get(t),s=o.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;tBi(i,t)<4*s)){r(n[t].id,n[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}(e.rooms||[],this._spaceModel().rooms,t,.02*this._gridPitch)}_closeOpenSpan(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch,s=function(t,e,i){const s=[(e[0]+e[2])/2,(e[1]+e[3])/2];return t.filter(t=>{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return Math.hypot(e[0]-s[0],e[1]-s[1])>4*i})}(this._openCuts(),t,i);let n=this._normalizeWalls(e.walls,s);if(!(Zn(this._spaceModel().rooms,n,s,t,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)>0)){const e=[];for(const t of Xn(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar))t.open||e.push([t.a[0],t.a[1],t.b[0],t.b[1]]);n=bo(n,t,e,this._wallKeyPitch,Ar,15),n=this._normalizeWalls(n,s)}n.length?e.walls=n:delete e.walls,this._persistOpenCuts(s),this._showToast(this._t("toast.openwall_closed_span")),this._saveConfig(),this.requestUpdate()}_openWallClick(t){const e=6*this._gridPitch,i=.02*this._gridPitch,s=this._openCuts();if(this._openWallAnchor){const{p:n,edge:o}=this._openWallAnchor,r=ho(po(t,o,yo(o,s,i),this._gridPitch,1.5*this._gridPitch),o),a=Math.hypot(r[0]-n[0],r[1]-n[1]);if(this._openWallAnchor=null,a<.5*this._gridPitch)return void this._showToast(this._t("toast.openwall_short"));if(Bi(t,o)>e)return void this._showToast(this._t("toast.openwall_pick"));const l=[n[0],n[1],r[0],r[1]],c=[...s,l],h=this._curSpaceCfg;if(!h)return;const d=this._normalizeWalls(h.walls,c);d.length?h.walls=d:delete h.walls;const p=(h.openings||[]).length;return h.openings=function(t,e,i,s){return t?.length?t.filter(t=>Bi([Number(t.x)*i,Number(t.y)*i],e)>s||!Nn([e[0],e[1]],[e[2],e[3]],Number(t.angle)||0)):t?t.slice():[]}(h.openings,l,Ar,e),(h.openings||[]).lengththis._pointInRoom(t,e));if(!a)return;if(!confirm(this._t("confirm.delete_room",{name:a.name})))return;const l=this._curSpaceCfg;l&&(l.rooms=l.rooms.filter(t=>t.id!==a.id),this._commitOpenSpans(),this._saveConfig(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate())}get _wallKeyPitch(){return qs}get _spaceWalls(){const t=this._curSpaceCfg?.walls;return Array.isArray(t)?t:[]}_cfgOpenCuts(){return lo(this._curSpaceCfg?.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_intervalCm(t){return Zn(this._spaceModel().rooms,this._spaceWalls,this._openCuts(),t,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)}_normalizeWalls(t,e){const i=function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=[],l=new Set;for(const c of Xn(t,e,i,s,n,o,r))!c.open&&c.cm>0&&!l.has(c.key)&&(l.add(c.key),a.push({key:c.key,cm:c.cm}));return a}(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar);return En(i,this._curSpaceCfg?.rooms||[],qs,1,e.map(t=>[t[0]/Ar,t[1]/Ar,t[2]/Ar,t[3]/Ar]))}_paperShapes(t){const e=this._spaceWalls;if(!e.length)return Me(t);const i=this._openPairs().flatMap(t=>t.segs);return so(t,e,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)}_thickWallCuts(){const t=this._spaceWalls;if(!t.length)return[];const e=this._openPairs().flatMap(t=>t.segs);return function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=new Set,l=[];for(const c of t||[]){if(!c?.id)continue;const h=Yn(t,c.id,e,i,s,n,o,r);if(!h)continue;const d=h.poly;for(let t=0;t0))continue;const u=Dn(e,i,s,r);if(a.has(u))continue;a.add(u);const _=xn(p,n,o),[g,m]=Hn(d,t),f=-g,v=-m,b=_/2,y=[[e[0]+f*b,e[1]+v*b],[i[0]+f*b,i[1]+v*b],[i[0]+g*b,i[1]+m*b],[e[0]+g*b,e[1]+m*b]];l.push({key:u,kind:c,cm:p,quad:y,a:[e[0],e[1]],b:[i[0],i[1]],depthUnits:_})}}return l}(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_wallThickHit(t){const e=6*this._gridPitch,i=this._openCuts();let s=null;for(const n of Xn(this._spaceModel().rooms,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)){const i=Bi(t,[n.a[0],n.a[1],n.b[0],n.b[1]]);i<=e&&(!s||i0?xn(e,this._cellCm,this._gridPitch):3*this._gridPitch,s=Math.max(i/2,1.25*this._gridPitch);let n="";for(const e of t.segs)n+=(n?" ":"")+Fn([[e[0],e[1]],[e[2],e[3]]],s,!1);return{segs:t.segs,open:t.open,d:n}}_wallThickClick(t){const e=this._wallThickHit(t);if(!e)return void this._showToast(this._t("toast.wallthick_pick"));if(e.open)return void this._showToast(this._t("toast.wallthick_open"));const i=e.cm,s=this._viewOr(this._baseVb()),n=(e.a[0]+e.b[0])/2,o=(e.a[1]+e.b[1])/2;this._wallDialog={a:e.a,b:e.b,value:kn(i,this._imperial),roomId:e.roomId,sx:(n-s.x)/s.w*100,sy:(o-s.y)/s.h*100}}_wallThickApply(t){const e=this._wallDialog;if(!e)return;const i=this._curSpaceCfg;if(!i)return;const s=$n(e.value,this._imperial),n=this._openCuts();let o;o=t&&e.roomId?In(i.walls,this._spaceModel().rooms,e.roomId,s,this._wallKeyPitch,n,Ar):On(i.walls,e.a,e.b,s,this._wallKeyPitch,Ar),o=this._normalizeWalls(o,n),o.length?i.walls=o:delete i.walls,this._wallDialog=null,this._showToast(this._t(null==s?"toast.wallthick_cleared":"toast.wallthick_set")),this._saveConfig(),this.requestUpdate()}_wallHatchDefs(t){if(!this._spaceWalls.length&&!this._markup)return B``;const e=Math.max(.4,1/Math.max(this._zoom,.4)),i=t||"var(--hp-muted, #607d8b)";return B` - `}_renderWallBodies(t){if(t&&!t.showBorders&&!this._markup)return B``;const e=this._spaceWalls;if(!e.length)return B``;const i=this._openPairs().flatMap(t=>t.segs),s=(this._curSpaceCfg?.openings||[]).map(t=>({x:Number(t.x)*Pr,y:Number(t.y)*Pr,angle:Number(t.angle)||0,length:(Number(t.length)>0?Number(t.length):.9)*Pr})),n=eo(this._spaceModel().rooms,e,i,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr);if(!n)return B``;const o=this._stageEl,r=this._viewOr(this._baseVb()),a=o&&o.clientWidth&&r.w?o.clientWidth/r.w:1,l=t?.color||"var(--hp-muted)",c=yn(n.depthUnits,a),h=this._fillColors.wall_fill;return B` + `}_renderWallBodies(t){if(t&&!t.showBorders&&!this._markup)return B``;const e=this._spaceWalls;if(!e.length)return B``;const i=this._openPairs().flatMap(t=>t.segs),s=(this._curSpaceCfg?.openings||[]).map(t=>({x:Number(t.x)*Ar,y:Number(t.y)*Ar,angle:Number(t.angle)||0,length:(Number(t.length)>0?Number(t.length):.9)*Ar})),n=eo(this._spaceModel().rooms,e,i,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar);if(!n)return B``;const o=this._stageEl,r=this._viewOr(this._baseVb()),a=o&&o.clientWidth&&r.w?o.clientWidth/r.w:1,l=t?.color||"var(--hp-muted)",c=yn(n.depthUnits,a),h=this._fillColors.wall_fill;return B`
-
`:W``}_openingClick(t,e=!1){const i=1.5*this._gridPitch,s=this._openingsR.find(e=>Math.hypot(t[0]-e.rx,t[1]-e.ry)<=Math.max(e.rlen/2,i));if(s)return void this._editOpening(s);const n=De(t,this._spaceModel().rooms,i);if(!n)return void this._showToast(this._t("toast.opening_no_wall"));if(function(t,e,i,s,n){for(const o of s)if(!(Bi([t,e],o)>n)&&Nn([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(n.x,n.y,n.angle,this._openCuts(),i))return void this._showToast(this._t("toast.opening_on_virtual"));const o=this._opRuler(n,this._cmToUnits(90),e);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{Nr(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1}}}_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=De(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,n=s?.openings?.find(t=>t.id===e.id);if(!n)return;const o=this._opRuler(i,n.length*Pr,t.shiftKey);this._opMeasure=o.measure;const r=o.x/Pr,a=o.y/this._spaceH;n.x===r&&n.y===a&&n.angle===i.angle||(this._opDrag.dirty=!0),n.x=r,n.y=a,n.angle=i.angle,this.requestUpdate()}_opRuler(t,e,i){const s=this._spaceModel().rooms,n=this._gridPitch/2;let o=t.x,r=t.y,a=Te([o,r],t.angle,e,s,n);if(!a||!a.centered||i||o===a.wallCenter[0]&&r===a.wallCenter[1]){if(a&&!i){const[i,l]=a.wallA,[c,h]=a.wallB,d=c-i,p=h-l,u=Math.hypot(d,p);if(u>0){const c=this._gridPitch,h=Math.min(e/2,u/2);let _=Math.round(((o-i)*d+(r-l)*p)/u/c)*c;_=Math.max(h,Math.min(u-h,_)),o=i+_/u*d,r=l+_/u*p,a=Te([o,r],t.angle,e,s,n)||a}}}else[o,r]=a.wallCenter,a=Te([o,r],t.angle,e,s,n);if(!a)return{x:o,y:r,angle:t.angle,measure:null};const l="mi"===this.hass?.config?.unit_system?.length,c=(t,e)=>({x:e[0],y:e[1],text:$e(t/this._gridPitch*this._cellCm,l)});return{x:o,y:r,angle:t.angle,measure:{labels:[c(a.sideA,a.midA),c(a.sideB,a.midB)],guide:a.centered&&!i?{x:a.wallCenter[0],y:a.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;const i=this._opDrag.moved;this._opMeasure=null,i&&this._opDrag.dirty&&this._saveConfig(),i?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._spaceH,s={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Pr,y:t.y/i,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/Pr,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===s.id);n>=0?e.openings[n]=s:e.openings.push(s),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;t?.id&&e?.openings&&(e.openings=e.openings.filter(e=>e.id!==t.id),this._saveConfig(),this._openingDialog=null,this.requestUpdate())}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&t.push([e,s?.attributes?.friendly_name||e,n?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_mergeClick(t){const e=this._spaceModel().rooms,i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const s=i.id;if(!this._mergeSel||this._mergeSel===s)return void(this._mergeSel=this._mergeSel===s?null:s);const n=e.find(t=>t.id===this._mergeSel),o=n?Se(n):null,r=Se(i),a=o&&r?Ve(o,r):null;if(!a)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:s,poly:a,pick:"a"},this._mergeSel=null}_commitMerge(){const t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._spaceH,s="a"===t.pick?t.aId:t.bId,n="a"===t.pick?t.bId:t.aId,o=e.rooms.find(t=>t.id===s);o?(o.poly=t.poly.map(t=>[t[0]/Pr,t[1]/i]),delete o.x,delete o.y,delete o.w,delete o.h,e.rooms=e.rooms.filter(t=>t.id!==n),this._commitOpenSpans(),this._saveConfig(),this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.rooms_merged",{name:o.name||""}))):this._mergeDialog=null}_splitClick(t,e=!1){const i=this._spaceModel().rooms;if(!this._splitSel){const e=[...i].reverse().find(e=>this._pointInRoom(t,e));if(!e?.id)return;return void(this._splitSel={roomId:e.id,pts:[]})}const s=i.find(t=>t.id===this._splitSel.roomId),n=s?Se(s):null;if(!s||!n)return void(this._splitSel=null);const o=.02*this._gridPitch,r=6*this._gridPitch,a=Ee(t,n),l=a&&!e&&function(t,e,i){let s=null,n=1/0;for(let o=0;o=n)continue;n=_;const g=Math.sqrt(p),m=(i>0?Math.max(0,Math.min(g,Math.round(u*g/i)*i)):u*g)/g;s=[r+m*h,a+m*d]}return s}(a,n,this._gridPitch)||a,c=a&&l&&Math.hypot(a[0]-t[0],a[1]-t[1])<=r?l:null,h=!!c&&Ne(c,n,o),d=this._splitSel.pts;if(!d.length)return h?void(this._splitSel={...this._splitSel,pts:[c]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const i=this._snap(t,e);return Oe(i,n,o)?void(this._splitSel={...this._splitSel,pts:[...d,i]}):void this._showToast(this._t("toast.split_pick_inside"))}const p=function(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(Pe(s,n,i))return null;const o=Ge(t,s,i),r=Ge(t,n,i);if(o<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!Oe(e,t,i))return null;for(let i=0;i{const r=[e];let a=(s+1)%t.length;for(let e=0;e<=t.length&&(r.push(t[a]),a!==o);e++)a=(a+1)%t.length;return r.push(n),Ke(r,i)};let c,h;if(o===r){const r=Ke([...e],i);if(r.length<3||Be(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}c=Ke(a,i),h=r}else c=Ke([...l(s,o,n,r),...[...a].reverse()],i),h=Ke([...l(n,r,s,o),...a],i);return c.length<3||h.length<3||Be(c)<=i||Be(h)<=i||Math.abs(Be(c)+Be(h)-Be(t))>Math.max(i,1e-6*Be(t))?null:[c,h]}(n,[...d,c],o);if(!p)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,g=Be(u)>=Be(_)?u:_,m=g===u?_:u;this._pendingSplit={roomId:s.id,mainPoly:g,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("opening"===this._tool||"openwall"===this._tool||"wallthick"===this._tool)return this._opShift=!!t.shiftKey,void(this._cursorPt=this._svgPoint(t));const e="draw"===this._tool&&this._path.length&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;(e||i)&&(this._cursorPt=this._snap(this._svgPoint(t),t))}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),n=De(t,this._spaceModel().rooms,e);if(!n)return null;const o=this._opRuler(n,s,this._opShift);return{x:o.x,y:o.y,angle:o.angle,rlen:s,measure:o.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._spaceH,i=!!this._pendingSplit;let s;if(this._pendingSplit){const i=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!i)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);i.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Pr,t[1]/e]),delete i.x,delete i.y,delete i.w,delete i.h,s=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;s=this._path.slice(0,-1)}const n=this._areaSel?this.hass.areas[this._areaSel]?.name:"",o={id:"r"+Date.now().toString(36),name:this._nameSel||n||this._t("room.default_name"),area:this._areaSel||null,poly:s.map(t=>[t[0]/Pr,t[1]/e]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(o),i&&this._commitOpenSpans(),!i){const e=this._drawWallCm;if(null!=e){this._cfgEpoch++;const i=this._openCuts();let s=function(t,e,i,s,n,o=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=Gn(e,i,o,n,r);if(!a)return t?t.slice():[];const l=Kn(t,a,n,r);let c=t?t.slice():[];for(let t=0;t0||(c=On(c,e,i,s,n,r))}return c}(t.walls,this._spaceModel().rooms,o.id,e,this._wallKeyPitch,i,Pr);s=this._normalizeWalls(s,i),s.length?t.walls=s:delete t.walls}}this._saveConfig(),this._path=[],this._pendingSplit=null,this._splitSel=null;const r=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let a=0;if(r){const t=Pr,e={...this._layout};for(const i of this._devices){if(i.area!==r||i.space!==this._space)continue;if(a++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/Pr,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const l=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(r?this._t("toast.room_saved",{n:l,added:a}):this._t("toast.room_saved_no_area",{n:l}))}_cancelPath(){this._path=[],this._cursorPt=null,this._roomDialog=!1,this._pendingSplit=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._openWallAnchor=null}_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: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:[...t.marker?.controls||[]],controlsFilter:"",isLight:!0===t.marker?.is_light,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 li)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=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&e.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&e.add("entity:"+t.bindingRef));const i=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&i.add(t.name.trim()+"|"+(t.area||""));const s=[];for(const n of Object.values(t.devices)){if("service"===n.entry_type)continue;const t="device:"+n.id;if(e.has(t))continue;const o=(n.name_by_user||n.name||n.id).trim();t!==this._markerDialog?.binding&&i.has(o+"|"+(n.area_id||""))||s.push({value:t,label:o,sub:(n.model||this._t("marker.sub_device"))+("Group"===n.model?this._t("marker.sub_z2m_group"):"")})}const n=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[i,o]of Object.entries(t.entities)){const r="entity:"+i;if(e.has(r))continue;const a=n.has(o.platform),l="group"===o.platform;if(!a&&!l)continue;if(o.hidden)continue;const c=t.states[i];s.push({value:r,label:o.name||c?.attributes?.friendly_name||i,sub:i.split(".")[0]+" · "+("group"===o.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const i=new Set(s.map(t=>t.value));for(const[n,o]of Object.entries(t.entities)){const r="entity:"+n;if(e.has(r)||i.has(r)||o.hidden)continue;const a=t.states[n],l=o.name||a?.attributes?.friendly_name||n,c=o.device_id?t.devices[o.device_id]:null,h=c&&(c.name_by_user||c.name)||"";s.push({value:r,label:l,sub:n.split(".")[0]+" · "+this._t("marker.sub_entity")+(h?" · "+h:"")})}}const o=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),r=o?s.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(o)):s;return r.sort((t,e)=>t.label.localeCompare(e.label)),r.slice(0,200)}_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",n=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const t={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||t.name,url:o.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}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 n=s?.space||null,o=s?.area||null;const r=s?.roomId||null;"virtual"!==t.binding||n||(n=this._space),i=function(t,e,i){const[s,n]=t.split(":");return"device"===s?n:"entity"===s?"lg_"+n:e&&e.startsWith("v_")?e:i()}(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const a=t.devId,l=e.markers.find(t=>t.id===i||t.id===a)?.vacuum||null,c={id:i,vacuum:l,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"badge"!==t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"badge"!==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:t.controls.length?t.controls:null,is_light:!!t.isLight||null,use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=parseFloat(t.glowRadius);return!Number.isFinite(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)&&(c.space=n,c.area=o,c.room_id=r);const h=a?this._devices.find(t=>t.id===a):null,d=h?.marker?.room_id??null,p=!!t.room&&null!=h&&(h.space!==n||h.area!==o||d!==r);let u=!1;const _=t.uploadId||a;if(_&&_!==i&&c.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:_,to_id:i}),e=t?.mapping||{};c.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const n="/files/"+e+"/",o="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(n))return t;const e=t.url.split(n)[1]||"",[i,r]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(n+i)[0]+o+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(n).join(o)}})}(c.pdfs,_,i,e),u=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),e.markers.push(c);let g=null;const m=n||h?.space||this._space,f=a?this._layout[a]:null,v=f?{s:f.s||h?.space||this._space,x:f.x,y:f.y}:a&&h&&this._defPos[a]?this._normPos(h.space,this._defPos[a].x,this._defPos[a].y):null;if(v&&v.s===m)i===a&&this._layout[i]&&!p||(g={s:v.s,x:v.x,y:v.y},this._layout={...this._layout,[i]:g});else if(!this._layout[i]||p){const t=this._spaceModel(n||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):o?t.rooms.find(t=>t.area===o):void 0;a&&([e,s]=this._roomCenter(a)),g=this._normPos(n||this._space,e,s),this._layout={...this._layout,[i]:g}}await this._saveConfigNow(),g&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:i,pos:g})),a&&a!==i&&(delete this._layout[a],await this.hass.callWS({type:"houseplan/layout/delete",device_id:a}).then(t=>this._noteLayoutRev(t)).catch(()=>{})),u&&_&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:_}).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)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null,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||[],e&&"virtual"===e.bindingKind?s.markers=s.markers.filter(t=>t.id!==e.id):e&&e.marker?(s.markers=s.markers.filter(t=>t.id!==e.id),"device"===e.bindingKind&&e.bindingRef?s.markers.push({id:e.id,binding:"device:"+e.bindingRef,hidden:!0}):"entity"===e.bindingKind&&e.bindingRef&&s.markers.push({id:e.id,binding:"entity:"+e.bindingRef,hidden:!0})):e&&"device"===e.bindingKind&&e.bindingRef?s.markers.push({id:e.id,binding:"device:"+e.bindingRef,hidden:!0}):e&&"entity"===e.bindingKind&&e.bindingRef&&s.markers.push({id:e.id,binding:"entity:"+e.bindingRef,hidden:!0});try{await this._saveConfigNow(),e&&"virtual"===e.bindingKind&&this._layout[e.id]&&(delete this._layout[e.id],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e.id}).then(t=>this._noteLayoutRev(t)).catch(()=>{})),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Pr,y:i/Pr}}_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=fi(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:Ss({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:gi,roomOpacity:mi,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 n=new Uint8Array(await i.arrayBuffer());let o="";for(let t=0;t{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=a});URL.revokeObjectURL(a),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:r,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return W`
${this._t("space.loading")}
`;const e=t.saved||[];if(!e.length)return W`
${this._t("space.no_saved")}
`;return W`
+
`:W``}_openingClick(t,e=!1){const i=1.5*this._gridPitch,s=this._openingsR.find(e=>Math.hypot(t[0]-e.rx,t[1]-e.ry)<=Math.max(e.rlen/2,i));if(s)return void this._editOpening(s);const n=De(t,this._spaceModel().rooms,i);if(!n)return void this._showToast(this._t("toast.opening_no_wall"));if(function(t,e,i,s,n){for(const o of s)if(!(Bi([t,e],o)>n)&&Nn([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(n.x,n.y,n.angle,this._openCuts(),i))return void this._showToast(this._t("toast.opening_on_virtual"));const o=this._opRuler(n,this._cmToUnits(90),e);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{Or(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1}}}_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=De(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,n=s?.openings?.find(t=>t.id===e.id);if(!n)return;const o=this._opRuler(i,n.length*Ar,t.shiftKey);this._opMeasure=o.measure;const r=o.x/Ar,a=o.y/this._spaceH;n.x===r&&n.y===a&&n.angle===i.angle||(this._opDrag.dirty=!0),n.x=r,n.y=a,n.angle=i.angle,this.requestUpdate()}_opRuler(t,e,i){const s=this._spaceModel().rooms,n=this._gridPitch/2;let o=t.x,r=t.y,a=Te([o,r],t.angle,e,s,n);if(!a||!a.centered||i||o===a.wallCenter[0]&&r===a.wallCenter[1]){if(a&&!i){const[i,l]=a.wallA,[c,h]=a.wallB,d=c-i,p=h-l,u=Math.hypot(d,p);if(u>0){const c=this._gridPitch,h=Math.min(e/2,u/2);let _=Math.round(((o-i)*d+(r-l)*p)/u/c)*c;_=Math.max(h,Math.min(u-h,_)),o=i+_/u*d,r=l+_/u*p,a=Te([o,r],t.angle,e,s,n)||a}}}else[o,r]=a.wallCenter,a=Te([o,r],t.angle,e,s,n);if(!a)return{x:o,y:r,angle:t.angle,measure:null};const l="mi"===this.hass?.config?.unit_system?.length,c=(t,e)=>({x:e[0],y:e[1],text:$e(t/this._gridPitch*this._cellCm,l)});return{x:o,y:r,angle:t.angle,measure:{labels:[c(a.sideA,a.midA),c(a.sideB,a.midB)],guide:a.centered&&!i?{x:a.wallCenter[0],y:a.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;const i=this._opDrag.moved;this._opMeasure=null,i&&this._opDrag.dirty&&this._saveConfig(),i?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._spaceH,s={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Ar,y:t.y/i,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/Ar,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===s.id);n>=0?e.openings[n]=s:e.openings.push(s),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;t?.id&&e?.openings&&(e.openings=e.openings.filter(e=>e.id!==t.id),this._saveConfig(),this._openingDialog=null,this.requestUpdate())}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&t.push([e,s?.attributes?.friendly_name||e,n?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_mergeClick(t){const e=this._spaceModel().rooms,i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const s=i.id;if(!this._mergeSel||this._mergeSel===s)return void(this._mergeSel=this._mergeSel===s?null:s);const n=e.find(t=>t.id===this._mergeSel),o=n?Se(n):null,r=Se(i),a=o&&r?Ve(o,r):null;if(!a)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:s,poly:a,pick:"a"},this._mergeSel=null}_commitMerge(){const t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._spaceH,s="a"===t.pick?t.aId:t.bId,n="a"===t.pick?t.bId:t.aId,o=e.rooms.find(t=>t.id===s);o?(o.poly=t.poly.map(t=>[t[0]/Ar,t[1]/i]),delete o.x,delete o.y,delete o.w,delete o.h,e.rooms=e.rooms.filter(t=>t.id!==n),this._commitOpenSpans(),this._saveConfig(),this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.rooms_merged",{name:o.name||""}))):this._mergeDialog=null}_splitClick(t,e=!1){const i=this._spaceModel().rooms;if(!this._splitSel){const e=[...i].reverse().find(e=>this._pointInRoom(t,e));if(!e?.id)return;return void(this._splitSel={roomId:e.id,pts:[]})}const s=i.find(t=>t.id===this._splitSel.roomId),n=s?Se(s):null;if(!s||!n)return void(this._splitSel=null);const o=.02*this._gridPitch,r=6*this._gridPitch,a=Ee(t,n),l=a&&!e&&function(t,e,i){let s=null,n=1/0;for(let o=0;o=n)continue;n=_;const g=Math.sqrt(p),m=(i>0?Math.max(0,Math.min(g,Math.round(u*g/i)*i)):u*g)/g;s=[r+m*h,a+m*d]}return s}(a,n,this._gridPitch)||a,c=a&&l&&Math.hypot(a[0]-t[0],a[1]-t[1])<=r?l:null,h=!!c&&Ne(c,n,o),d=this._splitSel.pts;if(!d.length)return h?void(this._splitSel={...this._splitSel,pts:[c]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const i=this._snap(t,e);return Oe(i,n,o)?void(this._splitSel={...this._splitSel,pts:[...d,i]}):void this._showToast(this._t("toast.split_pick_inside"))}const p=function(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(Pe(s,n,i))return null;const o=Ge(t,s,i),r=Ge(t,n,i);if(o<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!Oe(e,t,i))return null;for(let i=0;i{const r=[e];let a=(s+1)%t.length;for(let e=0;e<=t.length&&(r.push(t[a]),a!==o);e++)a=(a+1)%t.length;return r.push(n),Ke(r,i)};let c,h;if(o===r){const r=Ke([...e],i);if(r.length<3||Be(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}c=Ke(a,i),h=r}else c=Ke([...l(s,o,n,r),...[...a].reverse()],i),h=Ke([...l(n,r,s,o),...a],i);return c.length<3||h.length<3||Be(c)<=i||Be(h)<=i||Math.abs(Be(c)+Be(h)-Be(t))>Math.max(i,1e-6*Be(t))?null:[c,h]}(n,[...d,c],o);if(!p)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,g=Be(u)>=Be(_)?u:_,m=g===u?_:u;this._pendingSplit={roomId:s.id,mainPoly:g,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("opening"===this._tool||"openwall"===this._tool||"wallthick"===this._tool)return this._opShift=!!t.shiftKey,void(this._cursorPt=this._svgPoint(t));const e="draw"===this._tool&&this._path.length&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;(e||i)&&(this._cursorPt=this._snap(this._svgPoint(t),t))}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),n=De(t,this._spaceModel().rooms,e);if(!n)return null;const o=this._opRuler(n,s,this._opShift);return{x:o.x,y:o.y,angle:o.angle,rlen:s,measure:o.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._spaceH,i=!!this._pendingSplit;let s;if(this._pendingSplit){const i=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!i)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);i.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Ar,t[1]/e]),delete i.x,delete i.y,delete i.w,delete i.h,s=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;s=this._path.slice(0,-1)}const n=this._areaSel?this.hass.areas[this._areaSel]?.name:"",o={id:"r"+Date.now().toString(36),name:this._nameSel||n||this._t("room.default_name"),area:this._areaSel||null,poly:s.map(t=>[t[0]/Ar,t[1]/e]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(o),i&&this._commitOpenSpans(),!i){const e=this._drawWallCm;if(null!=e){this._cfgEpoch++;const i=this._openCuts();let s=function(t,e,i,s,n,o=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=Gn(e,i,o,n,r);if(!a)return t?t.slice():[];const l=Kn(t,a,n,r);let c=t?t.slice():[];for(let t=0;t0||(c=On(c,e,i,s,n,r))}return c}(t.walls,this._spaceModel().rooms,o.id,e,this._wallKeyPitch,i,Ar);s=this._normalizeWalls(s,i),s.length?t.walls=s:delete t.walls}}this._saveConfig(),this._path=[],this._pendingSplit=null,this._splitSel=null;const r=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let a=0;if(r){const t=Ar,e={...this._layout};for(const i of this._devices){if(i.area!==r||i.space!==this._space)continue;if(a++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/Ar,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const l=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(r?this._t("toast.room_saved",{n:l,added:a}):this._t("toast.room_saved_no_area",{n:l}))}_cancelPath(){this._path=[],this._cursorPt=null,this._roomDialog=!1,this._pendingSplit=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._openWallAnchor=null}_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: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:[...t.marker?.controls||[]],controlsFilter:"",isLight:!0===t.marker?.is_light,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 li)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=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&e.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&e.add("entity:"+t.bindingRef));const i=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&i.add(t.name.trim()+"|"+(t.area||""));const s=[];for(const n of Object.values(t.devices)){if("service"===n.entry_type)continue;const t="device:"+n.id;if(e.has(t))continue;const o=(n.name_by_user||n.name||n.id).trim();t!==this._markerDialog?.binding&&i.has(o+"|"+(n.area_id||""))||s.push({value:t,label:o,sub:(n.model||this._t("marker.sub_device"))+("Group"===n.model?this._t("marker.sub_z2m_group"):"")})}const n=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[i,o]of Object.entries(t.entities)){const r="entity:"+i;if(e.has(r))continue;const a=n.has(o.platform),l="group"===o.platform;if(!a&&!l)continue;if(o.hidden)continue;const c=t.states[i];s.push({value:r,label:o.name||c?.attributes?.friendly_name||i,sub:i.split(".")[0]+" · "+("group"===o.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const i=new Set(s.map(t=>t.value));for(const[n,o]of Object.entries(t.entities)){const r="entity:"+n;if(e.has(r)||i.has(r)||o.hidden)continue;const a=t.states[n],l=o.name||a?.attributes?.friendly_name||n,c=o.device_id?t.devices[o.device_id]:null,h=c&&(c.name_by_user||c.name)||"";s.push({value:r,label:l,sub:n.split(".")[0]+" · "+this._t("marker.sub_entity")+(h?" · "+h:"")})}}const o=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),r=o?s.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(o)):s;return r.sort((t,e)=>t.label.localeCompare(e.label)),r.slice(0,200)}_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",n=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const t={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||t.name,url:o.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}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 n=s?.space||null,o=s?.area||null;const r=s?.roomId||null;"virtual"!==t.binding||n||(n=this._space),i=function(t,e,i){const[s,n]=t.split(":");return"device"===s?n:"entity"===s?"lg_"+n:e&&e.startsWith("v_")?e:i()}(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const a=t.devId,l=e.markers.find(t=>t.id===i||t.id===a)?.vacuum||null,c={id:i,vacuum:l,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"badge"!==t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"badge"!==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:t.controls.length?t.controls:null,is_light:!!t.isLight||null,use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=parseFloat(t.glowRadius);return!Number.isFinite(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)&&(c.space=n,c.area=o,c.room_id=r);const h=a?this._devices.find(t=>t.id===a):null,d=h?.marker?.room_id??null,p=!!t.room&&null!=h&&(h.space!==n||h.area!==o||d!==r);let u=!1;const _=t.uploadId||a;if(_&&_!==i&&c.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:_,to_id:i}),e=t?.mapping||{};c.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const n="/files/"+e+"/",o="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(n))return t;const e=t.url.split(n)[1]||"",[i,r]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(n+i)[0]+o+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(n).join(o)}})}(c.pdfs,_,i,e),u=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),e.markers.push(c);let g=null;const m=n||h?.space||this._space,f=a?this._layout[a]:null,v=f?{s:f.s||h?.space||this._space,x:f.x,y:f.y}:a&&h&&this._defPos[a]?this._normPos(h.space,this._defPos[a].x,this._defPos[a].y):null;if(v&&v.s===m)i===a&&this._layout[i]&&!p||(g={s:v.s,x:v.x,y:v.y},this._layout={...this._layout,[i]:g});else if(!this._layout[i]||p){const t=this._spaceModel(n||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):o?t.rooms.find(t=>t.area===o):void 0;a&&([e,s]=this._roomCenter(a)),g=this._normPos(n||this._space,e,s),this._layout={...this._layout,[i]:g}}await this._saveConfigNow(),g&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:i,pos:g})),a&&a!==i&&(delete this._layout[a],await this.hass.callWS({type:"houseplan/layout/delete",device_id:a}).then(t=>this._noteLayoutRev(t)).catch(()=>{})),u&&_&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:_}).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)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null,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||[],e&&"virtual"===e.bindingKind?s.markers=s.markers.filter(t=>t.id!==e.id):e&&e.marker?(s.markers=s.markers.filter(t=>t.id!==e.id),"device"===e.bindingKind&&e.bindingRef?s.markers.push({id:e.id,binding:"device:"+e.bindingRef,hidden:!0}):"entity"===e.bindingKind&&e.bindingRef&&s.markers.push({id:e.id,binding:"entity:"+e.bindingRef,hidden:!0})):e&&"device"===e.bindingKind&&e.bindingRef?s.markers.push({id:e.id,binding:"device:"+e.bindingRef,hidden:!0}):e&&"entity"===e.bindingKind&&e.bindingRef&&s.markers.push({id:e.id,binding:"entity:"+e.bindingRef,hidden:!0});try{await this._saveConfigNow(),e&&"virtual"===e.bindingKind&&this._layout[e.id]&&(delete this._layout[e.id],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e.id}).then(t=>this._noteLayoutRev(t)).catch(()=>{})),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Ar,y:i/Ar}}_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=fi(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:Ss({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:gi,roomOpacity:mi,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 n=new Uint8Array(await i.arrayBuffer());let o="";for(let t=0;t{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=a});URL.revokeObjectURL(a),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:r,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return W`
${this._t("space.loading")}
`;const e=t.saved||[];if(!e.length)return W`
${this._t("space.no_saved")}
`;return W`
${e.map(e=>W`
@@ -2858,7 +2858,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
- `}_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 Ss(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Ms(this._sunGlobal(),this._sunSpace())}_effSunRays(){return Cs(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Ts(this.hass):null}_renderSunRays(t){const e=B``;if(this._editing||!this._effSunRays())return this._sunFadeReset(),e;const i=this._effNorth(),s=null!==i?Ts(this.hass):null;if(!s||s.elevation<=0)return this._sunFadeReset(),e;const n=Ds(this._sunGlobal()),o=function(t){if(!t)return 1;const e=$s[String(t).toLowerCase()];return void 0===e?1:e}(n?this.hass?.states?.[n]?.state:null),r=function(t=1){return.3*gs(t)}(o);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:Se(t)})).filter(t=>!!t.id&&!!t.poly),n=this._openingsR.filter(t=>"window"===t.type).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),o=this._spaceWalls,r=this._openPairs().flatMap(t=>t.segs),a={},c={};if(o.length){for(const i of e){const e=Qn(t.rooms,i.id,o,r,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr);e&&(a[i.id]=e)}for(const e of n){const i=no(t.rooms,{x:e.x,y:e.y,angle:e.angle,length:e.length},o,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr);i.cm>0&&(c[e.id]=xn(i.cm,this._cellCm,this._gridPitch))}}const h=ws(e,n,s.azimuth,s.elevation,i,o.length?a:void 0,o.length?c:void 0);this._sunRaysCache={key:l,rays:h,rims:h.map(t=>function(t,e=1e-4){const[i,s]=t.dir,n=-s,o=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((h[0]-a[0])*n+(h[1]-a[1])*o)>e)continue;const d=(c[0]-a[0])*i+(c[1]-a[1])*s,p=(h[0]-a[0])*i+(h[1]-a[1])*s;Math.abs(p-d)<=e||l.push(dt[0]-e[0]);const c=[];for(const t of l){const i=c[c.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):c.push([t[0],t[1]])}for(const[t,e]of c)r.push([[a[0]+i*t,a[1]+s*t],[a[0]+i*e,a[1]+s*e]])}return r}(t))}}const c=this._sunRaysCache.rays,h=this._sunRaysCache.rims;if(!c.length)return e;const d=(p=ms(s.elevation).warmth,ki("#ffe9c2","#ff9a45",gs(p)));var p;const u=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],_=function(t=1){return.42*gs(t)}(o),g=[[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 Ss(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Ms(this._sunGlobal(),this._sunSpace())}_effSunRays(){return Cs(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Ts(this.hass):null}_renderSunRays(t){const e=B``;if(this._editing||!this._effSunRays())return this._sunFadeReset(),e;const i=this._effNorth(),s=null!==i?Ts(this.hass):null;if(!s||s.elevation<=0)return this._sunFadeReset(),e;const n=Ds(this._sunGlobal()),o=function(t){if(!t)return 1;const e=$s[String(t).toLowerCase()];return void 0===e?1:e}(n?this.hass?.states?.[n]?.state:null),r=function(t=1){return.3*gs(t)}(o);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:Se(t)})).filter(t=>!!t.id&&!!t.poly),n=this._openingsR.filter(t=>"window"===t.type).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),o=this._spaceWalls,r=this._openPairs().flatMap(t=>t.segs),a={},c={};if(o.length){for(const i of e){const e=Qn(t.rooms,i.id,o,r,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar);e&&(a[i.id]=e)}for(const e of n){const i=no(t.rooms,{x:e.x,y:e.y,angle:e.angle,length:e.length},o,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar);i.cm>0&&(c[e.id]=xn(i.cm,this._cellCm,this._gridPitch))}}const h=ws(e,n,s.azimuth,s.elevation,i,o.length?a:void 0,o.length?c:void 0);this._sunRaysCache={key:l,rays:h,rims:h.map(t=>function(t,e=1e-4){const[i,s]=t.dir,n=-s,o=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((h[0]-a[0])*n+(h[1]-a[1])*o)>e)continue;const d=(c[0]-a[0])*i+(c[1]-a[1])*s,p=(h[0]-a[0])*i+(h[1]-a[1])*s;Math.abs(p-d)<=e||l.push(dt[0]-e[0]);const c=[];for(const t of l){const i=c[c.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):c.push([t[0],t[1]])}for(const[t,e]of c)r.push([[a[0]+i*t,a[1]+s*t],[a[0]+i*e,a[1]+s*e]])}return r}(t))}}const c=this._sunRaysCache.rays,h=this._sunRaysCache.rims;if(!c.length)return e;const d=(p=ms(s.elevation).warmth,ki("#ffe9c2","#ff9a45",gs(p)));var p;const u=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],_=function(t=1){return.42*gs(t)}(o),g=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]];return B` ${c.map((t,e)=>{const i=(t.a[0]+t.b[0])/2,s=(t.a[1]+t.b[1])/2,n=i+t.normal[0]*t.depth,o=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 ms(ks(t.elevation)).bg}const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":vi(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._serverCfg={...this._serverCfg,spaces:t.spaces},this._layout=t.layout;for(const e of Object.keys(t.layout))this._dirtyPos.add(e);this._modelCache=null,this._frame=null,await this._saveConfigNow(),await this._persistLayoutNow(),this._alignDialog=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}async _persistLayoutNow(){if(!this._serverStorage)return localStorage.setItem(Mr,JSON.stringify(this._layout)),void this._dirtyPos.clear();const t=[...this._dirtyPos];this._dirtyPos.clear(),await Promise.all(t.map(t=>{const e=this._layout[t];return e?(this._sentPos.set(t,e),this.hass.callWS({type:"houseplan/layout/update",device_id:t,pos:e}).then(t=>this._noteLayoutRev(t)).finally(()=>{this._sentPos.get(t)===e&&this._sentPos.delete(t)})):Promise.resolve()})),this._cacheSnapshot()}_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(bi),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const n=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(n)&&n>0&&300!==Math.round(n)?s.glow_radius_cm=Math.round(n):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 o=(t.weatherEntity||"").trim();o?s.weather_entity=o: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,n){const o=t=>{const e=Number(t.target.value);Number.isFinite(e)&&n(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 ms(ks(t.elevation)).bg}const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":vi(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._serverCfg={...this._serverCfg,spaces:t.spaces},this._layout=t.layout;for(const e of Object.keys(t.layout))this._dirtyPos.add(e);this._modelCache=null,this._frame=null,await this._saveConfigNow(),await this._persistLayoutNow(),this._alignDialog=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}async _persistLayoutNow(){if(!this._serverStorage)return localStorage.setItem(Cr,JSON.stringify(this._layout)),void this._dirtyPos.clear();const t=[...this._dirtyPos];this._dirtyPos.clear(),await Promise.all(t.map(t=>{const e=this._layout[t];return e?(this._sentPos.set(t,e),this.hass.callWS({type:"houseplan/layout/update",device_id:t,pos:e}).then(t=>this._noteLayoutRev(t)).finally(()=>{this._sentPos.get(t)===e&&this._sentPos.delete(t)})):Promise.resolve()})),this._cacheSnapshot()}_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(bi),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const n=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(n)&&n>0&&300!==Math.round(n)?s.glow_radius_cm=Math.round(n):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 o=(t.weatherEntity||"").trim();o?s.weather_entity=o: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,n){const o=t=>{const e=Number(t.target.value);Number.isFinite(e)&&n(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,n=t.rooms.map(t=>({r:t,poly:Se(t)})).filter(t=>!!t.poly),o=this._openingsR.filter(t=>"door"===t.type),r=[];for(const a of this._devices){if(a.space!==t.id)continue;if(a.hidden)continue;const l=Lo(this.hass,a);if(!l)continue;const c=Ti(this.hass.states[l],e.glow_light.c);if(!c)continue;const h=Number(a.marker?.glow_radius_cm),d=Number.isFinite(h)&&h>0?h/this._cellCm*this._gridPitch:i,p=this._pos(a),u=[...n].reverse().find(t=>this._pointInRoom([p.x,p.y],t.r));let _=null;if(u){const e=u.r.id?Ei(u.r.id,t.rooms):new Set([u.r.id]),i=n.filter(t=>t.r.id&&e.has(t.r.id)),r=i.length?i:[u],a=this._spaceWalls,l=this._openPairs().flatMap(t=>t.segs),c=r.map(e=>"M "+(a.length&&e.r.id&&Qn(t.rooms,e.r.id,a,l,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)||e.poly).map(t=>t[0]+" "+t[1]).join(" L ")+" Z"),h=n.filter(t=>!r.includes(t)).map(t=>t.poly);for(const t of o){const e=r.some(e=>{const i=Ee([t.rx,t.ry],e.poly);return i&&Math.hypot(i[0]-t.rx,i[1]-t.ry)<=.75*s});if(!e)continue;const i=t.angle*Math.PI/180,n=Math.cos(i)*t.rlen/2,o=Math.sin(i)*t.rlen/2;if(!Pi([t.rx,t.ry],t.angle,[p.x,p.y],h,.6*s))continue;const a=zi([p.x,p.y],[t.rx-n,t.ry-o],[t.rx+n,t.ry+o],d);a&&c.push("M "+a.map(t=>t[0]+" "+t[1]).join(" L ")+" Z")}_=c}r.push({pos:p,c:c.c,alpha:e.glow_light.a*c.bri,clip:_,r:d})}return r.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,n=t.rooms.map(t=>({r:t,poly:Se(t)})).filter(t=>!!t.poly),o=this._openingsR.filter(t=>"door"===t.type),r=[];for(const a of this._devices){if(a.space!==t.id)continue;if(a.hidden)continue;const l=qo(this.hass,a);if(!l)continue;const c=Ti(this.hass.states[l],e.glow_light.c);if(!c)continue;const h=Number(a.marker?.glow_radius_cm),d=Number.isFinite(h)&&h>0?h/this._cellCm*this._gridPitch:i,p=this._pos(a),u=[...n].reverse().find(t=>this._pointInRoom([p.x,p.y],t.r));let _=null;if(u){const e=u.r.id?Ei(u.r.id,t.rooms):new Set([u.r.id]),i=n.filter(t=>t.r.id&&e.has(t.r.id)),r=i.length?i:[u],a=this._spaceWalls,l=this._openPairs().flatMap(t=>t.segs),c=r.map(e=>"M "+(a.length&&e.r.id&&Qn(t.rooms,e.r.id,a,l,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)||e.poly).map(t=>t[0]+" "+t[1]).join(" L ")+" Z"),h=n.filter(t=>!r.includes(t)).map(t=>t.poly);for(const t of o){const e=r.some(e=>{const i=Ee([t.rx,t.ry],e.poly);return i&&Math.hypot(i[0]-t.rx,i[1]-t.ry)<=.75*s});if(!e)continue;const i=t.angle*Math.PI/180,n=Math.cos(i)*t.rlen/2,o=Math.sin(i)*t.rlen/2;if(!Pi([t.rx,t.ry],t.angle,[p.x,p.y],h,.6*s))continue;const a=zi([p.x,p.y],[t.rx-n,t.ry-o],[t.rx+n,t.ry+o],d);a&&c.push("M "+a.map(t=>t[0]+" "+t[1]).join(" L ")+" Z")}_=c}r.push({pos:p,c:c.c,alpha:e.glow_light.a*c.bri,clip:_,r:d})}return r.length?B` ${r.map((t,e)=>B` @@ -3009,7 +3009,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow -
${this._t("gs.about_version",{v:yr})}
+
${this._t("gs.about_version",{v:wr})}
${this._t("gs.about_github")} @@ -3069,7 +3069,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow - `}_saveKioskScale(t){this._kioskScale={...this._kioskScale,...t};try{localStorage.setItem(zr,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(Pr,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])}% @@ -3172,7 +3172,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${e.bg&&this._display(e.bg.href)?B``:V} ${""} ${o.hideDecor&&"decor"!==this._mode?V:this._renderDecorLayer()} - ${(()=>{const t=this._openPairs(),i=new Map,s=t=>(i.has(t)||i.set(t,Se(t)),i.get(t));return e.rooms.filter(t=>t.area||this._markup||o.showBorders).map(i=>{let n="room "+(e.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||i.id!==this._mergeSel&&i.id!==this._splitSel?.roomId||(n+=" picked");let a="";const l=Hi(o.fill,i);if(!this._markup&&(o.showBorders||"none"!==l)){n+=" styled";const t=[];t.push(`--room-stroke:${o.color}`,`--room-stroke-op:${o.showBorders?o.opacity:0}`);const e="glow"===l?this._fillColors.glow_base:"temp"===l?$i("temp",null,"none",this._roomTemp(i),o.tempMin,o.tempMax,this._fillColors):i.area?$i(l,"lqi"===l?this._roomLqi(i.area):null,"light"===l?Yo(this.hass,this._devices,i.area):"none",null,o.tempMin,o.tempMax,this._fillColors):null;e?(n+=" 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(";")}const c=t=>this._showTip(t,i.name,"",r?this._roomLqi(i.area):null,this._roomTemp(i)),h=!e.bg&&!o.showNames&&!this._markup,d=this._roomCenter(i),p=this._markup&&(i.id===this._mergeSel||i.id===this._splitSel?.roomId),u=i.id&&!p?t.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):[],_=p?[]:this._thickWallCuts(),g=u.concat(_);g.length&&(n+=" noedge");const m=s(i),f=this._spaceWalls,v=f.length&&i.id&&m&&Qn(e.rooms,i.id,f,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)||m,b=v?function(t,e,i=1e-6){const s=e.filter(e=>Ue(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ue(e,t,i)))}(v,(y=i,e.rooms.filter(t=>t!==y).map(s).filter(Boolean))):[];var y;const w=i.id||V,k=i.area||V,$=b.length&&v?B`{const t=this._openPairs(),i=new Map,s=t=>(i.has(t)||i.set(t,Se(t)),i.get(t));return e.rooms.filter(t=>t.area||this._markup||o.showBorders).map(i=>{let n="room "+(e.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||i.id!==this._mergeSel&&i.id!==this._splitSel?.roomId||(n+=" picked");let a="";const l=qi(o.fill,i);if(!this._markup&&(o.showBorders||"none"!==l)){n+=" styled";const t=[];t.push(`--room-stroke:${o.color}`,`--room-stroke-op:${o.showBorders?o.opacity:0}`);const e="glow"===l?this._fillColors.glow_base:"temp"===l?$i("temp",null,"none",this._roomTemp(i),o.tempMin,o.tempMax,this._fillColors):i.area?$i(l,"lqi"===l?this._roomLqi(i.area):null,"light"===l?Xo(this.hass,this._devices,i.area):"none",null,o.tempMin,o.tempMax,this._fillColors):null;e?(n+=" 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(";")}const c=t=>this._showTip(t,i.name,"",r?this._roomLqi(i.area):null,this._roomTemp(i)),h=!e.bg&&!o.showNames&&!this._markup,d=this._roomCenter(i),p=this._markup&&(i.id===this._mergeSel||i.id===this._splitSel?.roomId),u=i.id&&!p?t.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):[],_=p?[]:this._thickWallCuts(),g=u.concat(_);g.length&&(n+=" noedge");const m=s(i),f=this._spaceWalls,v=f.length&&i.id&&m&&Qn(e.rooms,i.id,f,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)||m,b=v?function(t,e,i=1e-6){const s=e.filter(e=>Ue(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ue(e,t,i)))}(v,(y=i,e.rooms.filter(t=>t!==y).map(s).filter(Boolean))):[];var y;const w=i.id||V,k=i.area||V,$=b.length&&v?B`t[0]+" "+t[1]).join(" L ")+" Z").join(" ")}" @mousemove=${c} @@ -3193,12 +3193,13 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow x="${d[0]}" y="${d[1]}">${i.name}`:V}`})})()} ${"glow"!==o.fill||this._markup?V:this._renderGlowLayer(e)} ${this._renderSunRays(e)} - ${this._renderOpenWalls(o)} ${this._editing?this._renderAlignGuides():V} ${u?.guide?this._renderOpeningCenterTick(u.guide):V} ${this._markup?this._renderMarkupLayer(i):V} ${""} ${this._renderWallBodies(o)} + ${""} + ${this._renderOpenWalls(o)} ${o.hideOpenings&&!this._markup?V:this._renderOpenings(o)} ${this._renderWallThickUi()} ${this._markup&&"resize"===this._tool?this._renderResizeLayer(c):V} @@ -3284,7 +3285,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
`:V} ${this._toast?W`
${this._toast}
`:V} - `}_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(Mo(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)}_senseTick(){if(this.hass)for(const t of this._devices){if(t.hidden||!t.primary||!t.primary.startsWith("binary_sensor."))continue;const e=this.hass.states[t.primary];if("motion"!==e?.attributes?.device_class)continue;const i=this._senseRt.get(t.id);i?("on"===e.state&&"off"===i.last&&(i.flashTs=Date.now(),i.gen++,clearTimeout(i.timer),i.timer=window.setTimeout(()=>this.requestUpdate(),3360)),i.last=e.state):this._senseRt.set(t.id,{last:e.state,flashTs:0,timer:0,gen:0})}}_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=zo(this.hass.states[i||""]?.state),n=So(this.hass.states[e]?.attributes);let o=this._vacRt.get(t.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(t.id,o)),s&&!o.moving&&(o.trail=[],o.lastPos=null);const r="never"!==Eo(t.marker?.vacuum)&&!n?.path;!s&&o.moving&&(o.endedTs=Date.now(),r&&o.lastPos&&(o.trail=To(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=s;const a=n?.pos;if(s&&a){const t=a.x+":"+a.y;if(t!==o.lastKey){const e=Date.now();o.jump=o.lastTs>0&&e-o.lastTs>1e4,o.lastKey=t,o.lastTs=e,r&&o.lastPos&&(o.trail=To(o.trail,o.lastPos,40)),o.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 V;const i=e.marker?.vacuum||{},s=this._vacSource(e),n=s?So(this.hass?.states[s]?.attributes):null,o=!!(n&&n.rooms.length>=3),r=n?.pos?_i(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(Co(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)}_senseTick(){if(this.hass)for(const t of this._devices){if(t.hidden||!t.primary||!t.primary.startsWith("binary_sensor."))continue;const e=this.hass.states[t.primary];if("motion"!==e?.attributes?.device_class)continue;const i=this._senseRt.get(t.id);i?("on"===e.state&&"off"===i.last&&(i.flashTs=Date.now(),i.gen++,clearTimeout(i.timer),i.timer=window.setTimeout(()=>this.requestUpdate(),3360)),i.last=e.state):this._senseRt.set(t.id,{last:e.state,flashTs:0,timer:0,gen:0})}}_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=Po(this.hass.states[i||""]?.state),n=Mo(this.hass.states[e]?.attributes);let o=this._vacRt.get(t.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(t.id,o)),s&&!o.moving&&(o.trail=[],o.lastPos=null);const r="never"!==No(t.marker?.vacuum)&&!n?.path;!s&&o.moving&&(o.endedTs=Date.now(),r&&o.lastPos&&(o.trail=zo(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=s;const a=n?.pos;if(s&&a){const t=a.x+":"+a.y;if(t!==o.lastKey){const e=Date.now();o.jump=o.lastTs>0&&e-o.lastTs>1e4,o.lastKey=t,o.lastTs=e,r&&o.lastPos&&(o.trail=zo(o.trail,o.lastPos,40)),o.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 V;const i=e.marker?.vacuum||{},s=this._vacSource(e),n=s?Mo(this.hass?.states[s]?.attributes):null,o=!!(n&&n.rooms.length>=3),r=n?.pos?_i(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}
@@ -3301,19 +3302,19 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${a.length?W`
${_i(this._t("vac.cal_maps"),{maps:a.join(", ")})}
`:V} `:V} -
`}_vacMapId(t,e){const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return n=e.mapId,o=s,"default"!==n?n:null!=o?String(o):"default";var n,o}_vacSaveMatrix(t,e,i,s){const n=this._devices.find(e=>e.id===t),o=n?this._vacEnsureMarker(n):this._serverCfg?.markers?.find(e=>e.id===t);if(!o)return!1;const r={...o.vacuum||{}};return r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},o.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate(),!0}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?So(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),n=(s?.rooms||[]).map(t=>({r:t,poly:Se(t)})).filter(({r:t,poly:e})=>t.name&&e).map(({r:t,poly:e})=>{const i=Le(e);return{name:t.name,cx:i[0],cy:i[1]}}),o=Do(i.rooms,n);o?this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),o.matrix)&&(o.residual>50&&this._showToast(_i(this._t("vac.autocal_res_warn"),{rooms:String(o.matched.length)})),this._showToast(_i(this._t("vac.autocal_done"),{rooms:String(o.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartFit(t){const e=this._vacSource(t),i=e?So(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),n=t.marker?.vacuum?.calibration?.[s],o=this._spaceModel(t.space),r=o?.vb||[0,0,Pr,Pr],a=n&&6===n.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 n=180*Math.atan2(-t[1],t[4])/Math.PI;return n=(90*Math.round(n/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:n,mir:i}}(n)||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 n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1,c={ox:0,oy:0,s:.6*Math.min(e[2],e[3])/l,rot:0,mir:!0},h=Ao(c),[d,p]=ko(h,(n+o)/2,(r+a)/2);return c.ox=e[0]+e[2]/2-d,c.oy=e[1]+e[3]/2-p,c}(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,Ao(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=So(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),n={...e.p,...t};this._vacFit={...e,p:Ro(n,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,n=s?.clientWidth||1,o=s?.clientHeight||1;return[e/n*t.w,i/o*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[n,o]=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+n,oy:s.p0.oy+o}};else{const t=So(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),r=Ao(s.p0),[a,l]=ko(r,e[0],e[1]),[c,h]=ko(r,s.fx,s.fy),d=Math.hypot(a-c,l-h)||1,[p,u]=[2*a-c,2*l-h],_=Math.hypot(p+2*n-c,u+2*o-h)/2,g=Math.max(.05,_/d),m={...s.p0,s:s.p0.s*g};this._vacFit={...i,p:Ro(m,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 V;const i=So(this.hass?.states[e.source]?.attributes);if(!i)return V;const s=Ao(e.p),n=[],o=[],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])=>ko(s,t,e));e.forEach(([t,e])=>{o.push(t),r.push(e)});const[i,a]=ko(s,t.cx,t.cy);n.push(B` - ${t.name}`)}let a=V;if(i.pos){const[e,n]=ko(s,i.pos.x,i.pos.y);a=B``}const l=[];if(o.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(...o),n=Math.max(...o),a=Math.min(...r),c=Math.max(...r),h=.022*t.w,d=h/4;for(const[t,s,o,r]of[[i,a,n,c],[n,a,i,c],[n,c,i,a],[i,c,n,a]]){const i=e(o,r);l.push(B``}_vacMapId(t,e){const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return n=e.mapId,o=s,"default"!==n?n:null!=o?String(o):"default";var n,o}_vacSaveMatrix(t,e,i,s){const n=this._devices.find(e=>e.id===t),o=n?this._vacEnsureMarker(n):this._serverCfg?.markers?.find(e=>e.id===t);if(!o)return!1;const r={...o.vacuum||{}};return r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},o.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate(),!0}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?Mo(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),n=(s?.rooms||[]).map(t=>({r:t,poly:Se(t)})).filter(({r:t,poly:e})=>t.name&&e).map(({r:t,poly:e})=>{const i=Le(e);return{name:t.name,cx:i[0],cy:i[1]}}),o=To(i.rooms,n);o?this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),o.matrix)&&(o.residual>50&&this._showToast(_i(this._t("vac.autocal_res_warn"),{rooms:String(o.matched.length)})),this._showToast(_i(this._t("vac.autocal_done"),{rooms:String(o.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartFit(t){const e=this._vacSource(t),i=e?Mo(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),n=t.marker?.vacuum?.calibration?.[s],o=this._spaceModel(t.space),r=o?.vb||[0,0,Ar,Ar],a=n&&6===n.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 n=180*Math.atan2(-t[1],t[4])/Math.PI;return n=(90*Math.round(n/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:n,mir:i}}(n)||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 n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1,c={ox:0,oy:0,s:.6*Math.min(e[2],e[3])/l,rot:0,mir:!0},h=Ro(c),[d,p]=$o(h,(n+o)/2,(r+a)/2);return c.ox=e[0]+e[2]/2-d,c.oy=e[1]+e[3]/2-p,c}(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,Ro(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=Mo(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),n={...e.p,...t};this._vacFit={...e,p:Eo(n,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,n=s?.clientWidth||1,o=s?.clientHeight||1;return[e/n*t.w,i/o*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[n,o]=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+n,oy:s.p0.oy+o}};else{const t=Mo(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),r=Ro(s.p0),[a,l]=$o(r,e[0],e[1]),[c,h]=$o(r,s.fx,s.fy),d=Math.hypot(a-c,l-h)||1,[p,u]=[2*a-c,2*l-h],_=Math.hypot(p+2*n-c,u+2*o-h)/2,g=Math.max(.05,_/d),m={...s.p0,s:s.p0.s*g};this._vacFit={...i,p:Eo(m,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 V;const i=Mo(this.hass?.states[e.source]?.attributes);if(!i)return V;const s=Ro(e.p),n=[],o=[],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])=>$o(s,t,e));e.forEach(([t,e])=>{o.push(t),r.push(e)});const[i,a]=$o(s,t.cx,t.cy);n.push(B` + ${t.name}`)}let a=V;if(i.pos){const[e,n]=$o(s,i.pos.x,i.pos.y);a=B``}const l=[];if(o.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(...o),n=Math.max(...o),a=Math.min(...r),c=Math.max(...r),h=.022*t.w,d=h/4;for(const[t,s,o,r]of[[i,a,n,c],[n,a,i,c],[n,c,i,a],[i,c,n,a]]){const i=e(o,r);l.push(B` `)}}return W`this._vacFitPointer(e,t)} @pointermove=${e=>this._vacFitPointer(e,t)} @pointerup=${e=>this._vacFitPointer(e,t)} - @pointercancel=${e=>this._vacFitPointer(e,t)}>${n}${a}${l}`}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const t=this.renderRoot,e=this._stageEl,i=this._vacLastView,s=t?.querySelectorAll?.(".vacpuck")||[];if(!e||!i||!s.length)return void(this._vacRaf=0);const n=e.getBoundingClientRect();for(const e of s){const s=e.getAttribute("data-mid"),o=e.getBoundingClientRect(),r=i.x+(o.left+o.width/2-n.left)/n.width*i.w,a=i.y+(o.top+o.height/2-n.top)/n.height*i.h;for(const e of t.querySelectorAll(`line.tip[data-mid="${s}"]`))e.setAttribute("x2",r.toFixed(1)),e.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return V;const i=this._space+"|"+e.x+"|"+e.y+"|"+e.w+"|"+e.h,s=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const n=[],o=[];for(const i of t){if(i.hidden||!this._isVacDev(i))continue;const t=this._vacSource(i);if(!t)continue;const r=So(this.hass?.states[t]?.attributes);if(!r)continue;const a=i.marker?.vacuum?.calibration?.[this._vacMapId(i,r)];if(!a||6!==a.length)continue;const l=this._vacRt.get(i.id),c=l?.moving??!1,h=Eo(i.marker?.vacuum),d="always"===h||"cleaning"===h&&c,p=this._vacSrvTrails[i.id],u=this._vacMapId(i,r),_=p?.current?.map_id===u&&Array.isArray(p.current.points)?p.current:null,g=p?.previous?.map_id===u&&Array.isArray(p.previous.points)?p.previous:null;if("always"===h&&g&&g.points.length>1){const t=g.points.map(([t,e])=>{const[i,s]=ko(a,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");o.push(B``)}if(d&&(c||_)){const t=_?.points||r.path||l?.trail||[],e=c&&(_||r.path)&&t.length>1?t.slice(0,-1):t;if(e.length>1){const t=e.map(([t,e])=>{const[i,s]=ko(a,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");if(o.push(B``),c){const[t,s]=ko(a,e[e.length-1][0],e[e.length-1][1]),n=t.toFixed(1),r=s.toFixed(1);o.push(B``)}}}if(!c||!r.pos)continue;const[m,f]=ko(a,r.pos.x,r.pos.y),v=(m-e.x)/e.w*100,b=(f-e.y)/e.h*100,y=l&&l.lastTs>0&&Date.now()-l.lastTs>6e4,w=i.marker?.icon||i.icon||"mdi:robot-vacuum";n.push(W`
this._vacFitPointer(e,t)}>${n}${a}${l}`}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const t=this.renderRoot,e=this._stageEl,i=this._vacLastView,s=t?.querySelectorAll?.(".vacpuck")||[];if(!e||!i||!s.length)return void(this._vacRaf=0);const n=e.getBoundingClientRect();for(const e of s){const s=e.getAttribute("data-mid"),o=e.getBoundingClientRect(),r=i.x+(o.left+o.width/2-n.left)/n.width*i.w,a=i.y+(o.top+o.height/2-n.top)/n.height*i.h;for(const e of t.querySelectorAll(`line.tip[data-mid="${s}"]`))e.setAttribute("x2",r.toFixed(1)),e.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return V;const i=this._space+"|"+e.x+"|"+e.y+"|"+e.w+"|"+e.h,s=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const n=[],o=[];for(const i of t){if(i.hidden||!this._isVacDev(i))continue;const t=this._vacSource(i);if(!t)continue;const r=Mo(this.hass?.states[t]?.attributes);if(!r)continue;const a=i.marker?.vacuum?.calibration?.[this._vacMapId(i,r)];if(!a||6!==a.length)continue;const l=this._vacRt.get(i.id),c=l?.moving??!1,h=No(i.marker?.vacuum),d="always"===h||"cleaning"===h&&c,p=this._vacSrvTrails[i.id],u=this._vacMapId(i,r),_=p?.current?.map_id===u&&Array.isArray(p.current.points)?p.current:null,g=p?.previous?.map_id===u&&Array.isArray(p.previous.points)?p.previous:null;if("always"===h&&g&&g.points.length>1){const t=g.points.map(([t,e])=>{const[i,s]=$o(a,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");o.push(B``)}if(d&&(c||_)){const t=_?.points||r.path||l?.trail||[],e=c&&(_||r.path)&&t.length>1?t.slice(0,-1):t;if(e.length>1){const t=e.map(([t,e])=>{const[i,s]=$o(a,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");if(o.push(B``),c){const[t,s]=$o(a,e[e.length-1][0],e[e.length-1][1]),n=t.toFixed(1),r=s.toFixed(1);o.push(B``)}}}if(!c||!r.pos)continue;const[m,f]=$o(a,r.pos.x,r.pos.y),v=(m-e.x)/e.w*100,b=(f-e.y)/e.h*100,y=l&&l.lastTs>0&&Date.now()-l.lastTs>6e4,w=i.marker?.icon||i.icon||"mdi:robot-vacuum";n.push(W`
`)}return this._vacLastView=e,n.length&&!this._vacRaf&&this._vacRafLoop(),n.length||o.length?W` ${o.length?B`${o}`:V} - ${n}`:V}_renderDevice(t,e,i=!0,s=!1){const n=this._pos(t),o=(n.x-e.x)/e.w*100,r=(n.y-e.y)/e.h*100;let a=t.hidden?"":this._stateClass(t);s&&"on"===a&&Lo(this.hass,t)&&(a="");const l=t.hidden?null:this._liveTemp(t),c=t.hidden?null:this._liveHum(t),h=!i||t.virtual||t.hidden?null:Ho(this.hass,t.entities),d=t.marker,p=d?.display||"badge",u=("ripple"===p||"icon_ripple"===p)&&!t.hidden,_=this._actEntity(t),g=_?this.hass.states[_]:void 0,m="value"!==p||t.hidden||null!=l||null!=c||!g||isNaN(parseFloat(g.state))?null:ci(this.hass,_),f="value"!==p||t.hidden?null:null!=l?l+"°":null!=c?c+"%":m?m.formatted?hi(m,g.attributes?.unit_of_measurement):parseFloat(g.state)+(g.attributes?.unit_of_measurement?" "+g.attributes.unit_of_measurement:""):null,v=_?_.split(".")[0]:null,b=this._config?.live_states&&!t.hidden?Ci(t.icon,v,g?.attributes?.device_class,g?.state,!!d?.icon):t.icon,y=(d?.controls||[]).filter(Ai),w=this._config?.live_states&&!t.hidden?y.length?y.map(t=>Di(this.hass.states[t])).find(t=>t)||null:"light"===v?Di(g):null:null,k=this._config?.live_states&&!t.hidden&&function(t,e,i){return"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&ji.has(e))}(v,g?.attributes?.device_class,g?.state),$=u&&!t.hidden&&!!_&&ze(this.hass.states[_]?.state),x=Number(d?.size)>0?Number(d.size):1,S=Number(d?.angle)||0,M=Number(d?.ripple_size)>0?Number(d.ripple_size):3,C=[`left:${o}%`,`top:${r}%`];return 1!==x&&C.push(`--dev-scale:${x}`),u&&(C.push(`--ripple-scale:${M}`),d?.ripple_color?C.push(`--ripple-color:${d.ripple_color}`):w&&C.push(`--ripple-color:${w}`)),W`
Di(this.hass.states[t])).find(t=>t)||null:"light"===v?Di(g):null:null,k=this._config?.live_states&&!t.hidden&&function(t,e,i){return"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&ji.has(e))}(v,g?.attributes?.device_class,g?.state),$=u&&!t.hidden&&!!_&&ze(this.hass.states[_]?.state),x=Number(d?.size)>0?Number(d.size):1,S=Number(d?.angle)||0,M=Number(d?.ripple_size)>0?Number(d.ripple_size):3,C=[`left:${o}%`,`top:${r}%`];return 1!==x&&C.push(`--dev-scale:${x}`),u&&(C.push(`--ripple-scale:${M}`),d?.ripple_color?C.push(`--ripple-color:${d.ripple_color}`):w&&C.push(`--ripple-color:${w}`)),W`
${l}°`:V} ${null!=c&&null==f?W`${c}%`:V} ${null!=h?W`${h}`:V} -
`}_roomTemp(t){const e=t.settings?.temp_source;return e?Xo(this.hass,e,"temp"):t.area?this._climate().get(t.area)?.temp??null:null}_roomHum(t){const e=t.settings?.hum_source;return e?Xo(this.hass,e,"hum"):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 n=new Set;for(const t of i||[]){if(!0!==t?.use_climate_temp)continue;const e=(t.binding||"").indexOf(":");e>0&&n.add(t.binding.slice(e+1))}const o=new Map;for(const[e,i]of Object.entries(t.entities)){const s=i.device_id?t.devices?.[i.device_id]:null,r=i.area_id||s?.area_id||null;if(!r)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(ht.has(i.platform))continue;if(Zo.test(e))continue}let a=o.get(r);a||(a=new Map,o.set(r,a));const l=i.device_id||e;let c=a.get(l);if(!c){const n=t.states?.[e];c={name:(s?s.name_by_user||s.name:i.name||n?.attributes?.friendly_name||e)||e,model:s?.model,ents:[]},a.set(l,c)}c.ents.push(e)}for(const[i,r]of o){const o=[],a=[];for(const[i,s]of r){const r=Vo(t,s.name,s.model,s.ents,e),l="mdi:thermometer"===r||"mdi:air-filter"===r;if(l){const e=qo(t,s.ents);null!=e&&o.push(e)}if(n.size>0&&(n.has(i)||s.ents.some(t=>n.has(t)))){const e=Uo(t,s.ents);null!=e&&o.push(e)}if(l||"mdi:water-percent"===r){const e=Bo(t,s.ents);null!=e&&a.push(e)}}(o.length||a.length)&&s.set(i,{temp:o.length?Math.round(o.reduce((t,e)=>t+e,0)/o.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=qi(t.settings?.name_scale),this._roomLabelScale=qi(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=this._roomSrcFilter.trim().toLowerCase(),i=[];for(const s of Object.values(t.devices)){if("service"===s.entry_type)continue;const t=(s.name_by_user||s.name||s.id).trim();e&&!t.toLowerCase().includes(e)||i.push({value:"device:"+s.id,label:t,sub:s.model||this._t("marker.sub_device")})}for(const[s,n]of Object.entries(t.entities)){if(!s.startsWith("sensor.")||n.hidden)continue;const o=n.name||t.states[s]?.attributes?.friendly_name||s;e&&!(o+" "+s).toLowerCase().includes(e)||i.push({value:"entity:"+s,label:o,sub:s})}return i.sort((t,e)=>t.label.localeCompare(e.label)),i.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*Pr,y:i.y*Pr};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},Nr(t),this._tip=null}_labelMove(t,e,i){const s="rl_"+(e.id||"");if(!this._drag||this._drag.id!==s)return;const n=this._stageEl;if(!n)return;const o=this._spaceModel(i).vb,r=n.getBoundingClientRect(),a=this._viewOr(o),l=(t.clientX-this._drag.sx)/r.width*a.w,c=(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 h=Ws(this._drag.ox+l),d=Ws(this._drag.oy+c);this._savePos({id:s,space:i},h,d,t.shiftKey)}_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 n=s.getBoundingClientRect(),o=n.left+n.width/2,r=n.top+n.height/2,a=Math.max(8,Math.hypot(t.clientX-o,t.clientY-r));this._rlResize={id:"rl_"+(e.id||""),space:i,k0:this._labelScale(e),cx:o,cy:r,d0:a},Nr(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))),n=this._layout[e.id];if(n)this._layout={...this._layout,[e.id]:{...n,k:s}};else{const t=e.id.slice(3),i=this._spaceModel(e.space).rooms.find(e=>e.id===t);if(!i)return;const n=this._labelPos(i,e.space);this._layout={...this._layout,[e.id]:{s:e.space,x:n.x/Pr,y:n.y/Pr,k:s}}}this._dirtyPos.add(e.id)}_rlResizeUp(){this._rlResize&&(this._rlResize=null,this._persistLayout())}_renderRoomGear(t,e,i){if(!t.id)return V;let s=null;if(t.poly?(s=this._gearPtCache.get(t.poly)||null,s||(s=Le(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 V;const n=(s[0]-i.x)/i.w*100,o=(s[1]-i.y)/i.h*100;return W``}_renderRoomLabel(t,e,i,s){if(!t.name&&!this._markup)return V;const n=this._labelPos(t,e.id),o=(n.x-i.x)/i.w*100,r=(n.y-i.y)/i.h*100,a=Math.min(1,s.opacity+.25),l=this._labelScale(t),c=[];if(t.area||t.settings?.temp_source||t.settings?.hum_source){if(s.labelTemp){const e=this._roomTemp(t);null!=e&&c.push(W`${e}°`)}if(s.labelHum){const e=this._roomHum(t);null!=e&&c.push(W`${e}%`)}if(s.labelLqi&&t.area){const e=this._roomLqi(t.area);null!=e&&c.push(W`${e}`)}if(s.labelLight&&t.area){const e=function(t,e,i){const s=new Set;let n=0;for(const o of e)if(o.area===i&&!o.hidden)for(const e of o.entities)e.startsWith("light.")&&!s.has(e)&&(s.add(e),"on"===t.states[e]?.state&&n++);return s.size?{on:n,total:s.size}:null}(this.hass,this._devices,t.area);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});c.push(W`${t}`)}}}return W`
this._labelDown(i,t,e.id)} @pointermove=${i=>this._labelMove(i,t,e.id)} @pointerup=${()=>this._labelUp(t)} @@ -3369,12 +3370,12 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @pointerup=${()=>this._rlResizeUp()} @pointercancel=${()=>this._rlResizeUp()}>`):V}
`}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"===this._tool&&this._path.length&&!this._contourClosed?this._path[this._path.length-1]:"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:null}_renderMeasureLabel(t){const e=this._measureAnchor,i=this._cursorPt,s=(i[0]-t.x)/t.w*100,n=(i[1]-t.y)/t.h*100,o=Ui(e,i),r=Math.round(10*o)/10,a=Wi(o);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,n]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-n)<1e-6)return null;const o=(e+s)/2,r=(i+n)/2;if("line"===t.kind){const e=Ui(t.a,t.b);return{x:o,y:r,on45:Wi(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}return{x:o,y:r,on45:!1,text:`${this._fmtLen([e,i],[s,i])} × ${this._fmtLen([s,i],[s,n])}`}}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=Pr,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=Se(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 i=Pr,s=this._decorH,n=this._decorMove?.id;for(const e of this._decorList)e.id!==n&&("line"===e.kind?t.push([e.x1*i,e.y1*s],[e.x2*i,e.y2*s]):"text"===e.kind?t.push([e.x*i,e.y*s]):t.push([e.x*i,e.y*s],[(e.x+e.w)*i,e.y*s],[e.x*i,(e.y+e.h)*s],[(e.x+e.w)*i,(e.y+e.h)*s]));this._decorDraft&&t.push(this._decorDraft.a);for(const i of e.rooms){const e=Se(i);if(e)for(const i of e)t.push(i)}return 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,n=null;for(const o of e)if(!(Math.abs(o[0]-t[0])<1e-6&&Math.abs(o[1]-t[1])<1e-6)){if(Math.abs(o[0]-t[0])<=i){const e=Math.abs(o[1]-t[1]);e>1e-6&&(!s||e1e-6&&(!n||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,n]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-n)<1e-6)return null;const o=(e+s)/2,r=(i+n)/2;if("line"===t.kind){const e=Ui(t.a,t.b);return{x:o,y:r,on45:Wi(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}return{x:o,y:r,on45:!1,text:`${this._fmtLen([e,i],[s,i])} × ${this._fmtLen([s,i],[s,n])}`}}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=Ar,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=Se(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 i=Ar,s=this._decorH,n=this._decorMove?.id;for(const e of this._decorList)e.id!==n&&("line"===e.kind?t.push([e.x1*i,e.y1*s],[e.x2*i,e.y2*s]):"text"===e.kind?t.push([e.x*i,e.y*s]):t.push([e.x*i,e.y*s],[(e.x+e.w)*i,e.y*s],[e.x*i,(e.y+e.h)*s],[(e.x+e.w)*i,(e.y+e.h)*s]));this._decorDraft&&t.push(this._decorDraft.a);for(const i of e.rooms){const e=Se(i);if(e)for(const i of e)t.push(i)}return 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,n=null;for(const o of e)if(!(Math.abs(o[0]-t[0])<1e-6&&Math.abs(o[1]-t[1])<1e-6)){if(Math.abs(o[0]-t[0])<=i){const e=Math.abs(o[1]-t[1]);e>1e-6&&(!s||e1e-6&&(!n||e ${i.map(e=>{const[i,o,r,a]="x"===e.axis?[e.at,e.from[1],e.at,t[1]+Math.sign(t[1]-e.from[1])*n]:[e.from[0],e.at,t[0]+Math.sign(t[0]-e.from[0])*n,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.hass.states[t.contact]?.state:null;return function(t,e,i=!1){return null==e||"unavailable"===e||"unknown"===e?"door"===t?1:0:ze(e)!==!!i?1:0}(t.type,e,!!t.invert)}_renderOpenings(t){const e=this._openingsR;if(!e.length)return B``;const i=t.color,s=this._spaceWalls,n=this._spaceModel().rooms;return B`${e.map(t=>{const e=t.rlen/2,o=this._openingAmt(t),r=o>0&&!!t.contact?"var(--hp-open)":i,a=s.length?no(n,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:t.flip_v},s,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr):{ox:0,oy:0,cm:0},l=a.cm>0?a.cm/this._cellCm*this._gridPitch/2:4,c=t.flip_h?-1:1,h=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*=h,p*=c}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.hass.states[t.contact]?.state:null;return function(t,e,i=!1){return null==e||"unavailable"===e||"unknown"===e?"door"===t?1:0:ze(e)!==!!i?1:0}(t.type,e,!!t.invert)}_renderOpenings(t){const e=this._openingsR;if(!e.length)return B``;const i=t.color,s=this._spaceWalls,n=this._spaceModel().rooms;return B`${e.map(t=>{const e=t.rlen/2,o=this._openingAmt(t),r=o>0&&!!t.contact?"var(--hp-open)":i,a=s.length?no(n,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:t.flip_v},s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar):{ox:0,oy:0,cm:0},l=a.cm>0?a.cm/this._cellCm*this._gridPitch/2:4,c=t.flip_h?-1:1,h=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*=h,p*=c}if("window"===t.type){const t=Math.PI/2*e,i=a.cm>0?B``:V;d=B` `}
- `}}Or.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},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{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},_decorTextDialog:{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}},Or.ZOOM_MAX=8,Or.ZOOM_MIN=1/3,Or._touchSeen=!1,Or._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,Or.styles=sr,customElements.get("houseplan-card")||customElements.define("houseplan-card",Or),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${yr} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); + `}}Ir.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},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{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},_decorTextDialog:{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}},Ir.ZOOM_MAX=8,Ir.ZOOM_MIN=1/3,Ir._touchSeen=!1,Ir._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,Ir.styles=nr,customElements.get("houseplan-card")||customElements.define("houseplan-card",Ir),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${wr} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); diff --git a/dist/houseplan-card.js b/dist/houseplan-card.js index cfa97412..c729d259 100755 --- a/dist/houseplan-card.js +++ b/dist/houseplan-card.js @@ -1,10 +1,10 @@ -const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,g=_?_.emptyScript:"",m=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?g: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}},b=(t,e)=>!a(t,e),y={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:b};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=y){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??y}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=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??b)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,m?.({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,T=`<${D}>`,z=document,P=()=>z.createComment(""),A=t=>null===t||"object"!=typeof t&&"function"!=typeof t,R=Array.isArray,E="[ \t\n\f\r]",N=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,O=/-->/g,I=/>/g,F=RegExp(`>|${E}(?:([^\\s"'>=/]+)(${E}*=${E}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,H=/"/g,q=/^(?: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"),V=Symbol.for("lit-nothing"),G=new WeakMap,K=z.createTreeWalker(z,129);function Y(t,e){if(!R(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const X=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",r=N;for(let e=0;e"===l[0]?(r=n??N,c=-1):void 0===l[1]?c=-2:(c=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?F:'"'===l[3]?H:L):r===H||r===L?r=F:r===O||r===I?r=N:(r=F,n=void 0);const d=r===F&&t[e+1].startsWith("/>")?" ":"";o+=r===N?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,o+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const r=t.length-1,a=this.parts,[l,c]=X(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iR(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!==V&&A(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=G.get(t.strings);return void 0===e&&G.set(t.strings,e=new Z(t)),e}k(t){R(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(P()),this.O(P()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=V}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=J(this,t,e,0),o=!A(t)||t!==this._$AH&&t!==j,o&&(this._$AH=t);else{const s=t;let r,a;for(t=n[0],r=0;r{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(P(),t),t,void 0,i??{})}return n._$AI(t),n})(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 ct=at.litElementPolyfillSupport;ct?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ht=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),dt=[{pattern:"протечк|leak|water sensor",icon:"mdi:water-alert"},{pattern:"клапан|valve",icon:"mdi:pipe-valve"},{pattern:"дым|smoke",icon:"mdi:smoke-detector"},{pattern:"термоголов|trv|radiator",icon:"mdi:radiator"},{pattern:"чайник|kettle|термопот",icon:"mdi:kettle"},{pattern:"сауна|sauna|harvia|парная|парилк",icon:"mdi:hot-tub"},{pattern:"температ|temperature|climate sensor",icon:"mdi:thermometer"},{pattern:"qingping|air monitor|молекул|air quality",icon:"mdi:air-filter"},{pattern:"штор|curtain|blind|shade",icon:"mdi:roller-shade"},{pattern:"розетк|plug|socket|outlet",icon:"mdi:power-socket-de"},{pattern:"выключат|switch",icon:"mdi:light-switch"},{pattern:"лампа|лампочк|bulb|gx53|светильник|rgb|lamp|light strip",icon:"mdi:lightbulb"},{pattern:"камер|camera",icon:"mdi:cctv"},{pattern:"замок|ttlock|lock|sn609|sn9161",icon:"mdi:lock"},{pattern:"ворота|garage|gate",icon:"mdi:garage-variant"},{pattern:"калитк|door|открыт|contact",icon:"mdi:door"},{pattern:"счётчик|счетчик|kws|meter",icon:"mdi:meter-electric"},{pattern:"вводный автомат|breaker|wifimcbn",icon:"mdi:electric-switch"},{pattern:"myheat|котёл|котел|boiler|отоплен|heating",icon:"mdi:water-boiler"},{pattern:"холодильник|fridge",icon:"mdi:fridge"},{pattern:"стиральн|washer|washing",icon:"mdi:washing-machine"},{pattern:"сушилк|dryer",icon:"mdi:tumble-dryer"},{pattern:"пылесос|vacuum|dreame|roborock",icon:"mdi:robot-vacuum"},{pattern:"soundbar",icon:"mdi:soundbar"},{pattern:"колонк|станц|speaker|яндекс|yandex|алиса|alice",icon:"mdi:speaker"},{pattern:"tv|телевизор|hyundaitv|mitv|television",icon:"mdi:television"},{pattern:"keenetic|роутер|router|mesh|access point",icon:"mdi:router-wireless"},{pattern:"ибп|ups|kirpich",icon:"mdi:battery-charging-high"},{pattern:"slzb|координат|zigbee|coordinator",icon:"mdi:zigbee"},{pattern:"motion|движен|presence|присутств",icon:"mdi:motion-sensor"},{pattern:"humidity|влажн",icon:"mdi:water-percent"}];function pt(t){const e=[];for(const i of t)if(i&&"string"==typeof i.pattern&&i.icon)try{e.push({re:new RegExp(i.pattern,"i"),icon:i.icon})}catch{}return e}const ut=pt(dt),_t={temperature:"mdi:thermometer",humidity:"mdi:water-percent",motion:"mdi:motion-sensor",occupancy:"mdi:motion-sensor",door:"mdi:door",window:"mdi:window-closed",garage_door:"mdi:garage-variant",smoke:"mdi:smoke-detector",moisture:"mdi:water-alert",gas:"mdi:gas-cylinder",power:"mdi:meter-electric",energy:"mdi:meter-electric",illuminance:"mdi:brightness-5",co2:"mdi:molecule-co2",pm25:"mdi:air-filter",battery:"mdi:battery"},gt="mdi:chip";function mt(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??ut)if(t.test(s))return e;return gt}const ft=["light","switch","cover","valve","lock","climate","fan","media_player","camera","vacuum","humidifier","water_heater","alarm_control_panel","sensor","binary_sensor","event","button","number","select","update"];var vt=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,bt=Math.ceil,yt=Math.floor,wt="[BigNumber Error] ",kt=wt+"Number primitive has more than 15 significant digits: ",$t=1e14,xt=14,St=9007199254740991,Mt=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],Ct=1e7,Dt=1e9;function Tt(t){var e=0|t;return t>0||t===e?e:e-1}function zt(t){for(var e,i,s=1,n=t.length,o=t[0]+"";sc^i?1:-1;for(a=(l=n.length)<(c=o.length)?l:c,r=0;ro[r]^i?1:-1;return l==c?0:l>c^i?1:-1}function At(t,e,i,s){if(ti||t!==yt(t))throw Error(wt+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Rt(t){var e=t.c.length-1;return Tt(t.e/xt)==e&&t.c[e]%2!=0}function Et(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Nt(t,e,i){var s,n;if(e<0){for(n=i+".";++e;n+=i);t=n+t}else if(++e>(s=t.length)){for(n=i,e-=s;--e;n+=i);t+=n}else eb?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>b?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!vt.test(d=String(t)))return n(p,d,c);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(At(e,2,x.length,"Base"),10==e&&S)return z(p=new M(t),_+p.e+1,g);if(d=String(t),c="number"==typeof t){if(0*t!=0)return n(p,d,c,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(kt+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,h=d.length;la){a=h;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return n(p,String(t),c,e)}c=!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(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(t>St||t!==yt(t)))throw Error(kt+p.s*t);if((a=a-l-1)>b)p.c=p.e=null;else if(a=f)?Et(l,r):Nt(l,r,"0");else if(o=(t=z(new M(t),e,i)).e,a=(l=zt(t.c)).length,1==s||2==s&&(e<=o||o<=m)){for(;ar),l=Nt(l,o,"0"),o+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=o-a)>0)for(o+1==a&&(l+=".");e--;l+="0");return t.s<0&&n?"-"+l:l}function D(t,e){for(var i,s,n=1,o=new M(t[0]);n=10;n/=10,s++);return(i=s+i*xt-1)>b?t.c=t.e=null:i=10;a/=10,n++);if((o=e-n)<0)o+=xt,r=e,l=d[c=0],h=yt(l/p[n-r-1]%10);else if((c=bt((o+1)/xt))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,n=1,r=(o%=xt)-xt+1}else{for(l=a=d[c],n=1;a>=10;a/=10,n++);h=(r=(o%=xt)-xt+n)<0?0:yt(l/p[n-r-1]%10)}if(s=s||e<0||null!=d[c+1]||(r<0?l:l%p[n-r-1]),s=i<4?(h||s)&&(0==i||i==(t.s<0?3:2)):h>5||5==h&&(4==i||s||6==i&&(o>0?r>0?l/p[n-r]:0:d[c-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[(xt-e%xt)%xt],t.e=-e||0):d[0]=t.e=0,t;if(0==o?(d.length=c,a=1,c--):(d.length=c+1,a=p[xt-o],d[c]=r>0?yt(l/p[n-r]%p[r])*a:0),s)for(;;){if(0==c){for(o=1,r=d[0];r>=10;r/=10,o++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);o!=a&&(t.e++,d[0]==$t&&(d[0]=1));break}if(d[c]+=a,d[c]!=$t)break;d[c--]=0,a=1}for(o=d.length;0===d[--o];d.pop());}t.e>b?t.c=t.e=null:t.e=f?Et(e,i):Nt(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(wt+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(At(i=t[e],0,Dt,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(At(i=t[e],0,8,e),g=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(At(i[0],-Dt,0,e),At(i[1],0,Dt,e),m=i[0],f=i[1]):(At(i,-Dt,Dt,e),m=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)At(i[0],-Dt,-1,e),At(i[1],1,Dt,e),v=i[0],b=i[1];else{if(At(i,-Dt,Dt,e),!i)throw Error(wt+e+" cannot be zero: "+i);v=-(b=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(wt+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw y=!i,Error(wt+"crypto unavailable");y=i}else y=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(At(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(At(i=t[e],0,Dt,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(wt+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(wt+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:g,EXPONENTIAL_AT:[m,f],RANGE:[v,b],CRYPTO:y,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,n=t.e,o=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===o||-1===o)&&n>=-Dt&&n<=Dt&&n===yt(n)){if(0===s[0]){if(0===n&&1===s.length)return!0;break t}if((e=(n+1)%xt)<1&&(e+=xt),String(s[0]).length==e){for(e=0;e=$t||i!==yt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===n&&(null===o||1===o||-1===o))return!0;throw Error(wt+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(o=9007199254740992,r=Math.random()*o&2097151?function(){return yt(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,n,o,a=0,l=[],c=new M(u);if(null==t?t=_:At(t,0,Dt),n=bt(t/xt),y)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(n*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(o%1e14),a+=2);a=n/2}else{if(!crypto.randomBytes)throw y=!1,Error(wt+"crypto unavailable");for(e=crypto.randomBytes(n*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(o%1e14),a+=7);a=n/7}if(!y)for(;a=10;o/=10,a++);ai-1&&(null==r[n+1]&&(r[n+1]=0),r[n+1]+=r[n]/i|0,r[n]%=i)}return r.reverse()}return function(s,n,o,r,a){var l,c,h,d,p,u,m,f,v=s.indexOf("."),b=_,y=g;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(n)).pow(s.length-v),k=d,f.c=e(Nt(zt(u.c),u.e,"0"),10,o,t),f.e=f.c.length),h=d=(m=e(s,n,o,a?(l=x,t):(l=t,x))).length;0==m[--d];m.pop());if(!m[0])return l.charAt(0);if(v<0?--h:(u.c=m,u.e=h,u.s=r,m=(u=i(u,f,b,y,o)).c,p=u.r,h=u.e),v=m[c=h+b+1],d=o/2,p=p||c<0||null!=m[c+1],p=y<4?(null!=v||p)&&(0==y||y==(u.s<0?3:2)):v>d||v==d&&(4==y||p||6==y&&1&m[c-1]||y==(u.s<0?8:7)),c<1||!m[0])s=p?Nt(l.charAt(1),-b,l.charAt(0)):l.charAt(0);else{if(m.length=c,p)for(--o;++m[--c]>o;)m[c]=0,c||(++h,m=[1].concat(m));for(d=m.length;!m[--d];);for(v=0,s="";v<=d;s+=l.charAt(m[v++]));s=Nt(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,n,o,r,a=0,l=t.length,c=e%Ct,h=e/Ct|0;for(t=t.slice();l--;)a=((n=c*(o=t[l]%Ct)+(s=h*o+(r=t[l]/Ct|0)*c)%Ct*Ct+a)/i|0)+(s/Ct|0)+h*r,t[l]=n%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var n,o;if(i!=s)o=i>s?1:-1;else for(n=o=0;ne[n]?1:-1;break}return o}function i(t,e,i,s){for(var n=0;i--;)t[i]-=n,n=t[i]1;t.splice(0,1));}return function(s,n,o,r,a){var l,c,h,d,p,u,_,g,m,f,v,b,y,w,k,$,x,S=s.s==n.s?1:-1,C=s.c,D=n.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&n.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(m=(g=new M(S)).c=[],S=o+(c=s.e-n.e)+1,a||(a=$t,c=Tt(s.e/xt)-Tt(n.e/xt),S=S/xt|0),h=0;D[h]==(C[h]||0);h++);if(D[h]>(C[h]||0)&&c--,S<0)m.push(1),d=!0;else{for(w=C.length,$=D.length,h=0,S+=2,(p=yt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),$=D.length,w=C.length),y=$,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(b=f[0],$!=v&&(b=b*a+(f[1]||0)),(p=yt(b/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,h++);z(g,o+(g.e=h+c*xt-1)+1,r,d)}else g.e=c,g.r=+d;return g}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,n=function(t,e,i,s){var n,o=i?e:e.replace(d,"");if(h.test(o))t.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(a,function(t,e,i){return n="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=n?t:e}),s&&(n=s,o=o.replace(l,"$1").replace(c,"0.$1")),e!=o))return new M(o,n);if(M.DEBUG)throw Error(wt+"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 Pt(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,n,o=this;if(null!=t)return At(t,0,Dt),null==e?e=g:At(e,0,8),z(new M(o),t+o.e+1,e);if(!(i=o.c))return null;if(s=((n=i.length-1)-Tt(this.e/xt))*xt,n=i[n])for(;n%10==0;n/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,g)},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,n,o,r,a,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(wt+"Exponent not an integer: "+P(t));if(null!=e&&(e=new M(e)),r=t.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!t.c||!t.c[0])return c=new M(Math.pow(+P(h),r?t.s*(2-Rt(t)):+P(t))),e?c.mod(e):c;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&h.isInteger()&&e.isInteger())&&(h=h.mod(e))}else{if(t.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||r&&h.c[1]>=24e7:h.c[0]<8e13||r&&h.c[0]<=9999975e7)))return o=h.s<0&&Rt(t)?-0:0,h.e>-1&&(o=1/o),new M(a?1/o:o);k&&(o=bt(k/xt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Rt(t)):l=(n=Math.abs(+P(t)))%2,c=new M(u);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):s&&(c=c.mod(e))}if(n){if(0===(n=yt(n/2)))break;l=n%2}else if(z(t=t.times(i),t.e+1,1),t.e>14)l=Rt(t);else{if(0===(n=+P(t)))break;l=n%2}h=h.times(h),o?h.c&&h.c.length>o&&(h.c.length=o):s&&(h=h.mod(e))}return s?c:(a&&(c=u.div(c)),e?c.mod(e):o?z(c,k,g,void 0):c)},p.integerValue=function(t){var e=new M(this);return null==t?t=g:At(t,0,8),z(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===Pt(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return Pt(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=Pt(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&Tt(this.e/xt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return Pt(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=Pt(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,n,o,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/xt,c=t.e/xt,h=r.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?r:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?r:3==g?-0:0)}if(l=Tt(l),c=Tt(c),h=h.slice(),a=l-c){for((o=a<0)?(a=-a,n=h):(c=l,n=d),n.reverse(),e=a;e--;n.push(0));n.reverse()}else for(s=(o=(a=h.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;h[i++]=0);for(e=$t-1;s>a;){if(h[--s]=0;){for(i=0,p=b[n]%m,u=b[n]/m|0,o=n+(r=l);o>n;)i=((c=p*(c=v[--r]%m)+(a=u*c+(h=v[r]/m|0)*p)%m*m+_[o]+i)/g|0)+(a/m|0)+u*h,_[o--]=c%g;_[o]=i}return i?++s:_.splice(0,1),T(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,n=s.s;if(e=(t=new M(t,e)).s,!n||!e)return new M(NaN);if(n!=e)return t.s=-e,s.minus(t);var o=s.e/xt,r=t.e/xt,a=s.c,l=t.c;if(!o||!r){if(!a||!l)return new M(n/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*n)}if(o=Tt(o),r=Tt(r),a=a.slice(),n=o-r){for(n>0?(r=o,i=l):(n=-n,i=a),i.reverse();n--;i.push(0));i.reverse()}for((n=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=n),n=0;e;)n=(a[--e]=a[e]+l[e]+n)/$t|0,a[e]=$t===a[e]?0:a[e]%$t;return n&&(a=[n].concat(a),++r),T(t,a,r)},p.precision=p.sd=function(t,e){var i,s,n,o=this;if(null!=t&&t!==!!t)return At(t,1,Dt),null==e?e=g:At(e,0,8),z(new M(o),t,e);if(!(i=o.c))return null;if(s=(n=i.length-1)*xt+1,n=i[n]){for(;n%10==0;n/=10,s--);for(n=i[0];n>=10;n/=10,s++);}return t&&o.e+1>s&&(s=o.e+1),s},p.shiftedBy=function(t){return At(t,-9007199254740991,St),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,n,o,r=this,a=r.c,l=r.s,c=r.e,h=_+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(+P(r)))||l==1/0?(((e=zt(a)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=Tt((c+1)/2)-(c<0||c%2),s=new M(e=l==1/0?"5e"+c:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+c)):s=new M(l+""),s.c[0])for((l=(c=s.e)+h)<3&&(l=0);;)if(o=s,s=d.times(o.plus(i(r,o,h,1))),zt(o.c).slice(0,l)===(e=zt(s.c)).slice(0,l)){if(s.e0&&_>0){for(o=_%a||a,h=u.substr(0,o);o<_;o+=a)h+=c+u.substr(o,a);l>0&&(h+=c+u.slice(o)),p&&(h="-"+h)}s=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,n,o,r,a,l,c,h,d,p,_,m=this,f=m.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(wt+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+P(l));if(!f)return new M(m);for(e=new M(u),h=s=new M(u),n=c=new M(u),_=zt(f),r=e.e=_.length-m.e-1,e.c[0]=Mt[(a=r%xt)<0?xt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:h:l,a=b,b=1/0,l=new M(_),c.c[0]=0;d=i(l,e,0,1),1!=(o=s.plus(d.times(n))).comparedTo(t);)s=n,n=o,h=c.plus(d.times(o=h)),c=o,e=l.minus(d.times(o=e)),l=o;return o=i(t.minus(s),n,0,1),c=c.plus(o.times(h)),s=s.plus(o.times(n)),c.s=h.s=m.s,p=i(h,n,r*=2,g).minus(m).abs().comparedTo(i(c,s,r,g).minus(m).abs())<1?[h,n]:[c,s],b=a,p},p.toNumber=function(){return+P(this)},p.toPrecision=function(t,e){return null!=t&&At(t,1,Dt),C(this,t,e,2)},p.toString=function(t){var e,i=this,n=i.s,o=i.e;return null===o?n?(e="Infinity",n<0&&(e="-"+e)):e="NaN":(null==t?e=o<=m||o>=f?Et(zt(i.c),o):Nt(zt(i.c),o,"0"):10===t&&S?e=Nt(zt((i=z(new M(i),_+o+1,g)).c),i.e,"0"):(At(t,2,x.length,"Base"),e=s(Nt(zt(i.c),o,"0"),10,t,n,!0)),n<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return P(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),It=class{key;left=null;right=null;constructor(t){this.key=t}},Ft=class extends It{constructor(t){super(t)}},Lt=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,n=null,o=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==n?o=r:n.right=r,n=r,r=e}}return null!=n&&(n.right=r.left,r.left=o),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)}}},Ht=class t extends Lt{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 Ft(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ft(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 Ft(t.key);return function t(e,i){let s,n;do{if(s=e.left,n=e.right,null!=s){const e=new Ft(s.key);i.left=e,t(s,e)}if(null!=n){const t=new Ft(n.key);i.right=t,e=n,i=t}}while(null!=n)}(t,e),e}toSet(){return this.clone()}entries(){return new Wt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Ut(this.wrap())}[Symbol.toStringTag]="[object Set]"},qt=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}},Ut=class extends qt{getValue(t){return t.key}},Wt=class extends qt{getValue(t){return[t.key,t.key]}},Bt=t=>()=>t,jt=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Bt(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Vt(t){const e=t?(e,i,s,n,o)=>e.exponentiatedBy(2).isLessThanOrEqualTo(n.minus(i).exponentiatedBy(2).plus(o.minus(s).exponentiatedBy(2)).times(t)):Bt(!1);return(t,i,s)=>{const n=t.x,o=t.y,r=s.x,a=s.y,l=o.minus(a).times(i.x.minus(r)).minus(n.minus(r).times(i.y.minus(a)));return e(l,n,o,r,a)?0:l.comparedTo(0)}}var Gt=t=>t,Kt=t=>{if(t){const e=new Ht(jt(t)),i=new Ht(jt(t)),s=(t,e)=>e.addAndReturn(t),n=t=>({x:s(t.x,e),y:s(t.y,i)});return n({x:new Ot(0),y:new Ot(0)}),n}return Gt},Yt=t=>({set:t=>{Xt=Yt(t)},reset:()=>Yt(t),compare:jt(t),snap:Kt(t),orient:Vt(t)}),Xt=Yt(),Zt=(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),Jt=(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}}},Qt=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),te=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ee=t=>te(t,t).sqrt(),ie=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return Qt(n,s).div(ee(n)).div(ee(s))},se=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return te(n,s).div(ee(n)).div(ee(s))},ne=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},oe=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},re=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:_e.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:ie(this.point,t.point,s.point),cosine:se(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:n,cosine:o}=e.get(t),{sine:r,cosine:a}=e.get(s);return n.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?o.isLessThan(a)?1:o.isGreaterThan(a)?-1:0:n.isLessThan(0)&&r.isLessThan(0)?o.isLessThan(a)?-1:o.isGreaterThan(a)?1:0:r.isLessThan(n)?-1:r.isGreaterThan(n)?1:0}}},ae=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,n=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}}},le=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&&n){const t=s.getIntersection(n);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(c))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(c))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(n.isLessThan(o)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(n.isGreaterThan(o)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!n.eq(o)){const t=l.minus(r),e=n.minus(i),h=c.minus(a),d=o.minus(s);if(t.isGreaterThan(e)&&h.isLessThan(d))return 1;if(t.isLessThan(e)&&h.isGreaterThan(d))return-1}return n.isGreaterThan(o)?1:n.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++ue,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 n,o,r;const a=re.comparePoints(e,i);if(a<0)n=e,o=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);n=i,o=e,r=-1}const l=new re(n,!0),c=new re(o,!1);return new t(l,c,[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 Xt.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=Jt(e,i);if(null===s)return null;const n=this.leftSE.point,o=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=Zt(e,r)&&0===this.comparePoint(r),c=Zt(i,n)&&0===t.comparePoint(n),h=Zt(e,a)&&0===this.comparePoint(a),d=Zt(i,o)&&0===t.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?a:null;if(c)return h&&n.x.eq(a.x)&&n.y.eq(a.y)?null:n;if(l)return d&&o.x.eq(r.x)&&o.y.eq(r.y)?null:r;if(d&&h)return null;if(d)return o;if(h)return a;const p=((t,e,i,s)=>{if(e.x.isZero())return oe(i,s,t.x);if(s.x.isZero())return oe(t,e,i.x);if(e.y.isZero())return ne(i,s,t.y);if(s.y.isZero())return ne(t,e,i.y);const n=Qt(e,s);if(n.isZero())return null;const o={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=Qt(o,e).div(n),a=Qt(o,s).div(n),l=t.x.plus(a.times(e.x)),c=i.x.plus(r.times(s.x)),h=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(n,this.vector(),r,t.vector());return null===p?null:Zt(s,p)?Xt.snap(p):null}split(e){const i=[],s=void 0!==e.events,n=new re(e,!0),o=new re(e,!1),r=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(n);const a=new t(n,r,this.rings.slice(),this.windings.slice());return re.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),re.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(n.checkForConsuming(),o.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}},ge=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=Xt.snap({x:new Ot(t[0][0]),y:new Ot(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let n=s;for(let e=1,i=t.length;epe.run("union",t,e),be=(t,...e)=>pe.run("intersection",t,e),ye=(t,...e)=>pe.run("difference",t,e);function we(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function ke(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 $e(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 xe(t,e,i=1){const s=t[0].toFixed(i),n=t[1].toFixed(i),o=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 Me(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 Ce(t){const e=[],i=new Set;for(const s of t||[]){const t=Se(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),o=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(o,Math.min(i-o,a));const l=a/i;n={x:e+l*c,y:r+l*h,angle:t}}else n={x:u[0],y:u[1],angle:t}}}return n}function Te(t,e,i,s,n,o=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,c=o;for(const e of Ce(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])>o||s(i[1])>o)continue;const n=(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],h=Math.min(n,r),d=Math.max(n,r),p=h>0?h:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],m=(h+d)/2;return{wallA:g(h),wallB:g(d),sideA:u,sideB:_,midA:g((h-p)/2),midB:g((p+d)/2),wallCenter:g(m),centered:Math.abs(m)<=n}}function ze(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}function Pe(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}function Re(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;let r=o?((t[0]-e[0])*s+(t[1]-e[1])*n)/o:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*n))}function Ee(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let n=0;nn&&r<-n||o<-n&&r>n)&&(a>n&&l<-n||a<-n&&l>n)}function Le(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1;let c=0,h=0,d=0;for(let e=0;e1e-9?[h/(3*c),d/(3*c)]:[(n+o)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!Ae([e,i],t))return-1/0;let s=1/0;for(let n=0;ng&&(g=c,_=[s,l])}if(_){const[t,i]=_,s=(o-n)/e,l=(a-r)/e;for(let e=-4;e<=4;e++)for(let n=-4;n<=4;n++){const o=t+s*e/4,r=i+l*n/4,a=u(o,r);a>g&&(g=a,_=[o,r])}}return _||He(t)||t[0]}function He(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(Oe(s,t,e))return s;for(let s=0;s[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ve(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=ve(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}function Ge(t,e,i){for(let s=0;s1&&Pe(i[0],i[i.length-1],e)&&i.pop(),i}function Ye(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function Xe(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 Ze(t,e,i,s){if(t.length<2)return;const n=e.x+s,o=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}function ai(t){return"opening"===t||"closing"===t}const li=["automation","script","scene"];function ci(t,e,i){const s=String(e??"").trim();if(!s)return null;const n=t?.states?.[s];if(!n)return null;const o=String(i??"").trim(),r=t=>t.slice(0,60);if(o){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)}(n.attributes?.[o]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,n,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=n.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function hi(t,e,i){const s=String(e??"").trim(),n=String(i??"").trim()||s;if(!n)return t.text;const o=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`${o} ${n}`}function di(t,e,i){const s=t??"";if(!(e?.entity||"").trim())return s;const n=function(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],n=s?.state;if(!s||null==n||""===n||"unavailable"===n||"unknown"===n)return"—";const o=(e?.attr||"").trim(),r=ci(t,i,o||null);return null===r?"—":hi(r,o?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}(i,e),o=s.indexOf("{}");return o>=0?s.slice(0,o)+n+s.slice(o+2):s?`${s} ${n}`:n}function pi(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 ui(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}function _i(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 gi="#55606c",mi=.55;function fi(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:gi,opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):mi,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 vi(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 bi={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}},yi=/^#[0-9a-f]{6}$/i;function wi(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(bi)){const s=bi[t],n=i[t];e[t]={c:n&&"string"==typeof n.c&&yi.test(n.c)?n.c:s.c,a:n&&"number"==typeof n.a?Math.min(1,Math.max(0,n.a)):s.a}}return e}function ki(t,e,i){const s=Math.min(1,Math.max(0,i)),n=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),o=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=n.map((t,e)=>Math.round(t+(o[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function $i(t,e,i,s,n,o,r){if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:ki(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(n,o),e=Math.max(n,o);return se?r.temp_hot:r.temp_ok}return null}const xi={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"]},Si=[["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 Mi(t){for(const e of[...Object.values(xi),...Si])if(t===e[0]||t===e[1])return e;return null}function Ci(t,e,i,s,n){if(!s||"unavailable"===s||"unknown"===s)return t;if(n){const i="cover"===e?Mi(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=xi[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const n=Mi(t);return n?"closed"===s?n[0]:n[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 Di(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}function Ti(t,e){if(!t||"on"!==t.state)return null;const i=t.attributes||{},s=Number(i.brightness),n=Number.isFinite(s)&&s>0?Math.max(.15,Math.min(1,s/255)):1,o=i.rgb_color;if(Array.isArray(o)&&o.length>=3&&o.every(t=>Number.isFinite(t)))return{c:`rgb(${o[0]}, ${o[1]}, ${o[2]})`,bri:n};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),n=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,o=t=>Math.round(Math.min(255,Math.max(0,t)));return[o(i),o(s),o(n)]}(r);return{c:`rgb(${t}, ${e}, ${i})`,bri:n}}return{c:e,bri:n}}function zi(t,e,i,s,n=170){const o=Math.hypot(e[0]-t[0],e[1]-t[1]),r=Math.hypot(i[0]-t[0],i[1]-t[1]);if(o<1e-6||r<1e-6||Math.min(o,r)>=s)return null;let a=Math.atan2(e[1]-t[1],e[0]-t[0]),l=Math.atan2(i[1]-t[1],i[0]-t[0])-a;for(;l>Math.PI;)l-=2*Math.PI;for(;l<-Math.PI;)l+=2*Math.PI;const c=n*Math.PI/180;if(Math.abs(l)>c){const t=a+l/2;l=c*Math.sign(l),a=t-l/2}const h=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=a+l*e/8;h.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return h}function Pi(t,e,i,s,n){const o=e*Math.PI/180,r=[-Math.sin(o),Math.cos(o)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*n*a,t[1]+r[1]*n*a];return s.some(t=>Oe(l,t,1e-9))}function Ai(t){return t.startsWith("light.")||t.startsWith("switch.")}function Ri(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let n=0;np||l>p)continue;const u=(n[0]-o[0])*h+(n[1]-o[1])*d,_=(r[0]-o[0])*h+(r[1]-o[1])*d,g=Math.max(0,Math.min(u,_)),m=Math.min(c,Math.max(u,_));m-g>i&&s.push([o[0]+h*g,o[1]+d*g,o[0]+h*m,o[1]+d*m])}}return s}function Ei(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let n=!0;for(;n;){n=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const o of e)if(o.id&&i.has(o.id)&&s(t,o)){i.add(t.id),n=!0;break}}return i}function Ni(t,e,i=1e-6){const s=[];for(const n of t){const t=[n[0],n[1]],o=[n[2],n[3]],r=o[0]-t[0],a=o[1]-t[1],l=Math.hypot(r,a);if(lo||n>o)continue;const r=(s[0]-t[0])*c+(s[1]-t[1])*h,a=(s[2]-t[0])*c+(s[3]-t[1])*h,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],o[0],o[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,n]of d)e-p>i&&s.push([t[0]+c*p,t[1]+h*p,t[0]+c*e,t[1]+h*e]),p=Math.max(p,n);l-p>i&&s.push([t[0]+c*p,t[1]+h*p,o[0],o[1]])}return s}const Oi=864e5,Ii=576e5;function Fi(t){const e=new Set,i=t=>{if("string"!=typeof t||!t)return;const i=Li(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 Li(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 Hi(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i?i:t}function qi(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Ui(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Wi(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Bi(t,e){const i=e[2]-e[0],s=e[3]-e[1],n=i*i+s*s;if(!n)return Math.hypot(t[0]-e[0],t[1]-e[1]);let o=((t[0]-e[0])*i+(t[1]-e[1])*s)/n;return o=Math.max(0,Math.min(1,o)),Math.hypot(t[0]-(e[0]+o*i),t[1]-(e[1]+o*s))}const ji=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Vi=(t,e)=>[t[0]-e[0],t[1]-e[1]],Gi=(t,e)=>[t[0]+e[0],t[1]+e[1]],Ki=(t,e)=>t[0]*e[0]+t[1]*e[1],Yi=t=>Math.hypot(t[0],t[1]);function Xi(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function Qi(t,e,i,s){const n=s||Ji(t,e),o=(e+1)%t.length;return t.map((t,s)=>s===e||s===o?[t[0]+n[0]*i,t[1]+n[1]*i]:[...t])}function ts(t,e,i,s){const n=[],o=Vi(i,e),r=Yi(o);if(rs||h>s)continue;const d=Ki(Vi(o,e),a),p=Ki(Vi(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&n.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return n}function es(t,e,i,s,n){const o=ts(t,e,i,n);if(!o.length)return null;const r=t=>o.some(([e,i])=>Zi(t,e,i)<=n),a=t.length,l=[];for(let o=0;on||f>n)continue;const v=Ki(Vi(e,c),u),b=Ki(Vi(i,c),u),y=Math.max(0,Math.min(v,b)),w=Math.min(p,Math.max(v,b));if(!(w-y<=n)){if(y>n&&yn&&wYi(Vi(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const n=i[(s-1+i.length)%i.length],o=i[(s+1)%i.length],r=(t[0]-n[0])*(o[1]-n[1])-(t[1]-n[1])*(o[0]-n[0]),a=Yi(Vi(o,n))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function ss(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-n[0])*a[1]-(t[1]-n[1])*a[0],c=t=>(t[0]-n[0])*a[0]+(t[1]-n[1])*a[1],h=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const n=c(e);n<=h||n>=d||tMath.abs(r+(t-p)/m*(a-r));s=Math.min(s,f(_),f(g))}}return s}function os(t,e,i){if(We(t,e,i))return!0;if(Ue(t,e,i)||Ue(e,t,i))return!1;let s=0;try{const i=be([[...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+=Be(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function rs(t,e,i,s,n){const o=[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]=Qi(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[Gi(i.a,o),Gi(i.b,o)]];continue}const t=ts(e.poly,i.a,i.b,n);if(!t.length)continue;const a=es(e.poly,i.a,i.b,o,n);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[Gi(t,o),Gi(e,o)]))}for(const t of e)Zi([t.x,t.y],i.a,i.b)<=n&&(r.openings[t.id]=[t.x+o[0],t.y+o[1]]);return r}function as(t,e,i){for(const s of e)for(let e=0;ei)continue;const c=(t.x-n[0])*l[0]+(t.y-n[1])*l[1];if(c-t.length/2>=-i&&c+t.length/2<=a+i)return!0}return!1}function ls(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),n=a.polys[e];if(!ss(n))return!1;const l=Xi(s.poly),h=Xi(n);if(Math.abs(h)t.find(t=>t.id===e).poly),d=t.map(c);for(const t of ls(e,h,2*r)){const e=a.openings[t.id];if(!as(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function hs(t,e,i,s,n,o){const r=t=>[s[0]+(t[0]-s[0])*n,s[1]+(t[1]-s[1])*n],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 c=t.filter(t=>t.id!==i).map(t=>t.poly),h=hs(l,e,c,s,n,2*a),d=h.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 n=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;n.length>=2&&i(n[n.length-2],n[n.length-1],s)<=0;)n.pop();n.push(s)}return s.pop(),n.pop(),s.concat(n)}(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 ls(e,[l.poly],2*a)){const e=h.openings[t.id];if(!as(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function ps(t,e,i){const s=i/e;return Be(t)*s*s/1e4}function us(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const _s=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],gs=t=>Math.min(1,Math.max(0,t));function ms(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=_s[_s.length-1][1];for(let t=1;t<_s.length;t++){const[s,n]=_s[t-1],[o,r]=_s[t];if(e<=o){i=ki(n,r,(e-s)/(o-s));break}}return{bg:i,planDim:.1*gs((10-e)/16),warmth:e<0?1:gs(1-e/10)}}function fs(t,e,i=6){const s=t.angle*Math.PI/180,n=[Math.sin(s),-Math.cos(s)],o=s=>{const o=[t.x+n[0]*i*s,t.y+n[1]*i*s];return e.find(t=>t.poly.length>=3&&Ae(o,t.poly))||null},r=o(1),a=o(-1);return r&&a?null:r||a?r?{normal:[-n[0],-n[1]],roomId:r.id}:{normal:n,roomId:a.id}:null}function vs(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function bs(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 ys(t,e){try{const i=be([[...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 ws(t,e,i,s,n,o,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,n),l=[-a[0],-a[1]],c=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),h=[];for(const i of e){if(!(i.length>0))continue;const e=fs(i,t);if(!e||!vs(e.normal,a,s))continue;const n=t.find(t=>t.id===e.roomId);if(!n)continue;const d=o&&o[e.roomId]||n.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],g=Math.max(0,r?.[i.id]||0),m=i.x+_[0]*g/2,f=i.y+_[1]*g/2,v=Math.cos(p)*u,b=Math.sin(p)*u,y=[m-v,f-b],w=[m+v,f+b],k=c*i.length,$=ys(bs(y,w,l,k),d);if(!$.length)continue;const x=l[0]*_[0]+l[1]*_[1];h.push({openingId:i.id,roomId:e.roomId,polys:$,a:y,b:w,dir:l,len:k,normal:_,depth:k*x})}return h}function ks(t){return Math.round(10*(Number(t)||0))/10}const $s={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 xs=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Ss(t,e){const i=xs(e?.north_deg);return null!==i?i:xs(t?.north_deg)}function Ms(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function Cs(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Ds(t){const e=t?.weather_entity;return"string"==typeof e&&e.trim()?e.trim():null}function Ts(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 zs=1e3;function Ps(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,n=s>=1?e:e*s,o=s>=1?e/s:e;return{x:(e-n)/2,y:(e-o)/2,w:n,h:o}}const As=.01;function Rs(t,e=1e3){const i=Ps(t?.plan_aspect,e),s=Number(t?.plan_scale),n=Number.isFinite(s)&&s>0?Math.min(100,Math.max(As,s)):1,o=Number(t?.plan_x),r=Number(t?.plan_y);return{x:i.x+(Number.isFinite(o)?Bs(o):0)*e,y:i.y+(Number.isFinite(r)?Bs(r):0)*e,w:i.w*n,h:i.h*n}}function Es(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 Ns(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=zs,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]*zs,i[1]*e,i[2]*zs,i[3]*e],bg:t.plan_url?{href:Li(t.plan_url),...Rs(t,zs)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...Es(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*zs:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*zs:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*zs,t[1]*e]):void 0}})}}):[]}const Os=5e3,Is=Os*zs,Fs=240,Ls=zs/Fs,Hs=1/Fs;function qs(t){if(!Number.isFinite(t))return t;const e=Math.round(t*Fs/zs)*zs/Fs;return Math.abs(e-t)<=1e-9*Ls?t:e}function Us(t){return{x:qs(t.x),y:qs(t.y)}}function Ws(t){return Number.isFinite(t)?Math.min(Is,Math.max(-Is,t)):0}function Bs(t){return Number.isFinite(t)?Math.min(Os,Math.max(-Os,t)):0}const js=1/3,Vs=200;function Gs(t){let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t){const t=Number(o[0]),r=Number(o[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>n&&(n=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:n}}function Ks(t){return t.poly&&t.poly.length?Gs(t.poly):null==t.x||null==t.y?null:Gs([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function Ys(t,e){const i=[];for(const e of t.rooms||[]){const t=Ks(e);t&&i.push(t)}t.bg&&i.push({minX:t.bg.x,minY:t.bg.y,maxX:t.bg.x+t.bg.w,maxY:t.bg.y+t.bg.h});for(const t of e||[])if(Array.isArray(t)){const e=Gs([t]);e&&i.push(e)}else i.push(t);return i}const Xs=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},Zs=t=>{let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t)o.minXs&&(s=o.maxX),o.maxY>n&&(n=o.maxY);return e>s||i>n?null:{x:e,y:i,w:s-e,h:n-i}};function Js(t,e){let{x:i,y:s,w:n,h:o}=t;n<30&&(i=i+n/2-100,n=Vs),o<30&&(s=s+o/2-100,o=Vs);const r=Math.max(n,o)*e;return{x:i-r,y:s-r,w:n+2*r,h:o+2*r}}function Qs(t,e={}){const i=e.pad??.05,s=e.k??10,n=e.minSpread??50,o=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=Is&&Math.abs(t.maxX)<=Is&&Math.abs(t.minY)<=Is&&Math.abs(t.maxY)<=Is);if(!o.length)return{core:null,all:null,outliers:0};const r=Zs(o);if(o.length<4){const t=Js(r,i);return{core:t,all:t,outliers:0}}const a=o.map(t=>(t.minX+t.maxX)/2),l=o.map(t=>(t.minY+t.maxY)/2),c=Xs(a),h=Xs(l),d=o.map((t,e)=>Math.max(Math.abs(a[e]-c),Math.abs(l[e]-h))),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),n),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,g=_&&_<=o.length*js?o.filter((t,e)=>!u[e]):o;return{core:Js(Zs(g)||r,i),all:Js(r,i),outliers:g===o?0:_}}function tn(t,e,i=.05){const s=Qs(Ys(t,e),{pad:i});if(s.core)return s.core;const n=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,zs,zs];return{x:n[0],y:n[1],w:n[2],h:n[3]}}function en(t){const e=tn(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function sn(t){const e=[];for(const i of t.rooms||[]){const t=Ks(i);t&&e.push(t)}const i=Qs(e,{pad:0}).core;return i?Math.max(zs,Math.min(Is,Math.max(i.w,i.h))):zs}function nn(t,e,i,s=1){const n=Number(i),o=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(n)||n<=0?t*o:t*sn(e)*o/n}const on=[1,2,5,10,20,50,100,200,500,1e3];function rn(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),n=Math.min(...i);return{x:s,y:n,w:Math.max(...e)-s,h:Math.max(...i)-n}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function an(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 ln=["furniture","appliance","sanitary","other"],cn=[{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]]}],hn=new Map(cn.map(t=>[t.id,t]));function dn(t){return t&&hn.get(t)||null}function pn(t,e,i=Ls,s=1e3){const n=Number(e)>0?Number(e):5;return(Number(t)||0)/n*i/s}const un=5e-4,_n=Os;function gn(t){return Number.isFinite(t)?Math.max(un,Math.min(_n,t)):un}const mn=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function fn(t,e,i){const s=dn(t);if(!(s&&e>0&&i>0))return"";const n=t=>mn(t*e),o=t=>mn(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}H${n(e+s)}V${o(i+a)}H${n(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}L${n(s)} ${o(a)}`)}else if("e"===t[0]){const[,s,a,l,c]=t;r.push(`M${n(s-l)} ${o(a)}A${mn(l*e)} ${mn(c*i)} 0 0 1 ${n(s+l)} ${o(a)}A${mn(l*e)} ${mn(c*i)} 0 0 1 ${n(s-l)} ${o(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${n(e[0])} ${o(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,n,o=0){let r=null,a=n;for(const n of s){const[s,l,c,h]=n,d=c-s,p=h-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let g=((t-s)*d+(e-l)*p)/u;g=Math.max(0,Math.min(1,g));let m=s+g*d,f=l+g*p;const v=Math.hypot(t-m,e-f);if(!(v0){const t=Math.round(g*_/o)*o;m=s+t/_*d,f=l+t/_*p}r={cx:m+b*(i/2),cy:f+y*(i/2),angle:vn(180*Math.atan2(-b,y)/Math.PI),dist:v}}return r}function yn(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function wn(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function kn(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function $n(t,e){const i="number"==typeof t?t:parseFloat(String(t).trim().replace(",","."));if(!Number.isFinite(i)||i<=0)return null;return wn(e?2.54*i:i)}function xn(t,e,i){const s=Number(e)>0?Number(e):5;return wn(t)/s*i}function Sn(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function Mn(t,e){let i=e[0]-t[0],s=e[1]-t[1];const n=Math.hypot(i,s);return n<1e-12?[1,0]:(i/=n,s/=n,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function Cn(t,e,i){const s=Sn((t[0]+e[0])/2,i),n=Sn((t[1]+e[1])/2,i),[o,r]=Mn(t,e);let a=Math.atan2(r,o);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,c=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(c)},${n.toFixed(c)}@${l.toFixed(4)}`}function Dn(t,e,i,s){return 1===s?Cn(t,e,i):Cn([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function Tn(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[n,o]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[n,o,r].every(Number.isFinite)&&s.push({w:e,x:n*i,y:o*i,ang:r})}return s}function zn(t,e){const[i,s]=Mn(t,e);let n=Math.atan2(s,i);return n<0&&(n+=Math.PI),n}function Pn(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function An(t,e,i,s,n=1){if(!t?.length)return null;const o=Dn(e,i,s,n),r=t.find(t=>t.key===o);if(r)return r;const a=n>0?n:1,l=(e[0]+i[0])/2,c=(e[1]+i[1])/2,h=zn(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of Tn(t,a))if(Pn(e.ang,h)&&Math.hypot(e.x-l,e.y-c)<=d)return e.w;return null}function Rn(t,e,i,s,n=1){const o=An(t,e,i,s,n);return o&&o.cm>0?wn(o.cm):0}function En(t,e,i,s=1,n=[]){if(!t?.length)return[];const o=new Set;for(const t of Ce(e))o.add(Dn([t[0],t[1]],[t[2],t[3]],i,s));const r=e||[],a=Math.max(i*s*.02,1e-9);for(let t=0;to.has(t.key)&&t.cm>=1&&t.cm<=100)}function Nn(t,e,i,s=8){const[n,o]=Mn(t,e);let r=Math.atan2(o,n);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 On(t,e,i,s,n,o=1){const r=Dn(e,i,n,o),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,{key:r,cm:wn(s)}]}function In(t,e,i,s,n,o=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,n=1){const o=Gn(t,e,i,s,n);if(!o)return[];const r=[];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),n=Bn(i,t);if(s&&n)return`${jn(s)} ${jn(Vn(n))}`}}let s="";for(let i=0;i.05)&&Jn(r,a,t[0],t[1],t[2],t[3])<=o)return!0}return!1}function Hn(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function qn(t,e){const i=t[e],s=t[(e+1)%t.length],n=s[0]-i[0],o=s[1]-i[1],r=Math.hypot(n,o)||1;let a=-o/r,l=n/r;const c=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,n=e.length-1;st[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r+0)+o&&(i=!i)}return i}([c[0]+.001*a,c[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function Un(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 Wn(t,e,i,s){const n=e[0]*s[1]-e[1]*s[0];if(Math.abs(n)<1e-12)return null;const o=[i[0]-t[0],i[1]-t[1]],r=(o[0]*s[1]-o[1]*s[0])/n;return[t[0]+r*e[0],t[1]+r*e[1]]}function Bn(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 n=0;n0||d>0)){s.push([t[n][0],t[n][1]]);continue}if(Un(y,w)){const e=t[n],i=[e[0]+p*h,e[1]+u*h],o=[e[0]+_*d,e[1]+g*d];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=Wn(k,y,$,w),S=Math.max(h,d,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}h>0&&s.push([t[n][0]+p*h,t[n][1]+u*h]),d>0&&s.push([t[n][0]+_*d,t[n][1]+g*d]),h>0||d>0||s.push([t[n][0],t[n][1]])}return s.length>=3?s:null}function jn(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),r=Se(o);if(!r||r.length<3)return null;const a=Hn(s,n),l=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Se(i);if(t)for(const e of Ri(r,t,a))l.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])l.push([t[0],t[1]],[t[2],t[3]]);const c=[],h=[];for(let t=0;ta)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=n||r>=1-n||(o.some(t=>Math.abs(t-r)*s<=2*a)||o.push(r))}o.sort((t,e)=>t-e);for(const s of o)c.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),h.push(t)}return{poly:c,parent:h,orig:r}}function Kn(t,e,i,s){const n=e.poly.length,o=new Array(n).fill(0);if(!t?.length)return o;const r=new Set,a=[];for(let l=0;l0?(o[l]=wn(c.cm),r.add(c.key)):a.push(l)}if(!a.length)return o;const l=s>0?s:1,c=Math.max(.5*i,1e-9)*l,h=Tn(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],n=e.orig[(t+1)%e.orig.length],a=zn(s,n),l=(s[0]+n[0])/2,d=(s[1]+n[1])/2;let p=null;for(const t of h){if(r.has(t.w.key))continue;if(!Pn(t.ang,a))continue;if(Jn(t.x,t.y,s[0],s[1],n[0],n[1])>c)continue;const e=Math.hypot(t.x-l,t.y-d);(!p||et?.id===e),n=Se(s);if(!n)return[];const o=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Se(s);if(t)for(const e of Ri(n,t,i))o.push(e)}return o}(t,e,Hn(n,a)),h=function(t,e,i,s,n){const o=Hn(s,n),r=[];for(let a=0;aJn(h,d,t[0],t[1],t[2],t[3])<=o);r.push(p?"shared":"outer")}return r}(l.poly,c,s,n,a),d=Kn(i,l,n,a),p=d.map((t,e)=>h[e]&&t>0?xn(t,o,r)/2:0);return{...l,kinds:h,cms:d,offsets:p}}function Xn(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Yn(t,l.id,e,i,s,n,o,r);if(c)for(let t=0;t4*l||(!p||tt?.id===e),c=Se(l);if(!c||c.length<3)return null;if(!i?.length)return c.map(t=>[t[0],t[1]]);const h=Yn(t,e,i,s,n,o,r,a);return h&&h.offsets.some(t=>t>0)&&Bn(h.poly,h.offsets)||c.map(t=>[t[0],t[1]])}function to(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=[],n,o,r,a=1){if(!e?.length)return null;const l=[],c=[];let h=0;for(const s of t||[]){if(!s?.id)continue;const d=Yn(t,s.id,e,i,n,o,r,a);if(!d||d.poly.length<3||!d.offsets.some(t=>t>0))continue;for(const t of d.offsets)t>0&&(h=Math.max(h,2*t));const p=io(d.poly,d.offsets),u=Bn(d.poly,d.offsets);p&&l.push(p),u&&c.push(u)}if(!l.length)return null;try{let t=to(l[0]);for(let e=1;e0))continue;const i=t.angle*Math.PI/180,s=Math.cos(i),o=Math.sin(i),r=-o,l=s,c=t.length/2,d=1.25*Math.max(h,n*a),p=[[t.x-s*c-r*d,t.y-o*c-l*d],[t.x+s*c-r*d,t.y+o*c-l*d],[t.x+s*c+r*d,t.y+o*c+l*d],[t.x-s*c+r*d,t.y-o*c+l*d]];e=ye(e,to(p))}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?" ":"")+jn(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:h}:null}catch{const s=function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Yn(t,l.id,e,i,s,n,o,r);if(!c||c.poly.length<3||!c.offsets.some(t=>t>0))continue;const h=io(c.poly,c.offsets),d=Bn(c.poly,c.offsets);if(!h||!d)continue;const p=`${jn(h)} ${jn(Vn(d))}`;let u="",_="outer",g=0,m=0;for(let t=0;t0){u=Dn(c.poly[t],c.poly[(t+1)%c.poly.length],s,r),_=c.kinds[t]||"outer",g=c.cms[t],m=xn(g,n,o);break}a.push({d:p,key:u,kind:_,cm:g,depthUnits:m})}return a}(t,e,i,n,o,r,a);return s.length?{d:s.map(t=>t.d).join(" "),depthUnits:h}: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]]);Vn(t),e.slice().reverse();const s=[];for(let n=0;n0||a>0)){s.push([t[n][0],t[n][1]]);continue}if(Un(y,w)){const e=t[n],i=[e[0]-l*r,e[1]-c*r],o=[e[0]-h*a,e[1]-d*a];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=Wn(k,y,$,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[n][0]-l*r,t[n][1]-c*r]),a>0&&s.push([t[n][0]-h*a,t[n][1]-d*a])}return s.length>=3?s:null}function so(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){const c=Se(l);if(c&&c.length>=3){const h=Yn(t,l.id,e,i,s,n,o,r),d=h&&h.offsets.some(t=>t>0)?io(h.poly,h.offsets):null,p=d||c;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 no(t,e,i,s,n,o,r=1){let a=null;for(const i of t||[]){const t=Se(i);if(t)for(let s=0;ss*r)return{ox:0,oy:0,cm:0};const l=Rn(i,a.a,a.b,s,r);if(!(l>0))return{ox:0,oy:0,cm:0};const c=xn(l,n,o),h=Se(a.room),[d,p]=qn(h,a.edge),u=e.flip_v?-1:1,_=c/2;return{ox:d*_*u,oy:p*_*u,cm:l}}function oo(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function ro(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function ao(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function lo(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!ao(t.a)||!ao(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],n=[Number(t.b[0]),Number(t.b[1])];Math.hypot(n[0]-s[0],n[1]-s[1])<.001||e.push({a:s,b:n})}return e}function co(t,e){const i=e[0],s=e[1],n=e[2]-i,o=e[3]-s,r=n*n+o*o;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*n+(t[1]-s)*o)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*n,s+a*o];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function ho(t,e){return co(t,e).q}function po(t,e,i,s,n){const o=co(t,e).q;let r=o,a=1/0;for(const t of i){const e=Math.hypot(o[0]-t[0],o[1]-t[1]);e<=n&&e0?s:1,g=Math.round(u/_)*_,m=Math.max(0,Math.min(p,g))/p;return[l+h*m,c+d*m]}function uo(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt?.id);let o=null;for(let e=0;et.seg)}(e,s);let o=null;for(const r of e||[]){if(!r?.id)continue;const e=Se(r);if(e)for(let a=0;ai)continue;let p=!1;for(const t of n)if(Bi([(l[0]+c[0])/2,(l[1]+c[1])/2],t)<2*s){p=!0;break}p||(!o||dt?.id),r=lo(e);if(r.length)return r.map(t=>ro(t,i));if(!n)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;too([t[0],t[1]],[t[2],t[3]],e))}function vo(t,e,i){let s=null;for(const n of e){const e=Bi(t,n);e<=i&&(!s||e.05)continue;const d=Rn(t,[e[0],e[1]],[e[2],e[3]],s,n);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-h[0],p[1]-h[1]);u0?l:o}(t,e,i,s,n,o);return On(t,[e[0],e[1]],[e[2],e[3]],r,s,n)}function yo(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],n=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const o of e)if(!(Bi(n,o)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Bi([o[0],o[1]],t)>i))for(const e of[[o[0],o[1]],[o[2],o[3]]])Bi(e,t)<=2*i&&s.push(e);return s}class wo{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=Li(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],n=s?this.now()-s.at:1/0;return n{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 n=0;for(const o of i){const i=t?.urls?.[o];"string"==typeof i&&i?(s[o]={url:i,at:e},this.retry.delete(o),n++):this.backOff(o)}n&&(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,n]of Object.entries(this.signed))e.has(t)&&i-n.at{const e=Number(t);return Number.isFinite(e)?e:null};function So(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=xo(e.x)&&null!=xo(e.y)?{x:xo(e.x),y:xo(e.y),a:xo(e.a??e.angle??e.theta)}:null;let s=null;const n=t.path?.points??t.path;if(Array.isArray(n)&&n.length){s=[];for(const t of n){const e=xo(Array.isArray(t)?t[0]:t?.x),i=xo(Array.isArray(t)?t[1]:t?.y);null!=e&&null!=i&&s.push([e,i])}s.length||(s=null)}const o=[],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=xo(e.cx??e.center?.x),n=xo(e.cy??e.center?.y);if(null==s||null==n){const t=xo(e.x0),i=xo(e.y0),o=xo(e.x1),r=xo(e.y1);null!=t&&null!=i&&null!=o&&null!=r&&(s=(t+o)/2,n=(i+r)/2)}if(null!=s&&null!=n||(s=xo(e.x),n=xo(e.y)),i&&null!=s&&null!=n){const r={id:t,name:i,cx:s,cy:n},a=xo(e.x0),l=xo(e.y0),c=xo(e.x1),h=xo(e.y1);null!=a&&null!=l&&null!=c&&null!=h&&(r.x0=Math.min(a,c),r.y0=Math.min(l,h),r.x1=Math.max(a,c),r.y1=Math.max(l,h)),o.push(r)}}const l=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||o.length||s?{pos:i,path:s,rooms:o,mapId:l}:null}function Mo(t){const e=t?.attributes;return!(!e||!e.vacuum_position&&!e.robot_position)}const Co=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function Do(t,e){const i=new Map(e.map(t=>[Co(t.name),t])),s=[],n=[];for(const e of t){const t=i.get(Co(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),n.push(e.name))}if(s.length<3)return null;const o=function(t){if(t.length<3)return null;let e=0,i=0,s=0,n=0,o=0,r=0,a=0,l=0,c=0,h=0,d=0,p=0;for(const[[u,_],[g,m]]of t){if(![u,_,g,m].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,n+=_*_,o+=_,r+=1,a+=u*g,l+=_*g,c+=g,h+=u*m,d+=_*m,p+=m}const u=[e,i,s,i,n,o,s,o,r],_=t=>{const[e,i,s,n,o,r,a,l,c]=u,h=e*(o*c-r*l)-i*(n*c-r*a)+s*(n*l-o*a);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-r*l)/h,(s*l-i*c)/h,(i*r-s*o)/h,(r*a-n*c)/h,(e*c-s*a)/h,(s*n-e*r)/h,(n*l-o*a)/h,(i*a-e*l)/h,(e*o-i*n)/h];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]]},g=_([a,l,c]),m=_([h,d,p]);if(!g||!m)return null;const f=[g[0],g[1],g[2],m[0],m[1],m[2]];return f.every(Number.isFinite)?f:null}(s);return o?{matrix:o,matched:n,residual:$o(o,s)}:null}function To(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 n=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[n,o]=s.pop(),[r,a]=t[n],[l,c]=t[o],h=l-r,d=c-a,p=Math.hypot(h,d)||1e-9;let u=0,_=-1;for(let e=n+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([n,_],[_,o]))}const n=[];for(let e=0;e600&&(n=n.filter((t,e)=>e%2==0||e===n.length-1)),n}function zo(t){return"cleaning"===t||"returning"===t||"on"===t}const Po={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function Ao(t){const[e,i]=Po[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 Ro(t,e,i,s){const[n,o]=ko(Ao(e),i,s),r=Ao({...t,ox:0,oy:0}),[a,l]=ko(r,i,s);return{...t,ox:n-a,oy:o-l}}function Eo(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}function No(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 Oo(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 Io(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)}function Fo(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),n=[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 n){const i=e.find(e=>Io(t,e.eid));if(i)return i.eid}for(const t of n)for(const e of ft){const i=t.find(t=>t.eid.split(".")[0]===e);if(i)return i.eid}for(const t of n)if(t.length)return t[0].eid}function Lo(t,e){const i=!0===e.marker?.is_light?[...e.marker?.controls||[],...e.entities]:e.entities.filter(t=>t.startsWith("light."));return i.find(e=>"on"===t.states[e]?.state)||null}function Ho(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const n=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===n){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const o=e.attributes?.linkquality??e.attributes?.lqi;if(null!=o){const t=parseFloat(o);isNaN(t)||i.push(t)}}return Ye(i)}function qo(t,e){for(const i of e){if(!Io(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 Uo(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 Wo(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 Bo(t,e){for(const i of e){if(!Wo(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 jo(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden)continue;let n=null;if("group"===s.platform)n=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if("Group"!==e?.model)continue;n=e.area_id||s.area_id||null}}if(!n)continue;const o=t.states[e];i.push({eid:e,name:s.name||o?.attributes?.friendly_name||e,area:n})}return i}function Vo(t,e,i,s,n){const o=mt(e,i,n);if(o!==gt)return o;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=_t[e];if(t)return t}return null}(r)??gt}function Go(t,e){t.marker=e,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 Ko(t){const{hass:e,areaToSpace:i,markers:s,settings:n,excluded:o,showAll:r,firstSpaceId:a,loc:l,iconRules:c}=t,h=!1!==n.group_lights,d=jo(e,h),p=new Set(d.map(t=>t.area)),u=No(e),_=new Set;for(const t of s){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||_.add(t.binding)}const g=(t,e)=>s.find(i=>i.binding===t+":"+e),m={},f=[];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(_.has("device:"+t.id))continue;const a=g("device",t.id);if(a&&a.hidden&&!n.filter_seeded)continue;const d=u[t.id]||[],v=Oo(e,t,d),b=!n.filter_seeded;if(b&&!r){if(o.has(v))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===v&&t.via_device_id)continue}const y=(t.name_by_user||t.name||l("device.unnamed")).trim(),w=y+"|"+s;let k=Vo(e,y,t.model,d,c);if(d.some(t=>t.startsWith("lock."))&&(k="mdi:lock"),b&&!r&&h&&"mdi:lightbulb"===k&&p.has(s))continue;m[w]=(m[w]||0)+1;const $=m[w]>1?y+" "+m[w]:y,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=Fo(e,d,k),"mdi:thermometer"!==k&&"mdi:air-filter"!==k||(x.temp=qo(e,d)),x.primary&&Wo(e,x.primary)&&(x.hum=Bo(e,d)),f.push(x)}for(const t of d)i[t.area]&&(_.has("entity:"+t.eid)||f.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.hidden&&!n.filter_seeded)continue;const[s,o]=t.binding.split(":");if("device"===s){const s=e.devices[o],n=t.area||s?.area_id||"",r=n&&i[n]||t.space||a,h=s&&u[s.id]||[];let d=s?Vo(e,s.name_by_user||s.name||"",s.model,h,c):"mdi:help-circle";h.some(t=>t.startsWith("lock."))&&(d="mdi:lock");const p={id:t.id,name:s?.name_by_user||s?.name||l("device.fallback"),model:s?.model||"",area:n,space:r,icon:d,entities:h,bindingKind:"device",bindingRef:o};p.primary=Fo(e,h,d),"mdi:thermometer"!==d&&"mdi:air-filter"!==d||(p.temp=qo(e,h)),p.primary&&Wo(e,p.primary)&&(p.hum=Bo(e,h)),p.primary&&Wo(e,p.primary)&&(p.hum=Bo(e,h)),Go(p,t),f.push(p)}else if("entity"===s){const s=e.entities[o],n=t.area||s?.area_id||s?.device_id&&e.devices[s.device_id]?.area_id||"",r=n&&i[n]||t.space||a,l=e.states[o],h=s?.name||l?.attributes?.friendly_name||o;let d=Vo(e,h,"",[o],c);o.startsWith("lock.")&&(d="mdi:lock");const p={id:t.id,name:h,model:"",area:n,space:r,icon:d,entities:[o],primary:o,bindingKind:"entity",bindingRef:o};"mdi:thermometer"!==d&&"mdi:air-filter"!==d||(p.temp=qo(e,[o])),Wo(e,o)&&(p.hum=Bo(e,[o])),Go(p,t),f.push(p)}else{const e=t.area||"",s=t.space||e&&i[e]||a,n={id:t.id,name:t.name||l("device.virtual"),model:t.model||"",area:e,space:s,icon:t.icon||"mdi:map-marker",entities:[],bindingKind:"virtual",virtual:!0};Go(n,t),f.push(n)}}return f}function Yo(t,e,i){let s=!1;for(const n of e)if(n.area===i&&!n.hidden)for(const e of n.entities)if(e.startsWith("light.")&&(s=!0,"on"===t.states[e]?.state))return"on";return s?"off":"none"}function Xo(t,e,i){if(!e)return null;const s=e.indexOf(":");if(s<0)return null;const n=e.slice(0,s),o=e.slice(s+1);if(!o)return null;if("entity"===n){const e=parseFloat(t.states[o]?.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===o).map(([t])=>t);return"temp"===i?qo(t,e):Bo(t,e)}return null}const Zo=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 Jo={"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.remove":"Remove","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":"Add a room: connect grid dots with lines until the outline closes","title.markup_merge":"Merge rooms: 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: click a virtual wall to make it solid; a shared solid wall merges the two rooms; an outer wall or inside a room deletes the room (with confirm).","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":"Add","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","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} · Esc/Ctrl+Z — undo a dot · close the outline by clicking the first one","markup.hint_start":"click a grid dot to start the outline","tip.lqi":"average zigbee signal:","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 badge","display.ripple":"Ripple only","display.icon_ripple":"Icon + ripple","marker.ripple_size":"Ripple 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":'Remove "{name}" from the plan?',"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.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 removed 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","editor.aspect_ratio":"Aspect ratio (e.g. 16:9 or auto)","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.color":"Color","decor.width":"Line width","decor.w_thin":"Thin","decor.w_mid":"Medium","decor.w_thick":"Thick","decor.fill":"Fill","decor.text_title":"Text label","decor.text_label":"Text","decor.text_hint":"A new line here is a new line on the plan; the block is centred. Drag a corner to resize it, the handle above it to turn it.","decor.text_hint_live":"{} is where the value lands. Without it the value is added at the end. A new line here is a new line on the plan.","decor.live_group":"Live value","decor.live_hint":"The label may show one entity's value — as Home Assistant reports it, with no rounding. A dead entity shows a dash.","decor.live_entity":"Entity","decor.live_entity_ph":"none — a plain label","decor.live_attr":"Attribute","decor.live_attr_ph":"the state","decor.live_unit":"Unit","decor.live_preview":"Preview","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag the picture to move it, pull a corner to resize it. Shift — off the grid.","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)","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 light sources","marker.controls_hint":"With tap action “Toggle”, a click flips all bound lights at once (any on → all off). The icon mirrors their state.","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":"Wall thickness","title.markup_wallthick":"Wall 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 for new room walls. Empty or zero — thin walls. 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","markup.openwall":"Open boundary","title.markup_openwall":"Open boundary — click a point on a shared wall, then a second point on the same wall (within the nearest corners) to make that stretch virtual. Click a dashed stretch to close it. 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.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 device from plan","marker.hide_tip":'The device is not drawn on the plan but still counts toward the room signal. To see it: the "Show hidden" button in the device editor.',"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":"Grid","gs.grid_hint":"Everything you draw or drag now lands on the grid. Hold Shift to place something between the nodes on purpose.","gs.align_all":"Align everything to the grid","gs.align_title":"Align everything to the grid","gs.align_none":"Everything is already on the grid — nothing to move.","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_warn":"This rewrites the plan and the device positions in one go. There is no undo: the only way back is to move things by hand. If some elements are deliberately between the nodes, cancel.","gs.align_run":"Align","gs.align_done":"Aligned to the grid: {n} elements moved","decor.furniture":"Furniture","furn.title":"Furniture library","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 Qo={en:Jo,ru:{"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.remove":"Убрать","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":"Добавить комнату: соединяйте точки сетки линиями до замкнутого контура","title.markup_merge":"Объединить комнаты: клик по одной, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить: виртуальная стена → обычная; общая глухая → слияние комнат; внешняя или клик внутрь → удалить комнату (с подтверждением)","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":"Проём","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} · Esc/Ctrl+Z — убрать точку · замкните контур кликом по первой","markup.hint_start":"кликните точку сетки, чтобы начать контур","tip.lqi":"средний сигнал zigbee:","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.ripple":"Только пульсация","display.icon_ripple":"Значок + пульсация","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.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.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":"Путь дашборда (куда вести)","editor.aspect_ratio":"Соотношение сторон (напр. 16:9 или auto)","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.color":"Цвет","decor.width":"Толщина линии","decor.w_thin":"Тонкая","decor.w_mid":"Средняя","decor.w_thick":"Толстая","decor.fill":"Залить","decor.text_title":"Надпись","decor.text_label":"Текст","decor.text_hint":"Перевод строки здесь — перевод строки на плане; блок выравнивается по центру. Тяните угол — меняет размер, ручка сверху — поворот.","decor.text_hint_live":"{} — место, куда встанет значение. Без него значение добавится в конец. Перевод строки здесь — перевод строки на плане.","decor.live_group":"Живое значение","decor.live_hint":"Надпись может показывать значение одной сущности — так, как его отдаёт Home Assistant, без округления. Мёртвая сущность — прочерк.","decor.live_entity":"Сущность","decor.live_entity_ph":"нет — обычная надпись","decor.live_attr":"Атрибут","decor.live_attr_ph":"состояние","decor.live_unit":"Единица","decor.live_preview":"Предпросмотр","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — двигается, тяните угол — меняет размер. Shift — мимо сетки.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","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":"Толщина стен","title.markup_wallthick":"Толщина стен — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина стен новой комнаты. Пустое или ноль — без толщины. Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","markup.openwall":"Открытая граница","title.markup_openwall":"Открытая граница — клик по точке на общей стене, затем второй клик на той же стене (не дальше ближайших углов) открывает отрезок. Клик по пунктиру закрывает. Курсор — крестик.","toast.openwall_pick":"Кликните по стене, разделяющей две комнаты","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.openwall_opened_span":"Виртуальный отрезок открыт","toast.openwall_closed_span":"Виртуальный отрезок закрыт","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":"Устройство не отображается на плане, но участвует в расчёте сигнала комнаты. Показать: кнопка «Показать скрытые» в редакторе устройств.","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":"Всё, что вы рисуете и перетаскиваете, ложится строго на сетку. Shift — временно поставить элемент между узлами.","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_warn":"Действие разом перезапишет план и позиции устройств. Отмены нет: вернуть можно только руками. Если какие-то элементы стоят между узлами намеренно — отмените.","gs.align_run":"Выровнять","gs.align_done":"Выровнено по сетке: сдвинуто элементов — {n}","decor.furniture":"Мебель","furn.title":"Библиотека мебели","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 tr(t,e){if(e&&e in Qo)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function er(t,e,i){return _i(Qo[t][e]??Jo[e]??e,i)}class ir 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 tr(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:er(e,"editor.lang_auto")},{value:"en",label:er(e,"editor.lang_en")},{value:"ru",label:er(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 V;this._loadSpaces();const t=this._lang,e={title:er(t,"editor.title"),default_floor:er(t,"editor.default_floor"),language:er(t,"editor.language"),icon_size:er(t,"editor.icon_size"),show_temperature:er(t,"editor.show_temperature"),live_states:er(t,"editor.live_states"),show_signal:er(t,"editor.show_signal"),kiosk:er(t,"editor.kiosk"),cycle:er(t,"editor.cycle")};return W`{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,g=_?_.emptyScript:"",m=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?g: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}},b=(t,e)=>!a(t,e),y={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:b};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=y){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??y}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=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??b)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,m?.({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,T=`<${D}>`,z=document,P=()=>z.createComment(""),A=t=>null===t||"object"!=typeof t&&"function"!=typeof t,R=Array.isArray,E="[ \t\n\f\r]",N=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,O=/-->/g,I=/>/g,F=RegExp(`>|${E}(?:([^\\s"'>=/]+)(${E}*=${E}*(?:[^ \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"),V=Symbol.for("lit-nothing"),G=new WeakMap,K=z.createTreeWalker(z,129);function Y(t,e){if(!R(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const X=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",r=N;for(let e=0;e"===l[0]?(r=n??N,c=-1):void 0===l[1]?c=-2:(c=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===O||r===I?r=N:(r=F,n=void 0);const d=r===F&&t[e+1].startsWith("/>")?" ":"";o+=r===N?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,o+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const r=t.length-1,a=this.parts,[l,c]=X(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iR(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!==V&&A(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=G.get(t.strings);return void 0===e&&G.set(t.strings,e=new Z(t)),e}k(t){R(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(P()),this.O(P()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=V}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=J(this,t,e,0),o=!A(t)||t!==this._$AH&&t!==j,o&&(this._$AH=t);else{const s=t;let r,a;for(t=n[0],r=0;r{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(P(),t),t,void 0,i??{})}return n._$AI(t),n})(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 ct=at.litElementPolyfillSupport;ct?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ht=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),dt=[{pattern:"протечк|leak|water sensor",icon:"mdi:water-alert"},{pattern:"клапан|valve",icon:"mdi:pipe-valve"},{pattern:"дым|smoke",icon:"mdi:smoke-detector"},{pattern:"термоголов|trv|radiator",icon:"mdi:radiator"},{pattern:"чайник|kettle|термопот",icon:"mdi:kettle"},{pattern:"сауна|sauna|harvia|парная|парилк",icon:"mdi:hot-tub"},{pattern:"температ|temperature|climate sensor",icon:"mdi:thermometer"},{pattern:"qingping|air monitor|молекул|air quality",icon:"mdi:air-filter"},{pattern:"штор|curtain|blind|shade",icon:"mdi:roller-shade"},{pattern:"розетк|plug|socket|outlet",icon:"mdi:power-socket-de"},{pattern:"выключат|switch",icon:"mdi:light-switch"},{pattern:"лампа|лампочк|bulb|gx53|светильник|rgb|lamp|light strip",icon:"mdi:lightbulb"},{pattern:"камер|camera",icon:"mdi:cctv"},{pattern:"замок|ttlock|lock|sn609|sn9161",icon:"mdi:lock"},{pattern:"ворота|garage|gate",icon:"mdi:garage-variant"},{pattern:"калитк|door|открыт|contact",icon:"mdi:door"},{pattern:"счётчик|счетчик|kws|meter",icon:"mdi:meter-electric"},{pattern:"вводный автомат|breaker|wifimcbn",icon:"mdi:electric-switch"},{pattern:"myheat|котёл|котел|boiler|отоплен|heating",icon:"mdi:water-boiler"},{pattern:"холодильник|fridge",icon:"mdi:fridge"},{pattern:"стиральн|washer|washing",icon:"mdi:washing-machine"},{pattern:"сушилк|dryer",icon:"mdi:tumble-dryer"},{pattern:"пылесос|vacuum|dreame|roborock",icon:"mdi:robot-vacuum"},{pattern:"soundbar",icon:"mdi:soundbar"},{pattern:"колонк|станц|speaker|яндекс|yandex|алиса|alice",icon:"mdi:speaker"},{pattern:"tv|телевизор|hyundaitv|mitv|television",icon:"mdi:television"},{pattern:"keenetic|роутер|router|mesh|access point",icon:"mdi:router-wireless"},{pattern:"ибп|ups|kirpich",icon:"mdi:battery-charging-high"},{pattern:"slzb|координат|zigbee|coordinator",icon:"mdi:zigbee"},{pattern:"motion|движен|presence|присутств",icon:"mdi:motion-sensor"},{pattern:"humidity|влажн",icon:"mdi:water-percent"}];function pt(t){const e=[];for(const i of t)if(i&&"string"==typeof i.pattern&&i.icon)try{e.push({re:new RegExp(i.pattern,"i"),icon:i.icon})}catch{}return e}const ut=pt(dt),_t={temperature:"mdi:thermometer",humidity:"mdi:water-percent",motion:"mdi:motion-sensor",occupancy:"mdi:motion-sensor",door:"mdi:door",window:"mdi:window-closed",garage_door:"mdi:garage-variant",smoke:"mdi:smoke-detector",moisture:"mdi:water-alert",gas:"mdi:gas-cylinder",power:"mdi:meter-electric",energy:"mdi:meter-electric",illuminance:"mdi:brightness-5",co2:"mdi:molecule-co2",pm25:"mdi:air-filter",battery:"mdi:battery"},gt="mdi:chip";function mt(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??ut)if(t.test(s))return e;return gt}const ft=["light","switch","cover","valve","lock","climate","fan","media_player","camera","vacuum","humidifier","water_heater","alarm_control_panel","sensor","binary_sensor","event","button","number","select","update"];var vt=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,bt=Math.ceil,yt=Math.floor,wt="[BigNumber Error] ",kt=wt+"Number primitive has more than 15 significant digits: ",$t=1e14,xt=14,St=9007199254740991,Mt=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],Ct=1e7,Dt=1e9;function Tt(t){var e=0|t;return t>0||t===e?e:e-1}function zt(t){for(var e,i,s=1,n=t.length,o=t[0]+"";sc^i?1:-1;for(a=(l=n.length)<(c=o.length)?l:c,r=0;ro[r]^i?1:-1;return l==c?0:l>c^i?1:-1}function At(t,e,i,s){if(ti||t!==yt(t))throw Error(wt+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Rt(t){var e=t.c.length-1;return Tt(t.e/xt)==e&&t.c[e]%2!=0}function Et(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Nt(t,e,i){var s,n;if(e<0){for(n=i+".";++e;n+=i);t=n+t}else if(++e>(s=t.length)){for(n=i,e-=s;--e;n+=i);t+=n}else eb?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>b?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!vt.test(d=String(t)))return n(p,d,c);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(At(e,2,x.length,"Base"),10==e&&S)return z(p=new M(t),_+p.e+1,g);if(d=String(t),c="number"==typeof t){if(0*t!=0)return n(p,d,c,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(kt+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,h=d.length;la){a=h;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return n(p,String(t),c,e)}c=!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(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(t>St||t!==yt(t)))throw Error(kt+p.s*t);if((a=a-l-1)>b)p.c=p.e=null;else if(a=f)?Et(l,r):Nt(l,r,"0");else if(o=(t=z(new M(t),e,i)).e,a=(l=zt(t.c)).length,1==s||2==s&&(e<=o||o<=m)){for(;ar),l=Nt(l,o,"0"),o+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=o-a)>0)for(o+1==a&&(l+=".");e--;l+="0");return t.s<0&&n?"-"+l:l}function D(t,e){for(var i,s,n=1,o=new M(t[0]);n=10;n/=10,s++);return(i=s+i*xt-1)>b?t.c=t.e=null:i=10;a/=10,n++);if((o=e-n)<0)o+=xt,r=e,l=d[c=0],h=yt(l/p[n-r-1]%10);else if((c=bt((o+1)/xt))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,n=1,r=(o%=xt)-xt+1}else{for(l=a=d[c],n=1;a>=10;a/=10,n++);h=(r=(o%=xt)-xt+n)<0?0:yt(l/p[n-r-1]%10)}if(s=s||e<0||null!=d[c+1]||(r<0?l:l%p[n-r-1]),s=i<4?(h||s)&&(0==i||i==(t.s<0?3:2)):h>5||5==h&&(4==i||s||6==i&&(o>0?r>0?l/p[n-r]:0:d[c-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[(xt-e%xt)%xt],t.e=-e||0):d[0]=t.e=0,t;if(0==o?(d.length=c,a=1,c--):(d.length=c+1,a=p[xt-o],d[c]=r>0?yt(l/p[n-r]%p[r])*a:0),s)for(;;){if(0==c){for(o=1,r=d[0];r>=10;r/=10,o++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);o!=a&&(t.e++,d[0]==$t&&(d[0]=1));break}if(d[c]+=a,d[c]!=$t)break;d[c--]=0,a=1}for(o=d.length;0===d[--o];d.pop());}t.e>b?t.c=t.e=null:t.e=f?Et(e,i):Nt(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(wt+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(At(i=t[e],0,Dt,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(At(i=t[e],0,8,e),g=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(At(i[0],-Dt,0,e),At(i[1],0,Dt,e),m=i[0],f=i[1]):(At(i,-Dt,Dt,e),m=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)At(i[0],-Dt,-1,e),At(i[1],1,Dt,e),v=i[0],b=i[1];else{if(At(i,-Dt,Dt,e),!i)throw Error(wt+e+" cannot be zero: "+i);v=-(b=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(wt+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw y=!i,Error(wt+"crypto unavailable");y=i}else y=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(At(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(At(i=t[e],0,Dt,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(wt+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(wt+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:g,EXPONENTIAL_AT:[m,f],RANGE:[v,b],CRYPTO:y,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,n=t.e,o=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===o||-1===o)&&n>=-Dt&&n<=Dt&&n===yt(n)){if(0===s[0]){if(0===n&&1===s.length)return!0;break t}if((e=(n+1)%xt)<1&&(e+=xt),String(s[0]).length==e){for(e=0;e=$t||i!==yt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===n&&(null===o||1===o||-1===o))return!0;throw Error(wt+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(o=9007199254740992,r=Math.random()*o&2097151?function(){return yt(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,n,o,a=0,l=[],c=new M(u);if(null==t?t=_:At(t,0,Dt),n=bt(t/xt),y)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(n*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(o%1e14),a+=2);a=n/2}else{if(!crypto.randomBytes)throw y=!1,Error(wt+"crypto unavailable");for(e=crypto.randomBytes(n*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(o%1e14),a+=7);a=n/7}if(!y)for(;a=10;o/=10,a++);ai-1&&(null==r[n+1]&&(r[n+1]=0),r[n+1]+=r[n]/i|0,r[n]%=i)}return r.reverse()}return function(s,n,o,r,a){var l,c,h,d,p,u,m,f,v=s.indexOf("."),b=_,y=g;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(n)).pow(s.length-v),k=d,f.c=e(Nt(zt(u.c),u.e,"0"),10,o,t),f.e=f.c.length),h=d=(m=e(s,n,o,a?(l=x,t):(l=t,x))).length;0==m[--d];m.pop());if(!m[0])return l.charAt(0);if(v<0?--h:(u.c=m,u.e=h,u.s=r,m=(u=i(u,f,b,y,o)).c,p=u.r,h=u.e),v=m[c=h+b+1],d=o/2,p=p||c<0||null!=m[c+1],p=y<4?(null!=v||p)&&(0==y||y==(u.s<0?3:2)):v>d||v==d&&(4==y||p||6==y&&1&m[c-1]||y==(u.s<0?8:7)),c<1||!m[0])s=p?Nt(l.charAt(1),-b,l.charAt(0)):l.charAt(0);else{if(m.length=c,p)for(--o;++m[--c]>o;)m[c]=0,c||(++h,m=[1].concat(m));for(d=m.length;!m[--d];);for(v=0,s="";v<=d;s+=l.charAt(m[v++]));s=Nt(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,n,o,r,a=0,l=t.length,c=e%Ct,h=e/Ct|0;for(t=t.slice();l--;)a=((n=c*(o=t[l]%Ct)+(s=h*o+(r=t[l]/Ct|0)*c)%Ct*Ct+a)/i|0)+(s/Ct|0)+h*r,t[l]=n%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var n,o;if(i!=s)o=i>s?1:-1;else for(n=o=0;ne[n]?1:-1;break}return o}function i(t,e,i,s){for(var n=0;i--;)t[i]-=n,n=t[i]1;t.splice(0,1));}return function(s,n,o,r,a){var l,c,h,d,p,u,_,g,m,f,v,b,y,w,k,$,x,S=s.s==n.s?1:-1,C=s.c,D=n.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&n.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(m=(g=new M(S)).c=[],S=o+(c=s.e-n.e)+1,a||(a=$t,c=Tt(s.e/xt)-Tt(n.e/xt),S=S/xt|0),h=0;D[h]==(C[h]||0);h++);if(D[h]>(C[h]||0)&&c--,S<0)m.push(1),d=!0;else{for(w=C.length,$=D.length,h=0,S+=2,(p=yt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),$=D.length,w=C.length),y=$,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(b=f[0],$!=v&&(b=b*a+(f[1]||0)),(p=yt(b/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,h++);z(g,o+(g.e=h+c*xt-1)+1,r,d)}else g.e=c,g.r=+d;return g}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,n=function(t,e,i,s){var n,o=i?e:e.replace(d,"");if(h.test(o))t.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(a,function(t,e,i){return n="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=n?t:e}),s&&(n=s,o=o.replace(l,"$1").replace(c,"0.$1")),e!=o))return new M(o,n);if(M.DEBUG)throw Error(wt+"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 Pt(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,n,o=this;if(null!=t)return At(t,0,Dt),null==e?e=g:At(e,0,8),z(new M(o),t+o.e+1,e);if(!(i=o.c))return null;if(s=((n=i.length-1)-Tt(this.e/xt))*xt,n=i[n])for(;n%10==0;n/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,g)},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,n,o,r,a,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(wt+"Exponent not an integer: "+P(t));if(null!=e&&(e=new M(e)),r=t.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!t.c||!t.c[0])return c=new M(Math.pow(+P(h),r?t.s*(2-Rt(t)):+P(t))),e?c.mod(e):c;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&h.isInteger()&&e.isInteger())&&(h=h.mod(e))}else{if(t.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||r&&h.c[1]>=24e7:h.c[0]<8e13||r&&h.c[0]<=9999975e7)))return o=h.s<0&&Rt(t)?-0:0,h.e>-1&&(o=1/o),new M(a?1/o:o);k&&(o=bt(k/xt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Rt(t)):l=(n=Math.abs(+P(t)))%2,c=new M(u);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):s&&(c=c.mod(e))}if(n){if(0===(n=yt(n/2)))break;l=n%2}else if(z(t=t.times(i),t.e+1,1),t.e>14)l=Rt(t);else{if(0===(n=+P(t)))break;l=n%2}h=h.times(h),o?h.c&&h.c.length>o&&(h.c.length=o):s&&(h=h.mod(e))}return s?c:(a&&(c=u.div(c)),e?c.mod(e):o?z(c,k,g,void 0):c)},p.integerValue=function(t){var e=new M(this);return null==t?t=g:At(t,0,8),z(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===Pt(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return Pt(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=Pt(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&Tt(this.e/xt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return Pt(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=Pt(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,n,o,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/xt,c=t.e/xt,h=r.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?r:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?r:3==g?-0:0)}if(l=Tt(l),c=Tt(c),h=h.slice(),a=l-c){for((o=a<0)?(a=-a,n=h):(c=l,n=d),n.reverse(),e=a;e--;n.push(0));n.reverse()}else for(s=(o=(a=h.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;h[i++]=0);for(e=$t-1;s>a;){if(h[--s]=0;){for(i=0,p=b[n]%m,u=b[n]/m|0,o=n+(r=l);o>n;)i=((c=p*(c=v[--r]%m)+(a=u*c+(h=v[r]/m|0)*p)%m*m+_[o]+i)/g|0)+(a/m|0)+u*h,_[o--]=c%g;_[o]=i}return i?++s:_.splice(0,1),T(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,n=s.s;if(e=(t=new M(t,e)).s,!n||!e)return new M(NaN);if(n!=e)return t.s=-e,s.minus(t);var o=s.e/xt,r=t.e/xt,a=s.c,l=t.c;if(!o||!r){if(!a||!l)return new M(n/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*n)}if(o=Tt(o),r=Tt(r),a=a.slice(),n=o-r){for(n>0?(r=o,i=l):(n=-n,i=a),i.reverse();n--;i.push(0));i.reverse()}for((n=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=n),n=0;e;)n=(a[--e]=a[e]+l[e]+n)/$t|0,a[e]=$t===a[e]?0:a[e]%$t;return n&&(a=[n].concat(a),++r),T(t,a,r)},p.precision=p.sd=function(t,e){var i,s,n,o=this;if(null!=t&&t!==!!t)return At(t,1,Dt),null==e?e=g:At(e,0,8),z(new M(o),t,e);if(!(i=o.c))return null;if(s=(n=i.length-1)*xt+1,n=i[n]){for(;n%10==0;n/=10,s--);for(n=i[0];n>=10;n/=10,s++);}return t&&o.e+1>s&&(s=o.e+1),s},p.shiftedBy=function(t){return At(t,-9007199254740991,St),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,n,o,r=this,a=r.c,l=r.s,c=r.e,h=_+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(+P(r)))||l==1/0?(((e=zt(a)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=Tt((c+1)/2)-(c<0||c%2),s=new M(e=l==1/0?"5e"+c:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+c)):s=new M(l+""),s.c[0])for((l=(c=s.e)+h)<3&&(l=0);;)if(o=s,s=d.times(o.plus(i(r,o,h,1))),zt(o.c).slice(0,l)===(e=zt(s.c)).slice(0,l)){if(s.e0&&_>0){for(o=_%a||a,h=u.substr(0,o);o<_;o+=a)h+=c+u.substr(o,a);l>0&&(h+=c+u.slice(o)),p&&(h="-"+h)}s=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,n,o,r,a,l,c,h,d,p,_,m=this,f=m.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(wt+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+P(l));if(!f)return new M(m);for(e=new M(u),h=s=new M(u),n=c=new M(u),_=zt(f),r=e.e=_.length-m.e-1,e.c[0]=Mt[(a=r%xt)<0?xt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:h:l,a=b,b=1/0,l=new M(_),c.c[0]=0;d=i(l,e,0,1),1!=(o=s.plus(d.times(n))).comparedTo(t);)s=n,n=o,h=c.plus(d.times(o=h)),c=o,e=l.minus(d.times(o=e)),l=o;return o=i(t.minus(s),n,0,1),c=c.plus(o.times(h)),s=s.plus(o.times(n)),c.s=h.s=m.s,p=i(h,n,r*=2,g).minus(m).abs().comparedTo(i(c,s,r,g).minus(m).abs())<1?[h,n]:[c,s],b=a,p},p.toNumber=function(){return+P(this)},p.toPrecision=function(t,e){return null!=t&&At(t,1,Dt),C(this,t,e,2)},p.toString=function(t){var e,i=this,n=i.s,o=i.e;return null===o?n?(e="Infinity",n<0&&(e="-"+e)):e="NaN":(null==t?e=o<=m||o>=f?Et(zt(i.c),o):Nt(zt(i.c),o,"0"):10===t&&S?e=Nt(zt((i=z(new M(i),_+o+1,g)).c),i.e,"0"):(At(t,2,x.length,"Base"),e=s(Nt(zt(i.c),o,"0"),10,t,n,!0)),n<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return P(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),It=class{key;left=null;right=null;constructor(t){this.key=t}},Ft=class extends It{constructor(t){super(t)}},Lt=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,n=null,o=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==n?o=r:n.right=r,n=r,r=e}}return null!=n&&(n.right=r.left,r.left=o),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)}}},qt=class t extends Lt{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 Ft(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ft(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 Ft(t.key);return function t(e,i){let s,n;do{if(s=e.left,n=e.right,null!=s){const e=new Ft(s.key);i.left=e,t(s,e)}if(null!=n){const t=new Ft(n.key);i.right=t,e=n,i=t}}while(null!=n)}(t,e),e}toSet(){return this.clone()}entries(){return new Wt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Ut(this.wrap())}[Symbol.toStringTag]="[object Set]"},Ht=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}},Ut=class extends Ht{getValue(t){return t.key}},Wt=class extends Ht{getValue(t){return[t.key,t.key]}},Bt=t=>()=>t,jt=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Bt(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Vt(t){const e=t?(e,i,s,n,o)=>e.exponentiatedBy(2).isLessThanOrEqualTo(n.minus(i).exponentiatedBy(2).plus(o.minus(s).exponentiatedBy(2)).times(t)):Bt(!1);return(t,i,s)=>{const n=t.x,o=t.y,r=s.x,a=s.y,l=o.minus(a).times(i.x.minus(r)).minus(n.minus(r).times(i.y.minus(a)));return e(l,n,o,r,a)?0:l.comparedTo(0)}}var Gt=t=>t,Kt=t=>{if(t){const e=new qt(jt(t)),i=new qt(jt(t)),s=(t,e)=>e.addAndReturn(t),n=t=>({x:s(t.x,e),y:s(t.y,i)});return n({x:new Ot(0),y:new Ot(0)}),n}return Gt},Yt=t=>({set:t=>{Xt=Yt(t)},reset:()=>Yt(t),compare:jt(t),snap:Kt(t),orient:Vt(t)}),Xt=Yt(),Zt=(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),Jt=(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}}},Qt=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),te=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ee=t=>te(t,t).sqrt(),ie=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return Qt(n,s).div(ee(n)).div(ee(s))},se=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return te(n,s).div(ee(n)).div(ee(s))},ne=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},oe=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},re=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:_e.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:ie(this.point,t.point,s.point),cosine:se(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:n,cosine:o}=e.get(t),{sine:r,cosine:a}=e.get(s);return n.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?o.isLessThan(a)?1:o.isGreaterThan(a)?-1:0:n.isLessThan(0)&&r.isLessThan(0)?o.isLessThan(a)?-1:o.isGreaterThan(a)?1:0:r.isLessThan(n)?-1:r.isGreaterThan(n)?1:0}}},ae=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,n=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}}},le=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&&n){const t=s.getIntersection(n);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(c))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(c))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(n.isLessThan(o)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(n.isGreaterThan(o)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!n.eq(o)){const t=l.minus(r),e=n.minus(i),h=c.minus(a),d=o.minus(s);if(t.isGreaterThan(e)&&h.isLessThan(d))return 1;if(t.isLessThan(e)&&h.isGreaterThan(d))return-1}return n.isGreaterThan(o)?1:n.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++ue,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 n,o,r;const a=re.comparePoints(e,i);if(a<0)n=e,o=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);n=i,o=e,r=-1}const l=new re(n,!0),c=new re(o,!1);return new t(l,c,[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 Xt.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=Jt(e,i);if(null===s)return null;const n=this.leftSE.point,o=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=Zt(e,r)&&0===this.comparePoint(r),c=Zt(i,n)&&0===t.comparePoint(n),h=Zt(e,a)&&0===this.comparePoint(a),d=Zt(i,o)&&0===t.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?a:null;if(c)return h&&n.x.eq(a.x)&&n.y.eq(a.y)?null:n;if(l)return d&&o.x.eq(r.x)&&o.y.eq(r.y)?null:r;if(d&&h)return null;if(d)return o;if(h)return a;const p=((t,e,i,s)=>{if(e.x.isZero())return oe(i,s,t.x);if(s.x.isZero())return oe(t,e,i.x);if(e.y.isZero())return ne(i,s,t.y);if(s.y.isZero())return ne(t,e,i.y);const n=Qt(e,s);if(n.isZero())return null;const o={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=Qt(o,e).div(n),a=Qt(o,s).div(n),l=t.x.plus(a.times(e.x)),c=i.x.plus(r.times(s.x)),h=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(n,this.vector(),r,t.vector());return null===p?null:Zt(s,p)?Xt.snap(p):null}split(e){const i=[],s=void 0!==e.events,n=new re(e,!0),o=new re(e,!1),r=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(n);const a=new t(n,r,this.rings.slice(),this.windings.slice());return re.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),re.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(n.checkForConsuming(),o.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}},ge=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=Xt.snap({x:new Ot(t[0][0]),y:new Ot(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let n=s;for(let e=1,i=t.length;epe.run("union",t,e),be=(t,...e)=>pe.run("intersection",t,e),ye=(t,...e)=>pe.run("difference",t,e);function we(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function ke(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 $e(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 xe(t,e,i=1){const s=t[0].toFixed(i),n=t[1].toFixed(i),o=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 Me(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 Ce(t){const e=[],i=new Set;for(const s of t||[]){const t=Se(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),o=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(o,Math.min(i-o,a));const l=a/i;n={x:e+l*c,y:r+l*h,angle:t}}else n={x:u[0],y:u[1],angle:t}}}return n}function Te(t,e,i,s,n,o=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,c=o;for(const e of Ce(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])>o||s(i[1])>o)continue;const n=(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],h=Math.min(n,r),d=Math.max(n,r),p=h>0?h:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],m=(h+d)/2;return{wallA:g(h),wallB:g(d),sideA:u,sideB:_,midA:g((h-p)/2),midB:g((p+d)/2),wallCenter:g(m),centered:Math.abs(m)<=n}}function ze(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}function Pe(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}function Re(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;let r=o?((t[0]-e[0])*s+(t[1]-e[1])*n)/o:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*n))}function Ee(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let n=0;nn&&r<-n||o<-n&&r>n)&&(a>n&&l<-n||a<-n&&l>n)}function Le(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1;let c=0,h=0,d=0;for(let e=0;e1e-9?[h/(3*c),d/(3*c)]:[(n+o)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!Ae([e,i],t))return-1/0;let s=1/0;for(let n=0;ng&&(g=c,_=[s,l])}if(_){const[t,i]=_,s=(o-n)/e,l=(a-r)/e;for(let e=-4;e<=4;e++)for(let n=-4;n<=4;n++){const o=t+s*e/4,r=i+l*n/4,a=u(o,r);a>g&&(g=a,_=[o,r])}}return _||qe(t)||t[0]}function qe(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(Oe(s,t,e))return s;for(let s=0;s[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ve(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=ve(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}function Ge(t,e,i){for(let s=0;s1&&Pe(i[0],i[i.length-1],e)&&i.pop(),i}function Ye(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function Xe(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 Ze(t,e,i,s){if(t.length<2)return;const n=e.x+s,o=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}function ai(t){return"opening"===t||"closing"===t}const li=["automation","script","scene"];function ci(t,e,i){const s=String(e??"").trim();if(!s)return null;const n=t?.states?.[s];if(!n)return null;const o=String(i??"").trim(),r=t=>t.slice(0,60);if(o){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)}(n.attributes?.[o]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,n,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=n.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function hi(t,e,i){const s=String(e??"").trim(),n=String(i??"").trim()||s;if(!n)return t.text;const o=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`${o} ${n}`}function di(t,e,i){const s=t??"";if(!(e?.entity||"").trim())return s;const n=function(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],n=s?.state;if(!s||null==n||""===n||"unavailable"===n||"unknown"===n)return"—";const o=(e?.attr||"").trim(),r=ci(t,i,o||null);return null===r?"—":hi(r,o?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}(i,e),o=s.indexOf("{}");return o>=0?s.slice(0,o)+n+s.slice(o+2):s?`${s} ${n}`:n}function pi(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 ui(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}function _i(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 gi="#55606c",mi=.55;function fi(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:gi,opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):mi,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 vi(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 bi={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}},yi=/^#[0-9a-f]{6}$/i;function wi(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(bi)){const s=bi[t],n=i[t];e[t]={c:n&&"string"==typeof n.c&&yi.test(n.c)?n.c:s.c,a:n&&"number"==typeof n.a?Math.min(1,Math.max(0,n.a)):s.a}}return e}function ki(t,e,i){const s=Math.min(1,Math.max(0,i)),n=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),o=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=n.map((t,e)=>Math.round(t+(o[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function $i(t,e,i,s,n,o,r){if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:ki(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(n,o),e=Math.max(n,o);return se?r.temp_hot:r.temp_ok}return null}const xi={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"]},Si=[["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 Mi(t){for(const e of[...Object.values(xi),...Si])if(t===e[0]||t===e[1])return e;return null}function Ci(t,e,i,s,n){if(!s||"unavailable"===s||"unknown"===s)return t;if(n){const i="cover"===e?Mi(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=xi[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const n=Mi(t);return n?"closed"===s?n[0]:n[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 Di(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}function Ti(t,e){if(!t||"on"!==t.state)return null;const i=t.attributes||{},s=Number(i.brightness),n=Number.isFinite(s)&&s>0?Math.max(.15,Math.min(1,s/255)):1,o=i.rgb_color;if(Array.isArray(o)&&o.length>=3&&o.every(t=>Number.isFinite(t)))return{c:`rgb(${o[0]}, ${o[1]}, ${o[2]})`,bri:n};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),n=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,o=t=>Math.round(Math.min(255,Math.max(0,t)));return[o(i),o(s),o(n)]}(r);return{c:`rgb(${t}, ${e}, ${i})`,bri:n}}return{c:e,bri:n}}function zi(t,e,i,s,n=170){const o=Math.hypot(e[0]-t[0],e[1]-t[1]),r=Math.hypot(i[0]-t[0],i[1]-t[1]);if(o<1e-6||r<1e-6||Math.min(o,r)>=s)return null;let a=Math.atan2(e[1]-t[1],e[0]-t[0]),l=Math.atan2(i[1]-t[1],i[0]-t[0])-a;for(;l>Math.PI;)l-=2*Math.PI;for(;l<-Math.PI;)l+=2*Math.PI;const c=n*Math.PI/180;if(Math.abs(l)>c){const t=a+l/2;l=c*Math.sign(l),a=t-l/2}const h=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=a+l*e/8;h.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return h}function Pi(t,e,i,s,n){const o=e*Math.PI/180,r=[-Math.sin(o),Math.cos(o)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*n*a,t[1]+r[1]*n*a];return s.some(t=>Oe(l,t,1e-9))}function Ai(t){return t.startsWith("light.")||t.startsWith("switch.")}function Ri(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let n=0;np||l>p)continue;const u=(n[0]-o[0])*h+(n[1]-o[1])*d,_=(r[0]-o[0])*h+(r[1]-o[1])*d,g=Math.max(0,Math.min(u,_)),m=Math.min(c,Math.max(u,_));m-g>i&&s.push([o[0]+h*g,o[1]+d*g,o[0]+h*m,o[1]+d*m])}}return s}function Ei(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let n=!0;for(;n;){n=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const o of e)if(o.id&&i.has(o.id)&&s(t,o)){i.add(t.id),n=!0;break}}return i}function Ni(t,e,i=1e-6){const s=[];for(const n of t){const t=[n[0],n[1]],o=[n[2],n[3]],r=o[0]-t[0],a=o[1]-t[1],l=Math.hypot(r,a);if(lo||n>o)continue;const r=(s[0]-t[0])*c+(s[1]-t[1])*h,a=(s[2]-t[0])*c+(s[3]-t[1])*h,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],o[0],o[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,n]of d)e-p>i&&s.push([t[0]+c*p,t[1]+h*p,t[0]+c*e,t[1]+h*e]),p=Math.max(p,n);l-p>i&&s.push([t[0]+c*p,t[1]+h*p,o[0],o[1]])}return s}const Oi=864e5,Ii=576e5;function Fi(t){const e=new Set,i=t=>{if("string"!=typeof t||!t)return;const i=Li(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 Li(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 qi(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i?i:t}function Hi(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Ui(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Wi(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Bi(t,e){const i=e[2]-e[0],s=e[3]-e[1],n=i*i+s*s;if(!n)return Math.hypot(t[0]-e[0],t[1]-e[1]);let o=((t[0]-e[0])*i+(t[1]-e[1])*s)/n;return o=Math.max(0,Math.min(1,o)),Math.hypot(t[0]-(e[0]+o*i),t[1]-(e[1]+o*s))}const ji=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Vi=(t,e)=>[t[0]-e[0],t[1]-e[1]],Gi=(t,e)=>[t[0]+e[0],t[1]+e[1]],Ki=(t,e)=>t[0]*e[0]+t[1]*e[1],Yi=t=>Math.hypot(t[0],t[1]);function Xi(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function Qi(t,e,i,s){const n=s||Ji(t,e),o=(e+1)%t.length;return t.map((t,s)=>s===e||s===o?[t[0]+n[0]*i,t[1]+n[1]*i]:[...t])}function ts(t,e,i,s){const n=[],o=Vi(i,e),r=Yi(o);if(rs||h>s)continue;const d=Ki(Vi(o,e),a),p=Ki(Vi(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&n.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return n}function es(t,e,i,s,n){const o=ts(t,e,i,n);if(!o.length)return null;const r=t=>o.some(([e,i])=>Zi(t,e,i)<=n),a=t.length,l=[];for(let o=0;on||f>n)continue;const v=Ki(Vi(e,c),u),b=Ki(Vi(i,c),u),y=Math.max(0,Math.min(v,b)),w=Math.min(p,Math.max(v,b));if(!(w-y<=n)){if(y>n&&yn&&wYi(Vi(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const n=i[(s-1+i.length)%i.length],o=i[(s+1)%i.length],r=(t[0]-n[0])*(o[1]-n[1])-(t[1]-n[1])*(o[0]-n[0]),a=Yi(Vi(o,n))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function ss(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-n[0])*a[1]-(t[1]-n[1])*a[0],c=t=>(t[0]-n[0])*a[0]+(t[1]-n[1])*a[1],h=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const n=c(e);n<=h||n>=d||tMath.abs(r+(t-p)/m*(a-r));s=Math.min(s,f(_),f(g))}}return s}function os(t,e,i){if(We(t,e,i))return!0;if(Ue(t,e,i)||Ue(e,t,i))return!1;let s=0;try{const i=be([[...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+=Be(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function rs(t,e,i,s,n){const o=[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]=Qi(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[Gi(i.a,o),Gi(i.b,o)]];continue}const t=ts(e.poly,i.a,i.b,n);if(!t.length)continue;const a=es(e.poly,i.a,i.b,o,n);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[Gi(t,o),Gi(e,o)]))}for(const t of e)Zi([t.x,t.y],i.a,i.b)<=n&&(r.openings[t.id]=[t.x+o[0],t.y+o[1]]);return r}function as(t,e,i){for(const s of e)for(let e=0;ei)continue;const c=(t.x-n[0])*l[0]+(t.y-n[1])*l[1];if(c-t.length/2>=-i&&c+t.length/2<=a+i)return!0}return!1}function ls(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),n=a.polys[e];if(!ss(n))return!1;const l=Xi(s.poly),h=Xi(n);if(Math.abs(h)t.find(t=>t.id===e).poly),d=t.map(c);for(const t of ls(e,h,2*r)){const e=a.openings[t.id];if(!as(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function hs(t,e,i,s,n,o){const r=t=>[s[0]+(t[0]-s[0])*n,s[1]+(t[1]-s[1])*n],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 c=t.filter(t=>t.id!==i).map(t=>t.poly),h=hs(l,e,c,s,n,2*a),d=h.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 n=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;n.length>=2&&i(n[n.length-2],n[n.length-1],s)<=0;)n.pop();n.push(s)}return s.pop(),n.pop(),s.concat(n)}(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 ls(e,[l.poly],2*a)){const e=h.openings[t.id];if(!as(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function ps(t,e,i){const s=i/e;return Be(t)*s*s/1e4}function us(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const _s=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],gs=t=>Math.min(1,Math.max(0,t));function ms(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=_s[_s.length-1][1];for(let t=1;t<_s.length;t++){const[s,n]=_s[t-1],[o,r]=_s[t];if(e<=o){i=ki(n,r,(e-s)/(o-s));break}}return{bg:i,planDim:.1*gs((10-e)/16),warmth:e<0?1:gs(1-e/10)}}function fs(t,e,i=6){const s=t.angle*Math.PI/180,n=[Math.sin(s),-Math.cos(s)],o=s=>{const o=[t.x+n[0]*i*s,t.y+n[1]*i*s];return e.find(t=>t.poly.length>=3&&Ae(o,t.poly))||null},r=o(1),a=o(-1);return r&&a?null:r||a?r?{normal:[-n[0],-n[1]],roomId:r.id}:{normal:n,roomId:a.id}:null}function vs(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function bs(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 ys(t,e){try{const i=be([[...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 ws(t,e,i,s,n,o,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,n),l=[-a[0],-a[1]],c=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),h=[];for(const i of e){if(!(i.length>0))continue;const e=fs(i,t);if(!e||!vs(e.normal,a,s))continue;const n=t.find(t=>t.id===e.roomId);if(!n)continue;const d=o&&o[e.roomId]||n.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],g=Math.max(0,r?.[i.id]||0),m=i.x+_[0]*g/2,f=i.y+_[1]*g/2,v=Math.cos(p)*u,b=Math.sin(p)*u,y=[m-v,f-b],w=[m+v,f+b],k=c*i.length,$=ys(bs(y,w,l,k),d);if(!$.length)continue;const x=l[0]*_[0]+l[1]*_[1];h.push({openingId:i.id,roomId:e.roomId,polys:$,a:y,b:w,dir:l,len:k,normal:_,depth:k*x})}return h}function ks(t){return Math.round(10*(Number(t)||0))/10}const $s={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 xs=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Ss(t,e){const i=xs(e?.north_deg);return null!==i?i:xs(t?.north_deg)}function Ms(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function Cs(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Ds(t){const e=t?.weather_entity;return"string"==typeof e&&e.trim()?e.trim():null}function Ts(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 zs=1e3;function Ps(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,n=s>=1?e:e*s,o=s>=1?e/s:e;return{x:(e-n)/2,y:(e-o)/2,w:n,h:o}}const As=.01;function Rs(t,e=1e3){const i=Ps(t?.plan_aspect,e),s=Number(t?.plan_scale),n=Number.isFinite(s)&&s>0?Math.min(100,Math.max(As,s)):1,o=Number(t?.plan_x),r=Number(t?.plan_y);return{x:i.x+(Number.isFinite(o)?Bs(o):0)*e,y:i.y+(Number.isFinite(r)?Bs(r):0)*e,w:i.w*n,h:i.h*n}}function Es(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 Ns(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=zs,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]*zs,i[1]*e,i[2]*zs,i[3]*e],bg:t.plan_url?{href:Li(t.plan_url),...Rs(t,zs)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...Es(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*zs:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*zs:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*zs,t[1]*e]):void 0}})}}):[]}const Os=5e3,Is=Os*zs,Fs=240,Ls=zs/Fs,qs=1/Fs;function Hs(t){if(!Number.isFinite(t))return t;const e=Math.round(t*Fs/zs)*zs/Fs;return Math.abs(e-t)<=1e-9*Ls?t:e}function Us(t){return{x:Hs(t.x),y:Hs(t.y)}}function Ws(t){return Number.isFinite(t)?Math.min(Is,Math.max(-Is,t)):0}function Bs(t){return Number.isFinite(t)?Math.min(Os,Math.max(-Os,t)):0}const js=1/3,Vs=200;function Gs(t){let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t){const t=Number(o[0]),r=Number(o[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>n&&(n=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:n}}function Ks(t){return t.poly&&t.poly.length?Gs(t.poly):null==t.x||null==t.y?null:Gs([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function Ys(t,e){const i=[];for(const e of t.rooms||[]){const t=Ks(e);t&&i.push(t)}t.bg&&i.push({minX:t.bg.x,minY:t.bg.y,maxX:t.bg.x+t.bg.w,maxY:t.bg.y+t.bg.h});for(const t of e||[])if(Array.isArray(t)){const e=Gs([t]);e&&i.push(e)}else i.push(t);return i}const Xs=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},Zs=t=>{let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t)o.minXs&&(s=o.maxX),o.maxY>n&&(n=o.maxY);return e>s||i>n?null:{x:e,y:i,w:s-e,h:n-i}};function Js(t,e){let{x:i,y:s,w:n,h:o}=t;n<30&&(i=i+n/2-100,n=Vs),o<30&&(s=s+o/2-100,o=Vs);const r=Math.max(n,o)*e;return{x:i-r,y:s-r,w:n+2*r,h:o+2*r}}function Qs(t,e={}){const i=e.pad??.05,s=e.k??10,n=e.minSpread??50,o=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=Is&&Math.abs(t.maxX)<=Is&&Math.abs(t.minY)<=Is&&Math.abs(t.maxY)<=Is);if(!o.length)return{core:null,all:null,outliers:0};const r=Zs(o);if(o.length<4){const t=Js(r,i);return{core:t,all:t,outliers:0}}const a=o.map(t=>(t.minX+t.maxX)/2),l=o.map(t=>(t.minY+t.maxY)/2),c=Xs(a),h=Xs(l),d=o.map((t,e)=>Math.max(Math.abs(a[e]-c),Math.abs(l[e]-h))),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),n),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,g=_&&_<=o.length*js?o.filter((t,e)=>!u[e]):o;return{core:Js(Zs(g)||r,i),all:Js(r,i),outliers:g===o?0:_}}function tn(t,e,i=.05){const s=Qs(Ys(t,e),{pad:i});if(s.core)return s.core;const n=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,zs,zs];return{x:n[0],y:n[1],w:n[2],h:n[3]}}function en(t){const e=tn(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function sn(t){const e=[];for(const i of t.rooms||[]){const t=Ks(i);t&&e.push(t)}const i=Qs(e,{pad:0}).core;return i?Math.max(zs,Math.min(Is,Math.max(i.w,i.h))):zs}function nn(t,e,i,s=1){const n=Number(i),o=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(n)||n<=0?t*o:t*sn(e)*o/n}const on=[1,2,5,10,20,50,100,200,500,1e3];function rn(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),n=Math.min(...i);return{x:s,y:n,w:Math.max(...e)-s,h:Math.max(...i)-n}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function an(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 ln=["furniture","appliance","sanitary","other"],cn=[{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]]}],hn=new Map(cn.map(t=>[t.id,t]));function dn(t){return t&&hn.get(t)||null}function pn(t,e,i=Ls,s=1e3){const n=Number(e)>0?Number(e):5;return(Number(t)||0)/n*i/s}const un=5e-4,_n=Os;function gn(t){return Number.isFinite(t)?Math.max(un,Math.min(_n,t)):un}const mn=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function fn(t,e,i){const s=dn(t);if(!(s&&e>0&&i>0))return"";const n=t=>mn(t*e),o=t=>mn(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}H${n(e+s)}V${o(i+a)}H${n(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}L${n(s)} ${o(a)}`)}else if("e"===t[0]){const[,s,a,l,c]=t;r.push(`M${n(s-l)} ${o(a)}A${mn(l*e)} ${mn(c*i)} 0 0 1 ${n(s+l)} ${o(a)}A${mn(l*e)} ${mn(c*i)} 0 0 1 ${n(s-l)} ${o(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${n(e[0])} ${o(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,n,o=0){let r=null,a=n;for(const n of s){const[s,l,c,h]=n,d=c-s,p=h-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let g=((t-s)*d+(e-l)*p)/u;g=Math.max(0,Math.min(1,g));let m=s+g*d,f=l+g*p;const v=Math.hypot(t-m,e-f);if(!(v0){const t=Math.round(g*_/o)*o;m=s+t/_*d,f=l+t/_*p}r={cx:m+b*(i/2),cy:f+y*(i/2),angle:vn(180*Math.atan2(-b,y)/Math.PI),dist:v}}return r}function yn(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function wn(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function kn(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function $n(t,e){const i="number"==typeof t?t:parseFloat(String(t).trim().replace(",","."));if(!Number.isFinite(i)||i<=0)return null;return wn(e?2.54*i:i)}function xn(t,e,i){const s=Number(e)>0?Number(e):5;return wn(t)/s*i}function Sn(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function Mn(t,e){let i=e[0]-t[0],s=e[1]-t[1];const n=Math.hypot(i,s);return n<1e-12?[1,0]:(i/=n,s/=n,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function Cn(t,e,i){const s=Sn((t[0]+e[0])/2,i),n=Sn((t[1]+e[1])/2,i),[o,r]=Mn(t,e);let a=Math.atan2(r,o);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,c=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(c)},${n.toFixed(c)}@${l.toFixed(4)}`}function Dn(t,e,i,s){return 1===s?Cn(t,e,i):Cn([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function Tn(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[n,o]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[n,o,r].every(Number.isFinite)&&s.push({w:e,x:n*i,y:o*i,ang:r})}return s}function zn(t,e){const[i,s]=Mn(t,e);let n=Math.atan2(s,i);return n<0&&(n+=Math.PI),n}function Pn(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function An(t,e,i,s,n=1){if(!t?.length)return null;const o=Dn(e,i,s,n),r=t.find(t=>t.key===o);if(r)return r;const a=n>0?n:1,l=(e[0]+i[0])/2,c=(e[1]+i[1])/2,h=zn(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of Tn(t,a))if(Pn(e.ang,h)&&Math.hypot(e.x-l,e.y-c)<=d)return e.w;return null}function Rn(t,e,i,s,n=1){const o=An(t,e,i,s,n);return o&&o.cm>0?wn(o.cm):0}function En(t,e,i,s=1,n=[]){if(!t?.length)return[];const o=new Set;for(const t of Ce(e))o.add(Dn([t[0],t[1]],[t[2],t[3]],i,s));const r=e||[],a=Math.max(i*s*.02,1e-9);for(let t=0;to.has(t.key)&&t.cm>=1&&t.cm<=100)}function Nn(t,e,i,s=8){const[n,o]=Mn(t,e);let r=Math.atan2(o,n);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 On(t,e,i,s,n,o=1){const r=Dn(e,i,n,o),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,{key:r,cm:wn(s)}]}function In(t,e,i,s,n,o=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,n=1){const o=Gn(t,e,i,s,n);if(!o)return[];const r=[];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),n=Bn(i,t);if(s&&n)return`${jn(s)} ${jn(Vn(n))}`}}let s="";for(let i=0;i.05)&&Jn(r,a,t[0],t[1],t[2],t[3])<=o)return!0}return!1}function qn(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function Hn(t,e){const i=t[e],s=t[(e+1)%t.length],n=s[0]-i[0],o=s[1]-i[1],r=Math.hypot(n,o)||1;let a=-o/r,l=n/r;const c=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,n=e.length-1;st[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r+0)+o&&(i=!i)}return i}([c[0]+.001*a,c[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function Un(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 Wn(t,e,i,s){const n=e[0]*s[1]-e[1]*s[0];if(Math.abs(n)<1e-12)return null;const o=[i[0]-t[0],i[1]-t[1]],r=(o[0]*s[1]-o[1]*s[0])/n;return[t[0]+r*e[0],t[1]+r*e[1]]}function Bn(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 n=0;n0||d>0)){s.push([t[n][0],t[n][1]]);continue}if(Un(y,w)){const e=t[n],i=[e[0]+p*h,e[1]+u*h],o=[e[0]+_*d,e[1]+g*d];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=Wn(k,y,$,w),S=Math.max(h,d,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}h>0&&s.push([t[n][0]+p*h,t[n][1]+u*h]),d>0&&s.push([t[n][0]+_*d,t[n][1]+g*d]),h>0||d>0||s.push([t[n][0],t[n][1]])}return s.length>=3?s:null}function jn(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),r=Se(o);if(!r||r.length<3)return null;const a=qn(s,n),l=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Se(i);if(t)for(const e of Ri(r,t,a))l.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])l.push([t[0],t[1]],[t[2],t[3]]);const c=[],h=[];for(let t=0;ta)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=n||r>=1-n||(o.some(t=>Math.abs(t-r)*s<=2*a)||o.push(r))}o.sort((t,e)=>t-e);for(const s of o)c.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),h.push(t)}return{poly:c,parent:h,orig:r}}function Kn(t,e,i,s){const n=e.poly.length,o=new Array(n).fill(0);if(!t?.length)return o;const r=new Set,a=[];for(let l=0;l0?(o[l]=wn(c.cm),r.add(c.key)):a.push(l)}if(!a.length)return o;const l=s>0?s:1,c=Math.max(.5*i,1e-9)*l,h=Tn(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],n=e.orig[(t+1)%e.orig.length],a=zn(s,n),l=(s[0]+n[0])/2,d=(s[1]+n[1])/2;let p=null;for(const t of h){if(r.has(t.w.key))continue;if(!Pn(t.ang,a))continue;if(Jn(t.x,t.y,s[0],s[1],n[0],n[1])>c)continue;const e=Math.hypot(t.x-l,t.y-d);(!p||et?.id===e),n=Se(s);if(!n)return[];const o=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Se(s);if(t)for(const e of Ri(n,t,i))o.push(e)}return o}(t,e,qn(n,a)),h=function(t,e,i,s,n){const o=qn(s,n),r=[];for(let a=0;aJn(h,d,t[0],t[1],t[2],t[3])<=o);r.push(p?"shared":"outer")}return r}(l.poly,c,s,n,a),d=Kn(i,l,n,a),p=d.map((t,e)=>h[e]&&t>0?xn(t,o,r)/2:0);return{...l,kinds:h,cms:d,offsets:p}}function Xn(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Yn(t,l.id,e,i,s,n,o,r);if(c)for(let t=0;t4*l||(!p||tt?.id===e),c=Se(l);if(!c||c.length<3)return null;if(!i?.length)return c.map(t=>[t[0],t[1]]);const h=Yn(t,e,i,s,n,o,r,a);return h&&h.offsets.some(t=>t>0)&&Bn(h.poly,h.offsets)||c.map(t=>[t[0],t[1]])}function to(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=[],n,o,r,a=1){if(!e?.length)return null;const l=[],c=[];let h=0;for(const s of t||[]){if(!s?.id)continue;const d=Yn(t,s.id,e,i,n,o,r,a);if(!d||d.poly.length<3||!d.offsets.some(t=>t>0))continue;for(const t of d.offsets)t>0&&(h=Math.max(h,2*t));const p=io(d.poly,d.offsets),u=Bn(d.poly,d.offsets);p&&l.push(p),u&&c.push(u)}if(!l.length)return null;const d=function(t,e,i,s,n,o,r){if(!e?.length||!i?.length)return[];const a=4*qn(s,r),l=new Map;for(const a of Xn(t,e,i,s,n,o,r))!a.open&&a.half>0&&!l.has(a.key)&&l.set(a.key,a);const c=[...l.values()];if(c.length<2)return[];const h=[];for(const t of i)for(const e of[[t[0],t[1]],[t[2],t[3]]])h.some(t=>Math.hypot(t[0]-e[0],t[1]-e[1])<=a)||h.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 n=Math.hypot(i,s);return n>a?[i/n,s/n]:null};for(const t of h){const e=c.map(e=>({iv:e,u:p(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(r>0?[t.slice(),h,u,p]:[t.slice(),p,u,h])}}return d}(t,e,i,n,o,r,a);try{let t=to(l[0]);for(let e=1;e0))continue;const i=t.angle*Math.PI/180,s=Math.cos(i),o=Math.sin(i),r=-o,l=s,c=t.length/2,d=1.25*Math.max(h,n*a),p=[[t.x-s*c-r*d,t.y-o*c-l*d],[t.x+s*c-r*d,t.y+o*c-l*d],[t.x+s*c+r*d,t.y+o*c+l*d],[t.x-s*c+r*d,t.y-o*c+l*d]];e=ye(e,to(p))}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?" ":"")+jn(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:h}:null}catch{const s=function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Yn(t,l.id,e,i,s,n,o,r);if(!c||c.poly.length<3||!c.offsets.some(t=>t>0))continue;const h=io(c.poly,c.offsets),d=Bn(c.poly,c.offsets);if(!h||!d)continue;const p=`${jn(h)} ${jn(Vn(d))}`;let u="",_="outer",g=0,m=0;for(let t=0;t0){u=Dn(c.poly[t],c.poly[(t+1)%c.poly.length],s,r),_=c.kinds[t]||"outer",g=c.cms[t],m=xn(g,n,o);break}a.push({d:p,key:u,kind:_,cm:g,depthUnits:m})}return a}(t,e,i,n,o,r,a);return s.length?{d:s.map(t=>t.d).join(" "),depthUnits:h}: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]]);Vn(t),e.slice().reverse();const s=[];for(let n=0;n0||a>0)){s.push([t[n][0],t[n][1]]);continue}if(Un(y,w)){const e=t[n],i=[e[0]-l*r,e[1]-c*r],o=[e[0]-h*a,e[1]-d*a];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=Wn(k,y,$,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[n][0]-l*r,t[n][1]-c*r]),a>0&&s.push([t[n][0]-h*a,t[n][1]-d*a])}return s.length>=3?s:null}function so(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){const c=Se(l);if(c&&c.length>=3){const h=Yn(t,l.id,e,i,s,n,o,r),d=h&&h.offsets.some(t=>t>0)?io(h.poly,h.offsets):null,p=d||c;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 no(t,e,i,s,n,o,r=1){let a=null;for(const i of t||[]){const t=Se(i);if(t)for(let s=0;ss*r)return{ox:0,oy:0,cm:0};const l=Rn(i,a.a,a.b,s,r);if(!(l>0))return{ox:0,oy:0,cm:0};const c=xn(l,n,o),h=Se(a.room),[d,p]=Hn(h,a.edge),u=e.flip_v?-1:1,_=c/2;return{ox:d*_*u,oy:p*_*u,cm:l}}function oo(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function ro(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function ao(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function lo(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!ao(t.a)||!ao(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],n=[Number(t.b[0]),Number(t.b[1])];Math.hypot(n[0]-s[0],n[1]-s[1])<.001||e.push({a:s,b:n})}return e}function co(t,e){const i=e[0],s=e[1],n=e[2]-i,o=e[3]-s,r=n*n+o*o;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*n+(t[1]-s)*o)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*n,s+a*o];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function ho(t,e){return co(t,e).q}function po(t,e,i,s,n){const o=co(t,e).q;let r=o,a=1/0;for(const t of i){const e=Math.hypot(o[0]-t[0],o[1]-t[1]);e<=n&&e0?s:1,g=Math.round(u/_)*_,m=Math.max(0,Math.min(p,g))/p;return[l+h*m,c+d*m]}function uo(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt?.id);let o=null;for(let e=0;et.seg)}(e,s);let o=null;for(const r of e||[]){if(!r?.id)continue;const e=Se(r);if(e)for(let a=0;ai)continue;let p=!1;for(const t of n)if(Bi([(l[0]+c[0])/2,(l[1]+c[1])/2],t)<2*s){p=!0;break}p||(!o||dt?.id),r=lo(e);if(r.length)return r.map(t=>ro(t,i));if(!n)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;too([t[0],t[1]],[t[2],t[3]],e))}function vo(t,e,i){let s=null;for(const n of e){const e=Bi(t,n);e<=i&&(!s||e.05)continue;const d=Rn(t,[e[0],e[1]],[e[2],e[3]],s,n);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-h[0],p[1]-h[1]);u0?l:o}(t,e,i,s,n,o);return On(t,[e[0],e[1]],[e[2],e[3]],r,s,n)}function yo(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],n=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const o of e)if(!(Bi(n,o)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Bi([o[0],o[1]],t)>i))for(const e of[[o[0],o[1]],[o[2],o[3]]])Bi(e,t)<=2*i&&s.push(e);return s}function wo(t,e,i,s){const n=lo(t);if(!n.length)return[];if(e.length!==i.length)return n;const o=[];for(const t of n){const n=ro(t,s),r=[n[0],n[1]],a=[n[2],n[3]];let l=r,c=a;for(let t=0;t{const e=co(t,[s[0],s[1],n[0],n[1]]);if(e.d>.001)return t;const i=h[0]-o[0],r=h[1]-o[1];return[o[0]+i*e.t,o[1]+r*e.t]};if(co([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],n[0],n[1]]).d<.01){l=d(r),c=d(a);break}}o.push(oo(l,c,s))}return o}class ko{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=Li(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],n=s?this.now()-s.at:1/0;return n{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 n=0;for(const o of i){const i=t?.urls?.[o];"string"==typeof i&&i?(s[o]={url:i,at:e},this.retry.delete(o),n++):this.backOff(o)}n&&(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,n]of Object.entries(this.signed))e.has(t)&&i-n.at{const e=Number(t);return Number.isFinite(e)?e:null};function Mo(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=So(e.x)&&null!=So(e.y)?{x:So(e.x),y:So(e.y),a:So(e.a??e.angle??e.theta)}:null;let s=null;const n=t.path?.points??t.path;if(Array.isArray(n)&&n.length){s=[];for(const t of n){const e=So(Array.isArray(t)?t[0]:t?.x),i=So(Array.isArray(t)?t[1]:t?.y);null!=e&&null!=i&&s.push([e,i])}s.length||(s=null)}const o=[],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=So(e.cx??e.center?.x),n=So(e.cy??e.center?.y);if(null==s||null==n){const t=So(e.x0),i=So(e.y0),o=So(e.x1),r=So(e.y1);null!=t&&null!=i&&null!=o&&null!=r&&(s=(t+o)/2,n=(i+r)/2)}if(null!=s&&null!=n||(s=So(e.x),n=So(e.y)),i&&null!=s&&null!=n){const r={id:t,name:i,cx:s,cy:n},a=So(e.x0),l=So(e.y0),c=So(e.x1),h=So(e.y1);null!=a&&null!=l&&null!=c&&null!=h&&(r.x0=Math.min(a,c),r.y0=Math.min(l,h),r.x1=Math.max(a,c),r.y1=Math.max(l,h)),o.push(r)}}const l=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||o.length||s?{pos:i,path:s,rooms:o,mapId:l}:null}function Co(t){const e=t?.attributes;return!(!e||!e.vacuum_position&&!e.robot_position)}const Do=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function To(t,e){const i=new Map(e.map(t=>[Do(t.name),t])),s=[],n=[];for(const e of t){const t=i.get(Do(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),n.push(e.name))}if(s.length<3)return null;const o=function(t){if(t.length<3)return null;let e=0,i=0,s=0,n=0,o=0,r=0,a=0,l=0,c=0,h=0,d=0,p=0;for(const[[u,_],[g,m]]of t){if(![u,_,g,m].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,n+=_*_,o+=_,r+=1,a+=u*g,l+=_*g,c+=g,h+=u*m,d+=_*m,p+=m}const u=[e,i,s,i,n,o,s,o,r],_=t=>{const[e,i,s,n,o,r,a,l,c]=u,h=e*(o*c-r*l)-i*(n*c-r*a)+s*(n*l-o*a);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-r*l)/h,(s*l-i*c)/h,(i*r-s*o)/h,(r*a-n*c)/h,(e*c-s*a)/h,(s*n-e*r)/h,(n*l-o*a)/h,(i*a-e*l)/h,(e*o-i*n)/h];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]]},g=_([a,l,c]),m=_([h,d,p]);if(!g||!m)return null;const f=[g[0],g[1],g[2],m[0],m[1],m[2]];return f.every(Number.isFinite)?f:null}(s);return o?{matrix:o,matched:n,residual:xo(o,s)}:null}function zo(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 n=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[n,o]=s.pop(),[r,a]=t[n],[l,c]=t[o],h=l-r,d=c-a,p=Math.hypot(h,d)||1e-9;let u=0,_=-1;for(let e=n+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([n,_],[_,o]))}const n=[];for(let e=0;e600&&(n=n.filter((t,e)=>e%2==0||e===n.length-1)),n}function Po(t){return"cleaning"===t||"returning"===t||"on"===t}const Ao={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function Ro(t){const[e,i]=Ao[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 Eo(t,e,i,s){const[n,o]=$o(Ro(e),i,s),r=Ro({...t,ox:0,oy:0}),[a,l]=$o(r,i,s);return{...t,ox:n-a,oy:o-l}}function No(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}function Oo(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 Io(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 Fo(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)}function Lo(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),n=[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 n){const i=e.find(e=>Fo(t,e.eid));if(i)return i.eid}for(const t of n)for(const e of ft){const i=t.find(t=>t.eid.split(".")[0]===e);if(i)return i.eid}for(const t of n)if(t.length)return t[0].eid}function qo(t,e){const i=!0===e.marker?.is_light?[...e.marker?.controls||[],...e.entities]:e.entities.filter(t=>t.startsWith("light."));return i.find(e=>"on"===t.states[e]?.state)||null}function Ho(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const n=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===n){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const o=e.attributes?.linkquality??e.attributes?.lqi;if(null!=o){const t=parseFloat(o);isNaN(t)||i.push(t)}}return Ye(i)}function Uo(t,e){for(const i of e){if(!Fo(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 Wo(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 Bo(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 jo(t,e){for(const i of e){if(!Bo(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 Vo(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden)continue;let n=null;if("group"===s.platform)n=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if("Group"!==e?.model)continue;n=e.area_id||s.area_id||null}}if(!n)continue;const o=t.states[e];i.push({eid:e,name:s.name||o?.attributes?.friendly_name||e,area:n})}return i}function Go(t,e,i,s,n){const o=mt(e,i,n);if(o!==gt)return o;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=_t[e];if(t)return t}return null}(r)??gt}function Ko(t,e){t.marker=e,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 Yo(t){const{hass:e,areaToSpace:i,markers:s,settings:n,excluded:o,showAll:r,firstSpaceId:a,loc:l,iconRules:c}=t,h=!1!==n.group_lights,d=Vo(e,h),p=new Set(d.map(t=>t.area)),u=Oo(e),_=new Set;for(const t of s){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||_.add(t.binding)}const g=(t,e)=>s.find(i=>i.binding===t+":"+e),m={},f=[];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(_.has("device:"+t.id))continue;const a=g("device",t.id);if(a&&a.hidden&&!n.filter_seeded)continue;const d=u[t.id]||[],v=Io(e,t,d),b=!n.filter_seeded;if(b&&!r){if(o.has(v))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===v&&t.via_device_id)continue}const y=(t.name_by_user||t.name||l("device.unnamed")).trim(),w=y+"|"+s;let k=Go(e,y,t.model,d,c);if(d.some(t=>t.startsWith("lock."))&&(k="mdi:lock"),b&&!r&&h&&"mdi:lightbulb"===k&&p.has(s))continue;m[w]=(m[w]||0)+1;const $=m[w]>1?y+" "+m[w]:y,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=Lo(e,d,k),"mdi:thermometer"!==k&&"mdi:air-filter"!==k||(x.temp=Uo(e,d)),x.primary&&Bo(e,x.primary)&&(x.hum=jo(e,d)),f.push(x)}for(const t of d)i[t.area]&&(_.has("entity:"+t.eid)||f.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.hidden&&!n.filter_seeded)continue;const[s,o]=t.binding.split(":");if("device"===s){const s=e.devices[o],n=t.area||s?.area_id||"",r=n&&i[n]||t.space||a,h=s&&u[s.id]||[];let d=s?Go(e,s.name_by_user||s.name||"",s.model,h,c):"mdi:help-circle";h.some(t=>t.startsWith("lock."))&&(d="mdi:lock");const p={id:t.id,name:s?.name_by_user||s?.name||l("device.fallback"),model:s?.model||"",area:n,space:r,icon:d,entities:h,bindingKind:"device",bindingRef:o};p.primary=Lo(e,h,d),"mdi:thermometer"!==d&&"mdi:air-filter"!==d||(p.temp=Uo(e,h)),p.primary&&Bo(e,p.primary)&&(p.hum=jo(e,h)),p.primary&&Bo(e,p.primary)&&(p.hum=jo(e,h)),Ko(p,t),f.push(p)}else if("entity"===s){const s=e.entities[o],n=t.area||s?.area_id||s?.device_id&&e.devices[s.device_id]?.area_id||"",r=n&&i[n]||t.space||a,l=e.states[o],h=s?.name||l?.attributes?.friendly_name||o;let d=Go(e,h,"",[o],c);o.startsWith("lock.")&&(d="mdi:lock");const p={id:t.id,name:h,model:"",area:n,space:r,icon:d,entities:[o],primary:o,bindingKind:"entity",bindingRef:o};"mdi:thermometer"!==d&&"mdi:air-filter"!==d||(p.temp=Uo(e,[o])),Bo(e,o)&&(p.hum=jo(e,[o])),Ko(p,t),f.push(p)}else{const e=t.area||"",s=t.space||e&&i[e]||a,n={id:t.id,name:t.name||l("device.virtual"),model:t.model||"",area:e,space:s,icon:t.icon||"mdi:map-marker",entities:[],bindingKind:"virtual",virtual:!0};Ko(n,t),f.push(n)}}return f}function Xo(t,e,i){let s=!1;for(const n of e)if(n.area===i&&!n.hidden)for(const e of n.entities)if(e.startsWith("light.")&&(s=!0,"on"===t.states[e]?.state))return"on";return s?"off":"none"}function Zo(t,e,i){if(!e)return null;const s=e.indexOf(":");if(s<0)return null;const n=e.slice(0,s),o=e.slice(s+1);if(!o)return null;if("entity"===n){const e=parseFloat(t.states[o]?.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===o).map(([t])=>t);return"temp"===i?Uo(t,e):jo(t,e)}return null}const Jo=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 Qo={"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.remove":"Remove","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":"Add a room: connect grid dots with lines until the outline closes","title.markup_merge":"Merge rooms: 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: click a virtual wall to make it solid; a shared solid wall merges the two rooms; an outer wall or inside a room deletes the room (with confirm).","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":"Add","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","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} · Esc/Ctrl+Z — undo a dot · close the outline by clicking the first one","markup.hint_start":"click a grid dot to start the outline","tip.lqi":"average zigbee signal:","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 badge","display.ripple":"Ripple only","display.icon_ripple":"Icon + ripple","marker.ripple_size":"Ripple 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":'Remove "{name}" from the plan?',"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.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 removed 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","editor.aspect_ratio":"Aspect ratio (e.g. 16:9 or auto)","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.color":"Color","decor.width":"Line width","decor.w_thin":"Thin","decor.w_mid":"Medium","decor.w_thick":"Thick","decor.fill":"Fill","decor.text_title":"Text label","decor.text_label":"Text","decor.text_hint":"A new line here is a new line on the plan; the block is centred. Drag a corner to resize it, the handle above it to turn it.","decor.text_hint_live":"{} is where the value lands. Without it the value is added at the end. A new line here is a new line on the plan.","decor.live_group":"Live value","decor.live_hint":"The label may show one entity's value — as Home Assistant reports it, with no rounding. A dead entity shows a dash.","decor.live_entity":"Entity","decor.live_entity_ph":"none — a plain label","decor.live_attr":"Attribute","decor.live_attr_ph":"the state","decor.live_unit":"Unit","decor.live_preview":"Preview","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag the picture to move it, pull a corner to resize it. Shift — off the grid.","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)","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 light sources","marker.controls_hint":"With tap action “Toggle”, a click flips all bound lights at once (any on → all off). The icon mirrors their state.","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":"Wall thickness","title.markup_wallthick":"Wall 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 for new room walls. Empty or zero — thin walls. 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","markup.openwall":"Open boundary","title.markup_openwall":"Open boundary — click a point on a shared wall, then a second point on the same wall (within the nearest corners) to make that stretch virtual. Click a dashed stretch to close it. 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.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 device from plan","marker.hide_tip":'The device is not drawn on the plan but still counts toward the room signal. To see it: the "Show hidden" button in the device editor.',"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":"Grid","gs.grid_hint":"Everything you draw or drag now lands on the grid. Hold Shift to place something between the nodes on purpose.","gs.align_all":"Align everything to the grid","gs.align_title":"Align everything to the grid","gs.align_none":"Everything is already on the grid — nothing to move.","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_warn":"This rewrites the plan and the device positions in one go. There is no undo: the only way back is to move things by hand. If some elements are deliberately between the nodes, cancel.","gs.align_run":"Align","gs.align_done":"Aligned to the grid: {n} elements moved","decor.furniture":"Furniture","furn.title":"Furniture library","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 tr={en:Qo,ru:{"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.remove":"Убрать","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":"Добавить комнату: соединяйте точки сетки линиями до замкнутого контура","title.markup_merge":"Объединить комнаты: клик по одной, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить: виртуальная стена → обычная; общая глухая → слияние комнат; внешняя или клик внутрь → удалить комнату (с подтверждением)","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":"Проём","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} · Esc/Ctrl+Z — убрать точку · замкните контур кликом по первой","markup.hint_start":"кликните точку сетки, чтобы начать контур","tip.lqi":"средний сигнал zigbee:","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.ripple":"Только пульсация","display.icon_ripple":"Значок + пульсация","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.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.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":"Путь дашборда (куда вести)","editor.aspect_ratio":"Соотношение сторон (напр. 16:9 или auto)","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.color":"Цвет","decor.width":"Толщина линии","decor.w_thin":"Тонкая","decor.w_mid":"Средняя","decor.w_thick":"Толстая","decor.fill":"Залить","decor.text_title":"Надпись","decor.text_label":"Текст","decor.text_hint":"Перевод строки здесь — перевод строки на плане; блок выравнивается по центру. Тяните угол — меняет размер, ручка сверху — поворот.","decor.text_hint_live":"{} — место, куда встанет значение. Без него значение добавится в конец. Перевод строки здесь — перевод строки на плане.","decor.live_group":"Живое значение","decor.live_hint":"Надпись может показывать значение одной сущности — так, как его отдаёт Home Assistant, без округления. Мёртвая сущность — прочерк.","decor.live_entity":"Сущность","decor.live_entity_ph":"нет — обычная надпись","decor.live_attr":"Атрибут","decor.live_attr_ph":"состояние","decor.live_unit":"Единица","decor.live_preview":"Предпросмотр","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — двигается, тяните угол — меняет размер. Shift — мимо сетки.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","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":"Толщина стен","title.markup_wallthick":"Толщина стен — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина стен новой комнаты. Пустое или ноль — без толщины. Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","markup.openwall":"Открытая граница","title.markup_openwall":"Открытая граница — клик по точке на общей стене, затем второй клик на той же стене (не дальше ближайших углов) открывает отрезок. Клик по пунктиру закрывает. Курсор — крестик.","toast.openwall_pick":"Кликните по стене, разделяющей две комнаты","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.openwall_opened_span":"Виртуальный отрезок открыт","toast.openwall_closed_span":"Виртуальный отрезок закрыт","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":"Устройство не отображается на плане, но участвует в расчёте сигнала комнаты. Показать: кнопка «Показать скрытые» в редакторе устройств.","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":"Всё, что вы рисуете и перетаскиваете, ложится строго на сетку. Shift — временно поставить элемент между узлами.","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_warn":"Действие разом перезапишет план и позиции устройств. Отмены нет: вернуть можно только руками. Если какие-то элементы стоят между узлами намеренно — отмените.","gs.align_run":"Выровнять","gs.align_done":"Выровнено по сетке: сдвинуто элементов — {n}","decor.furniture":"Мебель","furn.title":"Библиотека мебели","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 er(t,e){if(e&&e in tr)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function ir(t,e,i){return _i(tr[t][e]??Qo[e]??e,i)}class sr 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 er(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:ir(e,"editor.lang_auto")},{value:"en",label:ir(e,"editor.lang_en")},{value:"ru",label:ir(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 V;this._loadSpaces();const t=this._lang,e={title:ir(t,"editor.title"),default_floor:ir(t,"editor.default_floor"),language:ir(t,"editor.language"),icon_size:ir(t,"editor.icon_size"),show_temperature:ir(t,"editor.show_temperature"),live_states:ir(t,"editor.live_states"),show_signal:ir(t,"editor.show_signal"),kiosk:ir(t,"editor.kiosk"),cycle:ir(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)}}ir.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",ir);const sr=o` + >`}_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)}}sr.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",sr);const nr=o` :host { --hp-bg: var(--card-background-color, #16212e); --hp-line: var(--divider-color, #2b3d4f); @@ -2508,7 +2508,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow z-index: 120; max-width: 90vw; } - `;function nr(t){const e=Ns(t.cfg),i=e.find(e=>e.id===t.spaceId);if(!i)return null;const s=fi(t.cfg.spaces.find(e=>e.id===t.spaceId)),n=t.iconSize??2.5,o=n>8?2.5:n,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):ht,l=pt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:dt),c=Ko({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=>er(t.lang,e),iconRules:l}),h=c.filter(e=>e.space===t.spaceId),d=h.filter(t=>!t.hidden),p=function(t,e,i){const s={},n=i/100*sn(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 o=rn(i),r=.1*Math.min(o.w,o.h),a=o.w-2*r,l=o.h-2*r,c=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),h=a/c,d=l/Math.max(Math.ceil(e.length/c),1),p=e.map((t,e)=>({x:o.x+r+h*(e%c+.5),y:o.y+r+d*(Math.floor(e/c)+.5)}));Ze(p,o,n,.5*r),e.forEach((t,e)=>s[t.id]=Us(p[e]))}return s}(h,i,o),u=[];for(const e of d){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*zs,e=i.y*zs;u.push({minX:t,minY:e,maxX:t,maxY:e})}}const _=tn(i,u),g=[_.x,_.y,_.w,_.h],m=i.rooms.filter(t=>t.area||s.showBorders).map(e=>{let n="room "+(i.bg?"overlay":"yard"),o="";const r=Hi(s.fill,e);if(s.showBorders||"none"!==r){n+=" styled";const i=[`--room-stroke:${s.color}`,`--room-stroke-op:${s.showBorders?s.opacity:0}`],a=e.area?$i(r,"lqi"===r?function(t,e,i){const s=[];for(const n of e){if(n.area!==i||n.virtual)continue;const e=Ho(t,n.entities);null!=e&&s.push(e)}return Ye(s)}(t.hass,h,e.area):null,"light"===r?Yo(t.hass,h,e.area):"none","temp"===r?function(t,e,i){const s=[];for(const n of e){if(n.area!==i)continue;if("mdi:thermometer"!==n.icon&&"mdi:air-filter"!==n.icon)continue;const e=qo(t,n.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,h,e.area):null,s.tempMin,s.tempMax,wi(t.cfg?.settings)):null;a?(n+=" filled",i.push(`--room-fill:${a.c}`,`--room-fill-op:${a.a.toFixed(3)}`)):i.push("--room-fill:transparent","--room-fill-op:0"),o=i.join(";")}const a=!i.bg&&!s.showNames,l=an(e),c=e.id||V,d=e.area||V,p=e.poly?B`e.id===t.spaceId);if(!i)return null;const s=fi(t.cfg.spaces.find(e=>e.id===t.spaceId)),n=t.iconSize??2.5,o=n>8?2.5:n,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):ht,l=pt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:dt),c=Yo({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=>ir(t.lang,e),iconRules:l}),h=c.filter(e=>e.space===t.spaceId),d=h.filter(t=>!t.hidden),p=function(t,e,i){const s={},n=i/100*sn(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 o=rn(i),r=.1*Math.min(o.w,o.h),a=o.w-2*r,l=o.h-2*r,c=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),h=a/c,d=l/Math.max(Math.ceil(e.length/c),1),p=e.map((t,e)=>({x:o.x+r+h*(e%c+.5),y:o.y+r+d*(Math.floor(e/c)+.5)}));Ze(p,o,n,.5*r),e.forEach((t,e)=>s[t.id]=Us(p[e]))}return s}(h,i,o),u=[];for(const e of d){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*zs,e=i.y*zs;u.push({minX:t,minY:e,maxX:t,maxY:e})}}const _=tn(i,u),g=[_.x,_.y,_.w,_.h],m=i.rooms.filter(t=>t.area||s.showBorders).map(e=>{let n="room "+(i.bg?"overlay":"yard"),o="";const r=qi(s.fill,e);if(s.showBorders||"none"!==r){n+=" styled";const i=[`--room-stroke:${s.color}`,`--room-stroke-op:${s.showBorders?s.opacity:0}`],a=e.area?$i(r,"lqi"===r?function(t,e,i){const s=[];for(const n of e){if(n.area!==i||n.virtual)continue;const e=Ho(t,n.entities);null!=e&&s.push(e)}return Ye(s)}(t.hass,h,e.area):null,"light"===r?Xo(t.hass,h,e.area):"none","temp"===r?function(t,e,i){const s=[];for(const n of e){if(n.area!==i)continue;if("mdi:thermometer"!==n.icon&&"mdi:air-filter"!==n.icon)continue;const e=Uo(t,n.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,h,e.area):null,s.tempMin,s.tempMax,wi(t.cfg?.settings)):null;a?(n+=" filled",i.push(`--room-fill:${a.c}`,`--room-fill-op:${a.a.toFixed(3)}`)):i.push("--room-fill:transparent","--room-fill-op:0"),o=i.join(";")}const a=!i.bg&&!s.showNames,l=an(e),c=e.id||V,d=e.area||V,p=e.poly?B``:B``;return B`${p}${a?B`${e.name}`:V}`}),f=d.map(e=>{const s=function(t,e,i,s,n){const o=e[t.id];return o&&o.s===t.space?{x:o.x*zs,y:o.y*zs}:s[t.id]?s[t.id]:Us(en(n))}(e,t.layout,t.cfg,p,i),n=(s.x-g[0])/g[2]*100,o=(s.y-g[1])/g[3]*100,r=Number(e.marker?.size)>0?Number(e.marker.size):1,a=Number(e.marker?.angle)||0,l=[`left:${n}%`,`top:${o}%`];return 1!==r&&l.push(`--dev-scale:${r}`),W`
`}),v=s.showNames?i.rooms.filter(t=>t.name).map(e=>{const n=function(t,e,i){const s=i["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*zs,y:s.y*zs};const n=an(t);return Us({x:n[0],y:n[1]})}(e,i.id,t.layout,t.cfg),o=(n.x-g[0])/g[2]*100,r=(n.y-g[1])/g[3]*100,a=Math.min(1,s.opacity+.25);return W`
${e.name}
`}):[],b=i.bg?t.displayUrl?t.displayUrl(i.bg.href):i.bg.href:"",y=t.cfg.spaces.find(e=>e.id===t.spaceId)?.settings||{};let w="";if("daynight"===Ms(t.cfg?.settings,y)&&null!==Ss(t.cfg?.settings,y)){const e=Ts(t.hass);e&&(w=ms(e.elevation).bg)}const k=w||vi(t.cfg?.settings,s),$=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},x=Array.isArray($.walls)?$.walls:[],S=Number($.cell_cm)>0?Number($.cell_cm):5,M=x.length?so(i.rooms,x,[],Hs,S,Ls,zs):Me(i.rooms),C=wi(t.cfg.settings),D=x.length&&s.showBorders?eo(i.rooms,x,[],[],Hs,S,Ls,zs):null,T=t.stageWidth&&g[2]?t.stageWidth/g[2]:1,z=!!D&&yn(D.depthUnits,T);return W` + style="left:${o}%;top:${r}%;color:${s.color};opacity:${a}">${e.name}`}):[],b=i.bg?t.displayUrl?t.displayUrl(i.bg.href):i.bg.href:"",y=t.cfg.spaces.find(e=>e.id===t.spaceId)?.settings||{};let w="";if("daynight"===Ms(t.cfg?.settings,y)&&null!==Ss(t.cfg?.settings,y)){const e=Ts(t.hass);e&&(w=ms(e.elevation).bg)}const k=w||vi(t.cfg?.settings,s),$=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},x=Array.isArray($.walls)?$.walls:[],S=Number($.cell_cm)>0?Number($.cell_cm):5,M=x.length?so(i.rooms,x,[],qs,S,Ls,zs):Me(i.rooms),C=wi(t.cfg.settings),D=x.length&&s.showBorders?eo(i.rooms,x,[],[],qs,S,Ls,zs):null,T=t.stageWidth&&g[2]?t.stageWidth/g[2]:1,z=!!D&&yn(D.depthUnits,T);return W`
${D?B` @@ -2538,13 +2538,13 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${""}
${f}${v}
- `}let or=null,rr=null,ar=!1;const lr=new Set;function cr(){if(or)return or;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 hr(t){return or?Promise.resolve(or):rr||(rr=async function(t){const[e,i]=await Promise.all([t.callWS({type:"houseplan/config/get"}),t.callWS({type:"houseplan/layout/get"})]);if(or={config:e?.config??null,rev:e?.rev??0,layout:i?.layout??{}},!ar&&t.connection?.subscribeEvents){ar=!0;const e=()=>{or=null,lr.forEach(t=>t())};try{await t.connection.subscribeEvents(e,"houseplan_config_updated"),await t.connection.subscribeEvents(e,"houseplan_layout_updated")}catch{ar=!1}}return or}(t).finally(()=>{rr=null}),rr)}class dr 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 tr(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:"aspect_ratio",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}}]}render(){if(!this.hass||!this._config)return V;this._loadSpaces();const t=this._lang,e={space:er(t,"editor.space"),title:er(t,"editor.title"),show_button:er(t,"editor.show_button"),button_label:er(t,"editor.button_label"),button_target:er(t,"editor.button_target"),aspect_ratio:er(t,"editor.aspect_ratio"),icon_size:er(t,"editor.icon_size")};return W`{rr=null,cr.forEach(t=>t())};try{await t.connection.subscribeEvents(e,"houseplan_config_updated"),await t.connection.subscribeEvents(e,"houseplan_layout_updated")}catch{lr=!1}}return rr}(t).finally(()=>{ar=null}),ar)}class pr 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 er(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:"aspect_ratio",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}}]}render(){if(!this.hass||!this._config)return V;this._loadSpaces();const t=this._lang,e={space:ir(t,"editor.space"),title:ir(t,"editor.title"),show_button:ir(t,"editor.show_button"),button_label:ir(t,"editor.button_label"),button_target:ir(t,"editor.button_target"),aspect_ratio:ir(t,"editor.aspect_ratio"),icon_size:ir(t,"editor.icon_size")};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)}}dr.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",dr);const pr=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 ur extends lt{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._stageWidth=0,this._loadedOnce=!1,this._signer=new wo(()=>this.requestUpdate()),this._goToSpace=()=>{const t=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");pr(`${t}#space=${encodeURIComponent(this._config.space)}`)}}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(t){const e=cr();return{type:"custom:houseplan-space-card",space:Ns(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||cr()}connectedCallback(){var t;super.connectedCallback(),this._unsub=(t=()=>{this._loading=!1,this._snap=null,this.requestUpdate()},lr.add(t),()=>lr.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 hr(this.hass);this._snap=t,this._loadedOnce=!0}catch{}finally{this._loading=!1,this.requestUpdate()}}}get _lang(){return tr(this.hass,this._config?.language)}getCardSize(){const t=Ns(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 Fi(this._snap?.config)}render(){if(!this._config)return V;const t=this._snap?.config;if(!t)return W`
${er(this._lang,"space_card.loading")}
`;const e=this._config.space,i=nr({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(er(this._lang,"space_card.not_found",{id:e}));const s=Ns(t).find(t=>t.id===e),n=void 0!==this._config.title?this._config.title:s?.title||"",o=!1!==this._config.show_button,r=this._config.button_label||er(this._lang,"space_card.button");return W` + >
`}_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)}}pr.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",pr);const ur=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 _r extends lt{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._stageWidth=0,this._loadedOnce=!1,this._signer=new ko(()=>this.requestUpdate()),this._goToSpace=()=>{const t=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");ur(`${t}#space=${encodeURIComponent(this._config.space)}`)}}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(t){const e=hr();return{type:"custom:houseplan-space-card",space:Ns(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||hr()}connectedCallback(){var t;super.connectedCallback(),this._unsub=(t=()=>{this._loading=!1,this._snap=null,this.requestUpdate()},cr.add(t),()=>cr.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 dr(this.hass);this._snap=t,this._loadedOnce=!0}catch{}finally{this._loading=!1,this.requestUpdate()}}}get _lang(){return er(this.hass,this._config?.language)}getCardSize(){const t=Ns(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 Fi(this._snap?.config)}render(){if(!this._config)return V;const t=this._snap?.config;if(!t)return W`
${ir(this._lang,"space_card.loading")}
`;const e=this._config.space,i=or({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(ir(this._lang,"space_card.not_found",{id:e}));const s=Ns(t).find(t=>t.id===e),n=void 0!==this._config.title?this._config.title:s?.title||"",o=!1!==this._config.show_button,r=this._config.button_label||ir(this._lang,"space_card.button");return W` ${n?W`
${n}
`:V} ${i} @@ -2552,7 +2552,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow `:V}
- `}}ur.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0}},ur.styles=[sr,o` + `}}_r.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0}},_r.styles=[nr,o` .hp-static-title { font-weight: 700; padding: 10px 14px 6px; @@ -2627,7 +2627,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",ur),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 _r=4.166666666666667e-9;function gr(t){if(!Number.isFinite(t))return t;const e=Math.round(t/Hs)*Hs;return Math.abs(e-t)<=_r?t:e}const mr=(t,e,i,s)=>Math.hypot(i-t,s-e),fr=(t,e,i,s,n,o,r,a)=>Math.hypot(Math.max(Math.abs(n-t),Math.abs(n+r-(t+i))),Math.max(Math.abs(o-e),Math.abs(o+a-(e+s)))),vr=(t,e,i,s,n,o,r)=>{const a=Math.max(Number(s)||0,0)/2,l=Math.PI/180,c=Math.cos(i*l)*a,h=Math.sin(i*l)*a,d=Math.cos(r*l)*a,p=Math.sin(r*l)*a,u=Math.max(mr(t+c,e+h,n+d,o+p),mr(t-c,e-h,n-d,o-p)),_=Math.max(mr(t+c,e+h,n-d,o-p),mr(t-c,e-h,n+d,o+p));return Math.min(u,_)},br=t=>{const e=Number(t?.cell_cm);return e>0?e:5};const yr="1.59.0-beta.8",wr=1500,kr=new Map;let $r=0;const xr=t=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(t??{})}`;let Sr=1e4;const Mr="houseplan_card_layout_v1",Cr="houseplan_card_cfg_v1",Dr="houseplan_card_zoom_v1",Tr="houseplan_card_nav_v1",zr="houseplan_card_kiosk_v1",Pr=1e3,Ar=(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}},Rr=(t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)},Er=(t,e)=>{let i,s=null;const n=(...n)=>{clearTimeout(i),s=n,i=window.setTimeout(()=>{i=void 0;const e=s;s=null,e&&t(...e)},e)};return n.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const e=s;s=null,e&&t(...e)},n.pending=()=>void 0!==i,n},Nr=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}};class Or 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._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={color:"#607d8b",width:3,fill:!1},this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorTextDialog=null,this._furnPalette=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._tool="draw",this._wallDialog=null,this._drawWallField=null,this._rszSel=null,this._rszDrag=null,this._rszUndo=[],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._opShift=!1,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._warmRevivePending=!1,this._warmGen=++$r,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._senseRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._vacVisHandler=()=>{"visible"===document.visibilityState&&(this._vacJumpOnce=!0,this._skyElev=null,this.requestUpdate())},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._space=t,this._selId=null,this._restoreZoom(),this.requestUpdate())},this._drag=null,this._rlResize=null,this._holdFired=!1,this._cfgEpoch=0,this._modelCache=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 wo(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=Er(()=>{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(Mr,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._saveConfigDebounced=Er(()=>{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._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:vi(this._settings,{bgColor:null})||null,northDeg:Ss(this._settings,{}),bgMode:Ms(this._settings,{}),sunRays:Cs(this._settings,{}),weatherEntity:Ds(this._settings)||"",busy:!1}},this._openAlignDialog=()=>{if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[],e=function(t,e){const i=JSON.parse(JSON.stringify(t||[])),s=JSON.parse(JSON.stringify(e||{}));let n=0,o=0,r=0,a=0,l="",c=0;const h={};let d=5;for(const t of i){const e=br(t);null!=t?.id&&(h[String(t.id)]=e),e>d&&(d=e)}const p=(t,e,i,s=!1)=>{if(!(t>_r||s))return;n++,t>r&&(r=t);const o=t*Fs*e;o>a&&(a=o,l=i)};for(const t of i){const e=br(t),i=null!=t?.id?String(t.id):"";for(const s of t.rooms||[]){o++;let t=0;if(s.poly?.length)s.poly=s.poly.map(e=>{const i=[gr(e[0]),gr(e[1])];return t=Math.max(t,mr(e[0],e[1],i[0],i[1])),i});else if(null!=s.x&&null!=s.y){const e=s.x,i=s.y,n=s.w||0,o=s.h||0,r=gr(e+n),a=gr(i+o),l=gr(e),c=gr(i),h=Math.max(Hs,r-l),d=Math.max(Hs,a-c);t=fr(e,i,n,o,l,c,h,d),s.x=l,s.y=c,s.w=h,s.h=d}p(t,e,i)}for(const s of t.decor||[]){o++;let t=0;if("line"===s.kind){const e=[gr(s.x1),gr(s.y1)],i=[gr(s.x2),gr(s.y2)];t=Math.max(mr(s.x1,s.y1,e[0],e[1]),mr(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=gr(s.x),i=gr(s.y);if(null!=s.w&&null!=s.h){const n=gr(s.x+s.w),o=gr(s.y+s.h),r=Math.max(Hs,n-e),a=Math.max(Hs,o-i);t=fr(s.x,s.y,s.w,s.h,e,i,r,a),s.w=r,s.h=a}else t=mr(s.x,s.y,e,i);s.x=e,s.y=i}p(t,e,i)}for(const s of t.openings||[]){o++;const n=De([s.x,s.y],t.rooms||[],.025,{step:Hs,length:Number(s.length)||0});if(!n)continue;const r=Number(s.angle),a=!(Number.isFinite(r)&&r===n.angle),l=vr(s.x,s.y,Number.isFinite(r)?r:n.angle,Number(s.length)||0,n.x,n.y,n.angle);s.x=n.x,s.y=n.y,s.angle=n.angle,a&&c++,p(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;o++;const n=gr(i.x),r=gr(i.y),a=mr(i.x,i.y,n,r);s[t]={...i,x:n,y:r};const l="string"==typeof i.s?i.s:"";p(a,h[l]??d,l)}return{spaces:i,layout:s,report:{moved:n,total:o,maxShift:r,maxShiftCm:a,maxSpace:l,rotated:c},changed:n>0}}(t,this._layout||{}),i=Math.ceil(10*e.report.maxShiftCm)/10,s=t.find(t=>null!=t?.id&&String(t.id)===e.report.maxSpace),n=t.length>1&&s?String(s.title||s.id):"";this._alignDialog={report:e.report,spaces:e.spaces,layout:e.layout,cm:i,where:n,busy:!1}},this._openRulesDialog=()=>{if(!this._norm)return;const t=this._settings.icon_rules,e=(t&&t.length?t:dt).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)}_slideTo(t,e){if(t===this._space)return;const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;this._space=t,this._selId=null,this._restoreZoom(),i||(this._slide=e,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slide="",this.requestUpdate()},260),this.requestUpdate())}_cycleTick(){if(this._kiosk&&Number(this._config?.cycle)>0&&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,()=>Fi(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},wr)),!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))}disconnectedCallback(){document.removeEventListener("visibilitychange",this._vacVisHandler),this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._skySnapRaf&&(cancelAnimationFrame(this._skySnapRaf),this._skySnapRaf=0);for(const t of this._senseRt.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._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),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),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._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._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0)}if("decor"===this._mode)return"Delete"!==t.key&&"Backspace"!==t.key||!this._decorSel||t.target?.closest?.("input, textarea, select")?void("Escape"===t.key&&(t.preventDefault(),this._decorDraft?this._decorDraft=null: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)return;const e="Escape"===t.key||(t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase();if(e){if(this._roomDialog)return t.preventDefault(),void this._roomDialogCancel();if("draw"===this._tool&&this._path.length)return t.preventDefault(),void this._undoPoint();if(e)return"resize"===this._tool?(t.preventDefault(),this._rszDrag?void this._rszCancelDrag():(t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase()?void this._rszUndoPop():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?(t.preventDefault(),void(this._openWallAnchor?this._openWallAnchor=null:this._tool="draw")):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool||(t.preventDefault(),this._tool="draw"))}}_undoPoint(){this._path.length&&(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 kr.values())for(const e of t)clearTimeout(e.evict);kr.clear(),Sr=t&&t>0?t:1e4}static _warmBootStats(){let t=0,e=0;const i=[];for(const s of kr.values())for(const n of s)t++,n.dlg&&(e++,i.push(n.dlg.kind));return{keys:kr.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(Dr)||"{}")||{}}catch{this._zoomBySpace={}}try{const t=JSON.parse(localStorage.getItem(zr)||"null");this._kioskScale={icon:qi(t?.icon),font:qi(t?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(Cr)||"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()}_warmAdopt(){if(this._config?.kiosk)return;const t=xr(this._config);if(this._warmKey===t&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const e=this.parentNode,i=this._warmIdx(e),s=kr.get(t);if(!s||!s.length)return;const n=s.find(t=>t.owner===this._warmGen);if(n)return clearTimeout(n.evict),n.evict=0,n.freed=0,n.live=!0,this._warmSlot=n,void(this._warmKey=t);const{slot:o,sure:r}=((t,e,i,s)=>{const n=t=>{const e=!!i&&t.place?.deref()===i;return e&&t.idx===s?4:t.live?0:e?3:2};let o=null,r=0,a=0,l=null;for(const i of t){if(i.owner===e)continue;l=i;const t=n(i);t<=0||(t>r?(o=i,r=t,a=1):t===r&&a++)}return!o||a>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(s,this._warmGen,e,i);o&&(this._booting=!1,this._bootFading=!1,this._hdrH=o.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},wr)),this._warmKey=t,r?(clearTimeout(o.evict),o.evict=0,o.owner=this._warmGen,o.place=e?new WeakRef(e):null,o.idx=i,o.live=!0,this._warmSlot=o,this._warmVp=o.vp,this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,place:e?new WeakRef(e):null,idx:i,live:!0,hdrH:o.hdrH,stageH:o.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 n=kr.get(e);if(!t.live&&n&&n.length>1){const e=n.indexOf(t);e>=0&&n.splice(e,1)}},Sr+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=xr(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=kr.get(i)||[];for(s.push(this._warmSlot),kr.set(i,s),this._warmTrim(s);kr.size>8;){const t=kr.keys().next().value;if(void 0===t||t===i)break;for(const e of kr.get(t)||[])clearTimeout(e.evict);kr.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._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._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>Sr)&&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"decorText":this._decorTextDialog={...e.data};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(Cr,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.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 Ns(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:n,excluded:o,iconRules:r}=t,a=!1!==n.group_lights,l=jo(e,a),c=new Set(l.map(t=>t.area)),h=No(e),d=new Set(s.map(t=>t.binding)),p=[];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(d.has("device:"+t.id))continue;const n=h[t.id]||[],l=Oo(e,t,n);let u=o.has(l)||"Group"===t.model||/scene/i.test(t.model||"")||/bridge/i.test((t.model||"")+(t.name||""))||"myheat"===l&&!!t.via_device_id;!u&&a&&c.has(s)&&"mdi:lightbulb"===Vo(e,(t.name_by_user||t.name||"").trim(),t.model,n,r)&&(u=!0),u&&p.push("device:"+t.id)}return p}({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=pt(t)),this._rulesCompiled}get _fillColors(){return wi(this._settings)}get _excluded(){const t=this._settings.exclude_integrations;return t?new Set(t):ht}willUpdate(t){t?.has?.("hass")&&(this._vacTick(),this._senseTick()),this._skyPlan(),t.has("hass")&&this.hass&&(this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices())}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),n=Math.round(i+s);n>=0&&Math.abs(n-this._hdrH)>1&&(this._hdrH=n),n>=0&&!this._booting&&!this._config?.kiosk&&t.clientHeight>0&&this._warmPatch({hdrH:n,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._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null);const i=t?.config;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=>{(t?.data?.rev??-1)!==this._cfgRev&&this._reloadConfigOnly()},"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(),n=this._savedNav();!this._hashApplied&&s&&this._model.find(t=>t.id===s)?(this._space=s,this._hashApplied=!0):n?.space&&!this._navApplied&&!this._hashApplied&&this._model.find(t=>t.id===n.space)?(this._space=n.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(Mr)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1}this._regSignature="",this.requestUpdate()}async _reloadConfigOnly(t=!1){if(!t&&(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting))return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(),400));try{const t=await this.hass.callWS({type:"houseplan/config/get"}),e=t?.config;this._serverCfg=e&&Array.isArray(e.spaces)?e:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,"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,Fi(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._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")+":"+tr(t,this._config?.language);e===this._regSignature&&this._devices.length||(this._regSignature=e,this._devices=Ko({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())}_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:n}=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:n,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=Ho(this.hass,i.entities);null!=s&&e.push(s)}return Ye(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),n=Math.min(...i);return{x:s,y:n,w:Math.max(...e)-s,h:Math.max(...i)-n}}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*sn(i)*1.3;for(const e of i.rooms){if(!e.area)continue;const n=this._devices.filter(t=>t.area===e.area&&t.space===i.id);if(!n.length)continue;const o=this._roomBounds(e),r=.1*Math.min(o.w,o.h),a=o.w-2*r,l=o.h-2*r,c=Math.max(1,Math.round(Math.sqrt(n.length*a/Math.max(l,1)))),h=Math.ceil(n.length/c),d=a/c,p=l/Math.max(h,1),u=n.map((t,e)=>({x:o.x+r+d*(e%c+.5),y:o.y+r+p*(Math.floor(e/c)+.5)}));Ze(u,o,s,.5*r),n.forEach((e,i)=>t[e.id]=Us(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*Pr,y:i.y*Pr}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[t.id]?this._defPos[t.id]:Us(en(e))}_savePos(t,e,i,s=!1){if(this._norm){const n=this._gridPitch,o=s?e:Math.round(e/n)*n,r=s?i:Math.round(i/n)*n,a=this._layout[t.id]?.k;this._layout={...this._layout,[t.id]:{s:t.space,x:Bs(o/Pr),y:Bs(r/Pr),...a?{k:a}:{}}}}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?ri(t.entities):null}_actEntity(t){return this._coverIndicator(t)||t.primary}_stateClass(t){if(!this._config?.live_states)return"";const e=this._coverIndicator(t);if(e){const t=this.hass.states[e];return t?"unavailable"===t.state?"unavail":ai(t.state)?"covermove":"":""}const i=(t.marker?.controls||[]).filter(Ai);if(i.length)return i.some(t=>"on"===this.hass.states[t]?.state)?"on":"";if(Lo(this.hass,t))return"on";const s=this._actEntity(t),n=s?this.hass.states[s]:void 0;if(!n)return"";if("unavailable"===n.state)return"unavail";const o=s.split(".")[0];if(["light","switch","fan","humidifier"].includes(o))return"on"===n.state?"on":"";if("climate"===o){const t=n.attributes?.hvac_action;return null!=t?["heating","cooling","drying","fan"].includes(t)?"on":"":["off","unknown"].includes(n.state)?"":"on"}if("cover"===o)return ai(n.state)?"covermove":"";if("valve"===o)return["open","opening"].includes(n.state)?"open":"";if("lock"===o)return["unlocked","open"].includes(n.state)?"open":"";if("binary_sensor"===o){const e=n.attributes?.device_class;if(["door","window","garage_door","opening","gas","smoke","moisture","problem"].includes(e))return"on"===n.state?"open":"";if("motion"===e){const e=this._senseRt.get(t.id);return e&&e.flashTs&&Date.now()-e.flashTs<3300?e.gen%2==0?"senseflash sf2":"senseflash":""}if(["occupancy","presence"].includes(e))return"on"===n.state?"sensehold":""}return"media_player"===o?["playing","on"].includes(n.state)?"on":"":"vacuum"===o&&["cleaning","returning"].includes(n.state)?"on":""}_liveTemp(t){if(!this._config?.show_temperature)return null;if(!0===t.marker?.use_climate_temp){const e=Uo(this.hass,t.entities);if(null!=e)return e}return"mdi:thermometer"!==t.icon&&"mdi:air-filter"!==t.icon?null:qo(this.hass,t.entities)}_bindingHasClimate(t){if(t.startsWith("entity:"))return t.slice(7).startsWith("climate.");if(t.startsWith("device:")){const e=t.slice(7);for(const[t,i]of Object.entries(this.hass?.entities||{}))if(i?.device_id===e&&t.startsWith("climate."))return!0}return!1}_bindingCoverEntity(t){if(t.startsWith("entity:"))return ri([t.slice(7)]);if(t.startsWith("device:")){const e=t.slice(7);return ri(Object.entries(this.hass?.entities||{}).filter(([,t])=>t?.device_id===e).map(([t])=>t))}return null}_bindingCoverTap(t){const e=this._bindingCoverEntity(t);if(!e)return!1;const i=String(this.hass?.states?.[e]?.attributes?.device_class||"");return!oi.has(i)}_liveHum(t){return this._config?.show_temperature&&t.primary&&Wo(this.hass,t.primary)?Bo(this.hass,t.entities):null}_openMoreInfo(t){t?Rr(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),n=s?s.split(".")[0]:null,o=(t,i)=>{e.marker?.tap_confirm?this._tapConfirm={text:t,exec:i}:i()},r=(e.marker?.controls||[]).filter(Ai);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 o(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 n=t||e||("light"===i?"toggle":"info");return"more-info"===n?"more-info":"run"===n?"run"===t?"run":"info":"cover"===n?"cover"!==t||"cover"!==i||oi.has(String(s||""))?"info":"cover":"toggle"!==n||!i||ni.has(i)?"info":"toggle"===t?"toggle":si.has(i)?"cover"===i&&oi.has(String(s||""))?"info":"toggle":"info"}(e.tapAction,void 0,n,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 n=s.attributes?.friendly_name||t;return void o(this._t("confirm.tap_run",{name:n}),()=>{this.hass.callService(i.domain,i.service,{entity_id:t}).then(()=>this._showToast(this._t("toast.run_started",{name:n}))).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 o(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?o(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 er(tr(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,n=Math.sin(i)*t.rlen/2,o=Gs([[t.rx-s,t.ry-n],[t.rx+s,t.ry+n]]);o&&e.push(o)}const t=this._decorH;for(const i of this._decorList){const s=Gs("line"===i.kind?[[i.x1*Pr,i.y1*t],[i.x2*Pr,i.y2*t]]:[[i.x*Pr,i.y*t],[(i.x+(i.w||0))*Pr,(i.y+(i.h||0))*t]]);s&&e.push(s)}}return Ys(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=Qs(this._contentItems(t));let n=s.all||tn(t),o=this._showFar?n:s.core||tn(t);return e&&e.id===t.id&&i&&e.grow&&(o=Ar(e.rect,o),n=Ar(e.all,n)),this._frame={id:t.id,model:t,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:n,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._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?V:W`
+ `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",_r),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 gr=4.166666666666667e-9;function mr(t){if(!Number.isFinite(t))return t;const e=Math.round(t/qs)*qs;return Math.abs(e-t)<=gr?t:e}const fr=(t,e,i,s)=>Math.hypot(i-t,s-e),vr=(t,e,i,s,n,o,r,a)=>Math.hypot(Math.max(Math.abs(n-t),Math.abs(n+r-(t+i))),Math.max(Math.abs(o-e),Math.abs(o+a-(e+s)))),br=(t,e,i,s,n,o,r)=>{const a=Math.max(Number(s)||0,0)/2,l=Math.PI/180,c=Math.cos(i*l)*a,h=Math.sin(i*l)*a,d=Math.cos(r*l)*a,p=Math.sin(r*l)*a,u=Math.max(fr(t+c,e+h,n+d,o+p),fr(t-c,e-h,n-d,o-p)),_=Math.max(fr(t+c,e+h,n-d,o-p),fr(t-c,e-h,n+d,o+p));return Math.min(u,_)},yr=t=>{const e=Number(t?.cell_cm);return e>0?e:5};const wr="1.59.0-beta.9",kr=1500,$r=new Map;let xr=0;const Sr=t=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(t??{})}`;let Mr=1e4;const Cr="houseplan_card_layout_v1",Dr="houseplan_card_cfg_v1",Tr="houseplan_card_zoom_v1",zr="houseplan_card_nav_v1",Pr="houseplan_card_kiosk_v1",Ar=1e3,Rr=(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}},Er=(t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)},Nr=(t,e)=>{let i,s=null;const n=(...n)=>{clearTimeout(i),s=n,i=window.setTimeout(()=>{i=void 0;const e=s;s=null,e&&t(...e)},e)};return n.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const e=s;s=null,e&&t(...e)},n.pending=()=>void 0!==i,n},Or=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}};class Ir 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._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={color:"#607d8b",width:3,fill:!1},this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorTextDialog=null,this._furnPalette=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._tool="draw",this._wallDialog=null,this._drawWallField=null,this._rszSel=null,this._rszDrag=null,this._rszUndo=[],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._opShift=!1,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._warmRevivePending=!1,this._warmGen=++xr,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._senseRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._vacVisHandler=()=>{"visible"===document.visibilityState&&(this._vacJumpOnce=!0,this._skyElev=null,this.requestUpdate())},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._space=t,this._selId=null,this._restoreZoom(),this.requestUpdate())},this._drag=null,this._rlResize=null,this._holdFired=!1,this._cfgEpoch=0,this._modelCache=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 ko(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=Nr(()=>{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(Cr,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._saveConfigDebounced=Nr(()=>{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._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:vi(this._settings,{bgColor:null})||null,northDeg:Ss(this._settings,{}),bgMode:Ms(this._settings,{}),sunRays:Cs(this._settings,{}),weatherEntity:Ds(this._settings)||"",busy:!1}},this._openAlignDialog=()=>{if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[],e=function(t,e){const i=JSON.parse(JSON.stringify(t||[])),s=JSON.parse(JSON.stringify(e||{}));let n=0,o=0,r=0,a=0,l="",c=0;const h={};let d=5;for(const t of i){const e=yr(t);null!=t?.id&&(h[String(t.id)]=e),e>d&&(d=e)}const p=(t,e,i,s=!1)=>{if(!(t>gr||s))return;n++,t>r&&(r=t);const o=t*Fs*e;o>a&&(a=o,l=i)};for(const t of i){const e=yr(t),i=null!=t?.id?String(t.id):"";for(const s of t.rooms||[]){o++;let t=0;if(s.poly?.length)s.poly=s.poly.map(e=>{const i=[mr(e[0]),mr(e[1])];return t=Math.max(t,fr(e[0],e[1],i[0],i[1])),i});else if(null!=s.x&&null!=s.y){const e=s.x,i=s.y,n=s.w||0,o=s.h||0,r=mr(e+n),a=mr(i+o),l=mr(e),c=mr(i),h=Math.max(qs,r-l),d=Math.max(qs,a-c);t=vr(e,i,n,o,l,c,h,d),s.x=l,s.y=c,s.w=h,s.h=d}p(t,e,i)}for(const s of t.decor||[]){o++;let t=0;if("line"===s.kind){const e=[mr(s.x1),mr(s.y1)],i=[mr(s.x2),mr(s.y2)];t=Math.max(fr(s.x1,s.y1,e[0],e[1]),fr(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=mr(s.x),i=mr(s.y);if(null!=s.w&&null!=s.h){const n=mr(s.x+s.w),o=mr(s.y+s.h),r=Math.max(qs,n-e),a=Math.max(qs,o-i);t=vr(s.x,s.y,s.w,s.h,e,i,r,a),s.w=r,s.h=a}else t=fr(s.x,s.y,e,i);s.x=e,s.y=i}p(t,e,i)}for(const s of t.openings||[]){o++;const n=De([s.x,s.y],t.rooms||[],.025,{step:qs,length:Number(s.length)||0});if(!n)continue;const r=Number(s.angle),a=!(Number.isFinite(r)&&r===n.angle),l=br(s.x,s.y,Number.isFinite(r)?r:n.angle,Number(s.length)||0,n.x,n.y,n.angle);s.x=n.x,s.y=n.y,s.angle=n.angle,a&&c++,p(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;o++;const n=mr(i.x),r=mr(i.y),a=fr(i.x,i.y,n,r);s[t]={...i,x:n,y:r};const l="string"==typeof i.s?i.s:"";p(a,h[l]??d,l)}return{spaces:i,layout:s,report:{moved:n,total:o,maxShift:r,maxShiftCm:a,maxSpace:l,rotated:c},changed:n>0}}(t,this._layout||{}),i=Math.ceil(10*e.report.maxShiftCm)/10,s=t.find(t=>null!=t?.id&&String(t.id)===e.report.maxSpace),n=t.length>1&&s?String(s.title||s.id):"";this._alignDialog={report:e.report,spaces:e.spaces,layout:e.layout,cm:i,where:n,busy:!1}},this._openRulesDialog=()=>{if(!this._norm)return;const t=this._settings.icon_rules,e=(t&&t.length?t:dt).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)}_slideTo(t,e){if(t===this._space)return;const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;this._space=t,this._selId=null,this._restoreZoom(),i||(this._slide=e,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slide="",this.requestUpdate()},260),this.requestUpdate())}_cycleTick(){if(this._kiosk&&Number(this._config?.cycle)>0&&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,()=>Fi(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},kr)),!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))}disconnectedCallback(){document.removeEventListener("visibilitychange",this._vacVisHandler),this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._skySnapRaf&&(cancelAnimationFrame(this._skySnapRaf),this._skySnapRaf=0);for(const t of this._senseRt.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._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),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),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._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._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0)}if("decor"===this._mode)return"Delete"!==t.key&&"Backspace"!==t.key||!this._decorSel||t.target?.closest?.("input, textarea, select")?void("Escape"===t.key&&(t.preventDefault(),this._decorDraft?this._decorDraft=null: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)return;const e="Escape"===t.key||(t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase();if(e){if(this._roomDialog)return t.preventDefault(),void this._roomDialogCancel();if("draw"===this._tool&&this._path.length)return t.preventDefault(),void this._undoPoint();if(e)return"resize"===this._tool?(t.preventDefault(),this._rszDrag?void this._rszCancelDrag():(t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase()?void this._rszUndoPop():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?(t.preventDefault(),void(this._openWallAnchor?this._openWallAnchor=null:this._tool="draw")):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool||(t.preventDefault(),this._tool="draw"))}}_undoPoint(){this._path.length&&(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 $r.values())for(const e of t)clearTimeout(e.evict);$r.clear(),Mr=t&&t>0?t:1e4}static _warmBootStats(){let t=0,e=0;const i=[];for(const s of $r.values())for(const n of s)t++,n.dlg&&(e++,i.push(n.dlg.kind));return{keys:$r.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(Tr)||"{}")||{}}catch{this._zoomBySpace={}}try{const t=JSON.parse(localStorage.getItem(Pr)||"null");this._kioskScale={icon:Hi(t?.icon),font:Hi(t?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(Dr)||"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()}_warmAdopt(){if(this._config?.kiosk)return;const t=Sr(this._config);if(this._warmKey===t&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const e=this.parentNode,i=this._warmIdx(e),s=$r.get(t);if(!s||!s.length)return;const n=s.find(t=>t.owner===this._warmGen);if(n)return clearTimeout(n.evict),n.evict=0,n.freed=0,n.live=!0,this._warmSlot=n,void(this._warmKey=t);const{slot:o,sure:r}=((t,e,i,s)=>{const n=t=>{const e=!!i&&t.place?.deref()===i;return e&&t.idx===s?4:t.live?0:e?3:2};let o=null,r=0,a=0,l=null;for(const i of t){if(i.owner===e)continue;l=i;const t=n(i);t<=0||(t>r?(o=i,r=t,a=1):t===r&&a++)}return!o||a>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(s,this._warmGen,e,i);o&&(this._booting=!1,this._bootFading=!1,this._hdrH=o.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},kr)),this._warmKey=t,r?(clearTimeout(o.evict),o.evict=0,o.owner=this._warmGen,o.place=e?new WeakRef(e):null,o.idx=i,o.live=!0,this._warmSlot=o,this._warmVp=o.vp,this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,place:e?new WeakRef(e):null,idx:i,live:!0,hdrH:o.hdrH,stageH:o.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 n=$r.get(e);if(!t.live&&n&&n.length>1){const e=n.indexOf(t);e>=0&&n.splice(e,1)}},Mr+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=Sr(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=$r.get(i)||[];for(s.push(this._warmSlot),$r.set(i,s),this._warmTrim(s);$r.size>8;){const t=$r.keys().next().value;if(void 0===t||t===i)break;for(const e of $r.get(t)||[])clearTimeout(e.evict);$r.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._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._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>Mr)&&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"decorText":this._decorTextDialog={...e.data};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(Dr,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.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 Ns(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:n,excluded:o,iconRules:r}=t,a=!1!==n.group_lights,l=Vo(e,a),c=new Set(l.map(t=>t.area)),h=Oo(e),d=new Set(s.map(t=>t.binding)),p=[];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(d.has("device:"+t.id))continue;const n=h[t.id]||[],l=Io(e,t,n);let u=o.has(l)||"Group"===t.model||/scene/i.test(t.model||"")||/bridge/i.test((t.model||"")+(t.name||""))||"myheat"===l&&!!t.via_device_id;!u&&a&&c.has(s)&&"mdi:lightbulb"===Go(e,(t.name_by_user||t.name||"").trim(),t.model,n,r)&&(u=!0),u&&p.push("device:"+t.id)}return p}({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=pt(t)),this._rulesCompiled}get _fillColors(){return wi(this._settings)}get _excluded(){const t=this._settings.exclude_integrations;return t?new Set(t):ht}willUpdate(t){t?.has?.("hass")&&(this._vacTick(),this._senseTick()),this._skyPlan(),t.has("hass")&&this.hass&&(this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices())}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),n=Math.round(i+s);n>=0&&Math.abs(n-this._hdrH)>1&&(this._hdrH=n),n>=0&&!this._booting&&!this._config?.kiosk&&t.clientHeight>0&&this._warmPatch({hdrH:n,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._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null);const i=t?.config;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=>{(t?.data?.rev??-1)!==this._cfgRev&&this._reloadConfigOnly()},"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(),n=this._savedNav();!this._hashApplied&&s&&this._model.find(t=>t.id===s)?(this._space=s,this._hashApplied=!0):n?.space&&!this._navApplied&&!this._hashApplied&&this._model.find(t=>t.id===n.space)?(this._space=n.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(Cr)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1}this._regSignature="",this.requestUpdate()}async _reloadConfigOnly(t=!1){if(!t&&(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting))return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(),400));try{const t=await this.hass.callWS({type:"houseplan/config/get"}),e=t?.config;this._serverCfg=e&&Array.isArray(e.spaces)?e:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,"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,Fi(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._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")+":"+er(t,this._config?.language);e===this._regSignature&&this._devices.length||(this._regSignature=e,this._devices=Yo({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())}_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:n}=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:n,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=Ho(this.hass,i.entities);null!=s&&e.push(s)}return Ye(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),n=Math.min(...i);return{x:s,y:n,w:Math.max(...e)-s,h:Math.max(...i)-n}}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*sn(i)*1.3;for(const e of i.rooms){if(!e.area)continue;const n=this._devices.filter(t=>t.area===e.area&&t.space===i.id);if(!n.length)continue;const o=this._roomBounds(e),r=.1*Math.min(o.w,o.h),a=o.w-2*r,l=o.h-2*r,c=Math.max(1,Math.round(Math.sqrt(n.length*a/Math.max(l,1)))),h=Math.ceil(n.length/c),d=a/c,p=l/Math.max(h,1),u=n.map((t,e)=>({x:o.x+r+d*(e%c+.5),y:o.y+r+p*(Math.floor(e/c)+.5)}));Ze(u,o,s,.5*r),n.forEach((e,i)=>t[e.id]=Us(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*Ar,y:i.y*Ar}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[t.id]?this._defPos[t.id]:Us(en(e))}_savePos(t,e,i,s=!1){if(this._norm){const n=this._gridPitch,o=s?e:Math.round(e/n)*n,r=s?i:Math.round(i/n)*n,a=this._layout[t.id]?.k;this._layout={...this._layout,[t.id]:{s:t.space,x:Bs(o/Ar),y:Bs(r/Ar),...a?{k:a}:{}}}}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?ri(t.entities):null}_actEntity(t){return this._coverIndicator(t)||t.primary}_stateClass(t){if(!this._config?.live_states)return"";const e=this._coverIndicator(t);if(e){const t=this.hass.states[e];return t?"unavailable"===t.state?"unavail":ai(t.state)?"covermove":"":""}const i=(t.marker?.controls||[]).filter(Ai);if(i.length)return i.some(t=>"on"===this.hass.states[t]?.state)?"on":"";if(qo(this.hass,t))return"on";const s=this._actEntity(t),n=s?this.hass.states[s]:void 0;if(!n)return"";if("unavailable"===n.state)return"unavail";const o=s.split(".")[0];if(["light","switch","fan","humidifier"].includes(o))return"on"===n.state?"on":"";if("climate"===o){const t=n.attributes?.hvac_action;return null!=t?["heating","cooling","drying","fan"].includes(t)?"on":"":["off","unknown"].includes(n.state)?"":"on"}if("cover"===o)return ai(n.state)?"covermove":"";if("valve"===o)return["open","opening"].includes(n.state)?"open":"";if("lock"===o)return["unlocked","open"].includes(n.state)?"open":"";if("binary_sensor"===o){const e=n.attributes?.device_class;if(["door","window","garage_door","opening","gas","smoke","moisture","problem"].includes(e))return"on"===n.state?"open":"";if("motion"===e){const e=this._senseRt.get(t.id);return e&&e.flashTs&&Date.now()-e.flashTs<3300?e.gen%2==0?"senseflash sf2":"senseflash":""}if(["occupancy","presence"].includes(e))return"on"===n.state?"sensehold":""}return"media_player"===o?["playing","on"].includes(n.state)?"on":"":"vacuum"===o&&["cleaning","returning"].includes(n.state)?"on":""}_liveTemp(t){if(!this._config?.show_temperature)return null;if(!0===t.marker?.use_climate_temp){const e=Wo(this.hass,t.entities);if(null!=e)return e}return"mdi:thermometer"!==t.icon&&"mdi:air-filter"!==t.icon?null:Uo(this.hass,t.entities)}_bindingHasClimate(t){if(t.startsWith("entity:"))return t.slice(7).startsWith("climate.");if(t.startsWith("device:")){const e=t.slice(7);for(const[t,i]of Object.entries(this.hass?.entities||{}))if(i?.device_id===e&&t.startsWith("climate."))return!0}return!1}_bindingCoverEntity(t){if(t.startsWith("entity:"))return ri([t.slice(7)]);if(t.startsWith("device:")){const e=t.slice(7);return ri(Object.entries(this.hass?.entities||{}).filter(([,t])=>t?.device_id===e).map(([t])=>t))}return null}_bindingCoverTap(t){const e=this._bindingCoverEntity(t);if(!e)return!1;const i=String(this.hass?.states?.[e]?.attributes?.device_class||"");return!oi.has(i)}_liveHum(t){return this._config?.show_temperature&&t.primary&&Bo(this.hass,t.primary)?jo(this.hass,t.entities):null}_openMoreInfo(t){t?Er(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),n=s?s.split(".")[0]:null,o=(t,i)=>{e.marker?.tap_confirm?this._tapConfirm={text:t,exec:i}:i()},r=(e.marker?.controls||[]).filter(Ai);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 o(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 n=t||e||("light"===i?"toggle":"info");return"more-info"===n?"more-info":"run"===n?"run"===t?"run":"info":"cover"===n?"cover"!==t||"cover"!==i||oi.has(String(s||""))?"info":"cover":"toggle"!==n||!i||ni.has(i)?"info":"toggle"===t?"toggle":si.has(i)?"cover"===i&&oi.has(String(s||""))?"info":"toggle":"info"}(e.tapAction,void 0,n,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 n=s.attributes?.friendly_name||t;return void o(this._t("confirm.tap_run",{name:n}),()=>{this.hass.callService(i.domain,i.service,{entity_id:t}).then(()=>this._showToast(this._t("toast.run_started",{name:n}))).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 o(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?o(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 ir(er(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,n=Math.sin(i)*t.rlen/2,o=Gs([[t.rx-s,t.ry-n],[t.rx+s,t.ry+n]]);o&&e.push(o)}const t=this._decorH;for(const i of this._decorList){const s=Gs("line"===i.kind?[[i.x1*Ar,i.y1*t],[i.x2*Ar,i.y2*t]]:[[i.x*Ar,i.y*t],[(i.x+(i.w||0))*Ar,(i.y+(i.h||0))*t]]);s&&e.push(s)}}return Ys(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=Qs(this._contentItems(t));let n=s.all||tn(t),o=this._showFar?n:s.core||tn(t);return e&&e.id===t.id&&i&&e.grow&&(o=Rr(e.rect,o),n=Rr(e.all,n)),this._frame={id:t.id,model:t,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:n,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._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?V:W`
${this._t("canvas.far_objects",{n:this._outliers})} @@ -2635,7 +2635,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow style="left:${s.toFixed(1)}%;top:${n.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:Xe(t,this._stageAspect())}_screenToVb(t,e){const i=this._stageEl,s=this._viewOr(this._baseVb()),n=i?.clientWidth||1,o=i?.clientHeight||1;return[s.x+t/n*s.w,s.y+e/o*s.h]}_clampView(t,e){const i=(t,e,i,s)=>{const n=1*Math.max(e,s),o=i-n,r=i+s-e+n;return Math.max(Math.min(o,r),Math.min(Math.max(o,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(),n=Xe(s,this._stageAspect()),o=Math.min(Or.ZOOM_MAX,Math.max(Or.ZOOM_MIN,t)),r=n.w/o,a=n.h/o,l=this._viewOr(s),c=e??l.x+l.w/2,h=i??l.y+l.h/2;this._zoom=o,this._view=this._clampView({x:c-r/2,y:h-a/2,w:r,h:a},n)}_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},wr)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_refitView(){if(!this._stageEl)return;const t=this._view;this._applyView(this._zoom,t?t.x+t.w/2:void 0,t?t.y+t.h/2:void 0),this.requestUpdate()}_zoomAt(t,e,i){const s=this._stageEl;if(!s)return;const n=Xe(this._baseVb(),this._stageAspect()),o=Math.min(Or.ZOOM_MAX,Math.max(Or.ZOOM_MIN,i)),r=s.clientWidth,a=s.clientHeight,l=this._screenToVb(t,e),c=n.w/o,h=n.h/o;this._zoom=o,this._view=this._clampView({x:l[0]-t/r*c,y:l[1]-e/a*h,w:c,h:h},n)}_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=Xe(t,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(Dr,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._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId)if(this._decorDraft?.pid!==t.pointerId)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,n=(t[0].y+t[1].y)/2-i.top;this._zoomAt(s,n,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(),n=this._viewOr(t),o=Xe(t,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-e/(s.clientWidth||1)*n.w,y:this._panStart.vy-i/(s.clientHeight||1)*n.h,w:n.w,h:n.h},o)}}}else this._markupMove(t);else this._decorMoveUpdate(t);else this._decorDraft={...this._decorDraft,b:this._snap(this._svgPoint(t),t)};else this._bdMove(t);else this._dtMove(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 n="pan"===this._panLock?null:function(t,e,i,s,n,o=60){if(i>1.001||s.length<2)return null;if(Math.abs(t)t.id),this._space);n&&(this._slideTo(n,i<0?"left":"right"),this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}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._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()}_clickRoom(t){var e;!this._suppressClick&&t.area&&(e="/config/areas/area/"+t.area,history.pushState(null,"",e),Rr(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},Nr(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(),n=i.getBoundingClientRect(),o=this._viewOr(s),r=(t.clientX-this._drag.sx)/n.width*o.w,a=(t.clientY-this._drag.sy)/n.height*o.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=Ws(this._drag.ox+r),c=Ws(this._drag.oy+a);this._savePos(e,l,c,t.shiftKey)}_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 Or._noHoverMq||Or._touchSeen}_notePointer(t){"touch"!==t.pointerType&&"pen"!==t.pointerType||(Or._touchSeen=!0,this._tip&&(this._tip=null))}_showTip(t,e,i,s,n){this._noHover||this._drag||(this._tip={x:t.clientX,y:t.clientY,title:e,meta:i,lqi:s,temp:n})}get _gridPitch(){return Ls}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 $e(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,Pr}get _segments(){const t=this._curSpaceCfg,e=this._spaceH;return Ce(t?.rooms||[]).map(t=>[t[0]*Pr,t[1]*e,t[2]*Pr,t[3]*e])}_savedNav(){try{return JSON.parse(localStorage.getItem(Tr)||"null")}catch{return null}}_saveNav(){try{localStorage.setItem(Tr,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._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(this._mode=t,e&&(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._rszSel=null,this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._rszUndo=[],this._tip=null,this._decorDraft=null,this._decorSel=null,this._decorTool="decor"===t&&this._curSpaceCfg?.plan_url?"backdrop":"select",this._bdDrag=null,this._dtDrag=null,this._dtBox=null,"plan"===t&&this._primeDrawWallField(),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=kn(15,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?kn(15,this._imperial):this._drawWallField}get _drawWallCm(){return $n(this._drawWallFieldValue,this._imperial)}_svgPoint(t){const e=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t,e=!1){const i="boolean"==typeof e?e:!!e?.shiftKey,s=this._gridPitch;return i?[Ws(t[0]),Ws(t[1])]:[Ws(ke(t[0],s)),Ws(ke(t[1],s))]}_samePt(t,e){return Pe(t,e)}_dropLegacySegments(){for(const t of this._serverCfg?.spaces||[])if(delete t.segments,Array.isArray(t.walls)){const e=lo(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=En(t.walls,t.rooms||[],Hs,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()}_roomAt(t){return this._spaceModel().rooms.find(e=>{const i=Se(e);return!!i&&Oe(t,i)})}_overlapRoom(t){return this._spaceModel().rooms.find(e=>{const i=Se(e);return!!i&&We(t,i)})}_pointInRoom(t,e){return e.poly?Ae(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}_markupClick(t){if(this._vacFit)return;if(!this._markup)return;if(this._suppressClick)return;if(this._drag||this._rlResize)return;const e=t.composedPath?.()||[];if(e.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;const i=this._svgPoint(t);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._deleteWallClick(i);if("opening"===this._tool)return void this._openingClick(i,t.shiftKey);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("split"===this._tool)return void this._splitClick(i,t.shiftKey);const s=this._snap(i,t),n=this._path.length>=3&&this._samePt(s,this._path[0]);if(!this._path.length)return void(this._path=[s]);const o=this._path[this._path.length-1];if(!this._samePt(s,o)){if(n){const t=this._overlapRoom(this._path);return t?void this._showToast(this._t("toast.room_overlap",{name:t.name||""})):(this._path=[...this._path,s],this._cursorPt=null,this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),void(this._roomDialog=!0))}this._path=[...this._path,s]}}_rszRooms(){const t=[];for(const e of this._spaceModel().rooms){const i=e.id?Se(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(){const t=this._curSpaceCfg;return JSON.stringify({rooms:t?.rooms||[],openings:t?.openings||[],walls:t?.walls||[],open_spans:t?.open_spans||[]})}_rszRestore(t){const e=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!e)return;const i=JSON.parse(t);e.rooms=i.rooms,e.openings=i.openings,Array.isArray(i.walls)&&i.walls.length?e.walls=i.walls:delete e.walls,Array.isArray(i.open_spans)&&i.open_spans.length?e.open_spans=i.open_spans:delete e.open_spans,this._cfgEpoch++,this.requestUpdate()}_rszApplyPreview(t,e){const i=this._rszDrag,s=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!i||!s)return;const n=JSON.parse(i.snap),o={...s,rooms:n.rooms,openings:n.openings,walls:n.walls},r=this._spaceH;for(const[e,i]of Object.entries(t)){const t=o.rooms.find(t=>t.id===e);t&&(t.poly=i.map(t=>[t[0]/Pr,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=(o.openings||[]).find(e=>e.id===t);e&&(e.x=i[0]/Pr,e.y=i[1]/r)}this._rszPreview={space:this._space,sp:o},this._cfgEpoch++}_rszEdgeDown(t,e,i){if("resize"!==this._tool||this._rszDrag)return;t.stopPropagation(),t.preventDefault(),Nr(t);const s=this._rszRooms(),n=function(t,e,i){const s=t.find(t=>t.id===e);if(!s||!s.poly||s.poly.length<3)return null;if(i<0||i>=s.poly.length)return null;const n=[...s.poly[i]],o=[...s.poly[(i+1)%s.poly.length]];return{roomId:e,edge:i,a:n,b:o,n:Ji(s.poly,i)}}(s,e,i);n&&(this._rszDrag={kind:"edge",pid:t.pointerId,roomId:e,plan:n,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(),Nr(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 s=e.plan,n=(i[0]-s.a[0])*s.n[0]+(i[1]-s.a[1])*s.n[1],o=this._snap([s.a[0]+s.n[0]*n,s.a[1]+s.n[1]*n],t);let r=(o[0]-s.a[0])*s.n[0]+(o[1]-s.a[1])*s.n[1];if(r=function(t,e,i,s,n,o){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(n,1e-6);for(let s=0;s<4096&&a>1e-9;s++,a-=l){const s=r*a;if(cs(t,e,i,s,o))return s}return 0}(e.rooms,e.openings,s,r,this._gridPitch,this._rszOpts()),r===e.d&&e.moved)return;e.d=r,e.moved=!0;const a=rs(e.rooms,e.openings,s,r,this._rszOpts().eps);e.changed=Object.keys(a.polys),this._rszApplyPreview(a.polys,a.openings),this._rszLive=this._rszEdgeLabels(a,s)}else{const s=e.fixed,n=this._snap(i,t);let o=Math.hypot(n[0]-s[0],n[1]-s[1])/(e.span0||1);if(o=Math.max(.05,Math.min(20,o)),o=function(t,e,i,s,n,o){if(!Number.isFinite(n)||n<=0)return 1;if(ds(t,e,i,s,n,o))return n;let r=1,a=n;for(let n=0;n<28;n++){const n=(r+a)/2;ds(t,e,i,s,n,o)?r=n:a=n}return r}(e.rooms,e.openings,e.roomId,s,o,this._rszOpts()),o===e.k&&e.moved)return;e.k=o,e.moved=!0;const r=e.rooms.find(t=>t.id===e.roomId),a=e.rooms.filter(t=>t.id!==e.roomId).map(t=>t.poly),l=hs(r,e.openings,a,s,o,2*this._rszOpts().eps);e.changed=[e.roomId],this._rszApplyPreview({[e.roomId]:l.poly},l.openings),this._rszLive=this._rszScaleLabels(l.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=this._curSpaceCfg;if(s){s.rooms=i.sp.rooms,s.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?s.walls=i.sp.walls:delete s.walls);for(const t of e.changed){const e=s.rooms.find(e=>e.id===t);e?.poly&&(e.poly=is(e.poly,1e-9))}const t=this._spaceH,n=[],o=[];for(const i of e.changed){const r=e.rooms.find(t=>t.id===i),a=s.rooms.find(t=>t.id===i);if(!r||!a?.poly)continue;const l=a.poly.map(e=>[e[0]*Pr,e[1]*t]);if(r.poly.length===l.length)for(let t=0;t30&&this._rszUndo.shift(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),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())}_rszUndoPop(){for(let t=this._rszUndo.length-1;t>=0;t--){if(this._rszUndo[t].space!==this._space)continue;const[e]=this._rszUndo.splice(t,1);return this._rszRestore(e.snap),void this._saveConfig()}}_rszEdgeLabels(t,e){const i=this._rszDrag,s=[],n=t.polys[e.roomId]||i.rooms.find(t=>t.id===e.roomId).poly,o=n.length,r=e.edge,a=(r+1)%o;for(const[t,e]of[[n[(r-1+o)%o],n[r]],[n[r],n[a]],[n[a],n[(a+1)%o]]])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,c=Object.keys(t.polys).length?Object.keys(t.polys):[e.roomId],h=this._spaceWalls,d=this._openPairs().flatMap(t=>t.segs);for(const e of c){const n=t.polys[e]||i.rooms.find(t=>t.id===e).poly,o=h.length&&Qn(Object.entries(t.polys).map(([t,e])=>({id:t,poly:e})),e,h,d,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)||n,r=Le(o);s.push({x:r[0],y:r[1],text:us(ps(o,this._gridPitch,this._cellCm),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]),n=Math.max(...i)-Math.min(...i),o=Math.max(...s)-Math.min(...s),r=this._spaceWalls,a=r.length&&this._rszSel&&Qn([{id:this._rszSel,poly:t}],this._rszSel,r,[],this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)||t,l=Le(a);return[{x:Math.min(...i),y:Math.min(...s),text:`${this._fmtLen([0,0],[n,0])} × ${this._fmtLen([0,0],[o,0])}`},{x:l[0],y:l[1],text:us(ps(a,this._gridPitch,this._cellCm),e),area:!0}]}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),i=e/2,s=t=>t.toFixed(1),n=`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)}`,o=[],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:Xe(t,this._stageAspect())}_screenToVb(t,e){const i=this._stageEl,s=this._viewOr(this._baseVb()),n=i?.clientWidth||1,o=i?.clientHeight||1;return[s.x+t/n*s.w,s.y+e/o*s.h]}_clampView(t,e){const i=(t,e,i,s)=>{const n=1*Math.max(e,s),o=i-n,r=i+s-e+n;return Math.max(Math.min(o,r),Math.min(Math.max(o,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(),n=Xe(s,this._stageAspect()),o=Math.min(Ir.ZOOM_MAX,Math.max(Ir.ZOOM_MIN,t)),r=n.w/o,a=n.h/o,l=this._viewOr(s),c=e??l.x+l.w/2,h=i??l.y+l.h/2;this._zoom=o,this._view=this._clampView({x:c-r/2,y:h-a/2,w:r,h:a},n)}_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},kr)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_refitView(){if(!this._stageEl)return;const t=this._view;this._applyView(this._zoom,t?t.x+t.w/2:void 0,t?t.y+t.h/2:void 0),this.requestUpdate()}_zoomAt(t,e,i){const s=this._stageEl;if(!s)return;const n=Xe(this._baseVb(),this._stageAspect()),o=Math.min(Ir.ZOOM_MAX,Math.max(Ir.ZOOM_MIN,i)),r=s.clientWidth,a=s.clientHeight,l=this._screenToVb(t,e),c=n.w/o,h=n.h/o;this._zoom=o,this._view=this._clampView({x:l[0]-t/r*c,y:l[1]-e/a*h,w:c,h:h},n)}_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=Xe(t,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(Tr,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._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId)if(this._decorDraft?.pid!==t.pointerId)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,n=(t[0].y+t[1].y)/2-i.top;this._zoomAt(s,n,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(),n=this._viewOr(t),o=Xe(t,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-e/(s.clientWidth||1)*n.w,y:this._panStart.vy-i/(s.clientHeight||1)*n.h,w:n.w,h:n.h},o)}}}else this._markupMove(t);else this._decorMoveUpdate(t);else this._decorDraft={...this._decorDraft,b:this._snap(this._svgPoint(t),t)};else this._bdMove(t);else this._dtMove(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 n="pan"===this._panLock?null:function(t,e,i,s,n,o=60){if(i>1.001||s.length<2)return null;if(Math.abs(t)t.id),this._space);n&&(this._slideTo(n,i<0?"left":"right"),this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}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._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()}_clickRoom(t){var e;!this._suppressClick&&t.area&&(e="/config/areas/area/"+t.area,history.pushState(null,"",e),Er(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},Or(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(),n=i.getBoundingClientRect(),o=this._viewOr(s),r=(t.clientX-this._drag.sx)/n.width*o.w,a=(t.clientY-this._drag.sy)/n.height*o.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=Ws(this._drag.ox+r),c=Ws(this._drag.oy+a);this._savePos(e,l,c,t.shiftKey)}_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 Ir._noHoverMq||Ir._touchSeen}_notePointer(t){"touch"!==t.pointerType&&"pen"!==t.pointerType||(Ir._touchSeen=!0,this._tip&&(this._tip=null))}_showTip(t,e,i,s,n){this._noHover||this._drag||(this._tip={x:t.clientX,y:t.clientY,title:e,meta:i,lqi:s,temp:n})}get _gridPitch(){return Ls}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 $e(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,Ar}get _segments(){const t=this._curSpaceCfg,e=this._spaceH;return Ce(t?.rooms||[]).map(t=>[t[0]*Ar,t[1]*e,t[2]*Ar,t[3]*e])}_savedNav(){try{return JSON.parse(localStorage.getItem(zr)||"null")}catch{return null}}_saveNav(){try{localStorage.setItem(zr,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._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(this._mode=t,e&&(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._rszSel=null,this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._rszUndo=[],this._tip=null,this._decorDraft=null,this._decorSel=null,this._decorTool="decor"===t&&this._curSpaceCfg?.plan_url?"backdrop":"select",this._bdDrag=null,this._dtDrag=null,this._dtBox=null,"plan"===t&&this._primeDrawWallField(),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=kn(15,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?kn(15,this._imperial):this._drawWallField}get _drawWallCm(){return $n(this._drawWallFieldValue,this._imperial)}_svgPoint(t){const e=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t,e=!1){const i="boolean"==typeof e?e:!!e?.shiftKey,s=this._gridPitch;return i?[Ws(t[0]),Ws(t[1])]:[Ws(ke(t[0],s)),Ws(ke(t[1],s))]}_samePt(t,e){return Pe(t,e)}_dropLegacySegments(){for(const t of this._serverCfg?.spaces||[])if(delete t.segments,Array.isArray(t.walls)){const e=lo(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=En(t.walls,t.rooms||[],qs,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()}_roomAt(t){return this._spaceModel().rooms.find(e=>{const i=Se(e);return!!i&&Oe(t,i)})}_overlapRoom(t){return this._spaceModel().rooms.find(e=>{const i=Se(e);return!!i&&We(t,i)})}_pointInRoom(t,e){return e.poly?Ae(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}_markupClick(t){if(this._vacFit)return;if(!this._markup)return;if(this._suppressClick)return;if(this._drag||this._rlResize)return;const e=t.composedPath?.()||[];if(e.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;const i=this._svgPoint(t);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._deleteWallClick(i);if("opening"===this._tool)return void this._openingClick(i,t.shiftKey);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("split"===this._tool)return void this._splitClick(i,t.shiftKey);const s=this._snap(i,t),n=this._path.length>=3&&this._samePt(s,this._path[0]);if(!this._path.length)return void(this._path=[s]);const o=this._path[this._path.length-1];if(!this._samePt(s,o)){if(n){const t=this._overlapRoom(this._path);return t?void this._showToast(this._t("toast.room_overlap",{name:t.name||""})):(this._path=[...this._path,s],this._cursorPt=null,this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),void(this._roomDialog=!0))}this._path=[...this._path,s]}}_rszRooms(){const t=[];for(const e of this._spaceModel().rooms){const i=e.id?Se(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(){const t=this._curSpaceCfg;return JSON.stringify({rooms:t?.rooms||[],openings:t?.openings||[],walls:t?.walls||[],open_spans:t?.open_spans||[]})}_rszRestore(t){const e=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!e)return;const i=JSON.parse(t);e.rooms=i.rooms,e.openings=i.openings,Array.isArray(i.walls)&&i.walls.length?e.walls=i.walls:delete e.walls,Array.isArray(i.open_spans)&&i.open_spans.length?e.open_spans=i.open_spans:delete e.open_spans,this._cfgEpoch++,this.requestUpdate()}_rszApplyPreview(t,e){const i=this._rszDrag,s=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!i||!s)return;const n=JSON.parse(i.snap),o={...s,rooms:n.rooms,openings:n.openings,walls:n.walls,open_spans:n.open_spans};Array.isArray(n.open_spans)&&n.open_spans.length||delete o.open_spans;const r=this._spaceH;for(const[e,i]of Object.entries(t)){const t=o.rooms.find(t=>t.id===e);t&&(t.poly=i.map(t=>[t[0]/Ar,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=(o.openings||[]).find(e=>e.id===t);e&&(e.x=i[0]/Ar,e.y=i[1]/r)}const a=[],l=[];for(const t of i.changed){const e=i.rooms.find(e=>e.id===t),s=o.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const n=s.poly.map(t=>[t[0]*Ar,t[1]*r]);if(e.poly.length===n.length)for(let t=0;t0?n:1,a=Math.max(.5*s,1e-9)*r,l=new Set,c=[];for(const n of t){let t=o.get(n.key)||"";if(!t){const o=Tn([n],r)[0];if(o)for(let n=0;n1.000001)continue;if(Jn(o.x,o.y,l[0],l[1],c[0],c[1])>a)continue;const m=h[0]+(d[0]-h[0])*Math.max(0,Math.min(1,g)),f=h[1]+(d[1]-h[1])*Math.max(0,Math.min(1,g)),[v,b]=Mn(h,d),y=Math.max(s*r,1e-6);t=Dn([m-v*y,f-b*y],[m+v*y,f+b*y],s,r);break}}t||(t=n.key),l.has(t)||(l.add(t),c.push({key:t,cm:n.cm}))}return c}(o.walls,a,l,this._wallKeyPitch,Ar))}this._rszPreview={space:this._space,sp:o},this._cfgEpoch++}_rszEdgeDown(t,e,i){if("resize"!==this._tool||this._rszDrag)return;t.stopPropagation(),t.preventDefault(),Or(t);const s=this._rszRooms(),n=function(t,e,i){const s=t.find(t=>t.id===e);if(!s||!s.poly||s.poly.length<3)return null;if(i<0||i>=s.poly.length)return null;const n=[...s.poly[i]],o=[...s.poly[(i+1)%s.poly.length]];return{roomId:e,edge:i,a:n,b:o,n:Ji(s.poly,i)}}(s,e,i);n&&(this._rszDrag={kind:"edge",pid:t.pointerId,roomId:e,plan:n,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(),Or(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 s=e.plan,n=(i[0]-s.a[0])*s.n[0]+(i[1]-s.a[1])*s.n[1],o=this._snap([s.a[0]+s.n[0]*n,s.a[1]+s.n[1]*n],t);let r=(o[0]-s.a[0])*s.n[0]+(o[1]-s.a[1])*s.n[1];if(r=function(t,e,i,s,n,o){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(n,1e-6);for(let s=0;s<4096&&a>1e-9;s++,a-=l){const s=r*a;if(cs(t,e,i,s,o))return s}return 0}(e.rooms,e.openings,s,r,this._gridPitch,this._rszOpts()),r===e.d&&e.moved)return;e.d=r,e.moved=!0;const a=rs(e.rooms,e.openings,s,r,this._rszOpts().eps);e.changed=Object.keys(a.polys),this._rszApplyPreview(a.polys,a.openings),this._rszLive=this._rszEdgeLabels(a,s)}else{const s=e.fixed,n=this._snap(i,t);let o=Math.hypot(n[0]-s[0],n[1]-s[1])/(e.span0||1);if(o=Math.max(.05,Math.min(20,o)),o=function(t,e,i,s,n,o){if(!Number.isFinite(n)||n<=0)return 1;if(ds(t,e,i,s,n,o))return n;let r=1,a=n;for(let n=0;n<28;n++){const n=(r+a)/2;ds(t,e,i,s,n,o)?r=n:a=n}return r}(e.rooms,e.openings,e.roomId,s,o,this._rszOpts()),o===e.k&&e.moved)return;e.k=o,e.moved=!0;const r=e.rooms.find(t=>t.id===e.roomId),a=e.rooms.filter(t=>t.id!==e.roomId).map(t=>t.poly),l=hs(r,e.openings,a,s,o,2*this._rszOpts().eps);e.changed=[e.roomId],this._rszApplyPreview({[e.roomId]:l.poly},l.openings),this._rszLive=this._rszScaleLabels(l.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=this._curSpaceCfg;if(s){s.rooms=i.sp.rooms,s.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?s.walls=i.sp.walls:delete s.walls),Array.isArray(i.sp.open_spans)&&i.sp.open_spans.length?s.open_spans=i.sp.open_spans:delete s.open_spans;for(const t of e.changed){const e=s.rooms.find(e=>e.id===t);e?.poly&&(e.poly=is(e.poly,1e-9))}this._commitOpenSpans(),Array.isArray(s.walls)&&s.walls.length&&(s.walls=En(s.walls,s.rooms||[],qs,1,this._cfgOpenCuts()),s.walls.length||delete s.walls)}this._rszUndo.push({space:this._space,snap:e.snap}),this._rszUndo.length>30&&this._rszUndo.shift(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),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())}_rszUndoPop(){for(let t=this._rszUndo.length-1;t>=0;t--){if(this._rszUndo[t].space!==this._space)continue;const[e]=this._rszUndo.splice(t,1);return this._rszRestore(e.snap),void this._saveConfig()}}_rszEdgeLabels(t,e){const i=this._rszDrag,s=[],n=t.polys[e.roomId]||i.rooms.find(t=>t.id===e.roomId).poly,o=n.length,r=e.edge,a=(r+1)%o;for(const[t,e]of[[n[(r-1+o)%o],n[r]],[n[r],n[a]],[n[a],n[(a+1)%o]]])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,c=Object.keys(t.polys).length?Object.keys(t.polys):[e.roomId],h=this._spaceWalls,d=this._openPairs().flatMap(t=>t.segs);for(const e of c){const n=t.polys[e]||i.rooms.find(t=>t.id===e).poly,o=h.length&&Qn(Object.entries(t.polys).map(([t,e])=>({id:t,poly:e})),e,h,d,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)||n,r=Le(o);s.push({x:r[0],y:r[1],text:us(ps(o,this._gridPitch,this._cellCm),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]),n=Math.max(...i)-Math.min(...i),o=Math.max(...s)-Math.min(...s),r=this._spaceWalls,a=r.length&&this._rszSel&&Qn([{id:this._rszSel,poly:t}],this._rszSel,r,[],this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)||t,l=Le(a);return[{x:Math.min(...i),y:Math.min(...s),text:`${this._fmtLen([0,0],[n,0])} × ${this._fmtLen([0,0],[o,0])}`},{x:l[0],y:l[1],text:us(ps(a,this._gridPitch,this._cellCm),e),area:!0}]}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),i=e/2,s=t=>t.toFixed(1),n=`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)}`,o=[],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)} @@ -2645,7 +2645,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)}>`),o.push(B``)}return B`${o}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH;return(t?.openings||[]).map(t=>({...t,rx:t.x*Pr,ry:t.y*e,rlen:t.length*Pr}))}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return Pr}_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._snap(this._svgPoint(t),t);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},Nr(t),!0}if("text"===e){const e=this._snap(this._svgPoint(t),t);return this._decorTextDialog={x:Bs(e[0]/Pr),y:Bs(e[1]/this._decorH),text:"",color:this._decorStyle.color},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t),!0);if(this._decorSel=null,this._bdMovable){const e=this._bdRect,i=this._svgPoint(t);if(i[0]>=e.x&&i[0]<=e.x+e.w&&i[1]>=e.y&&i[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(Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])t.id!==e.id),this._decorSel===e.id&&(this._decorSel=null),this._saveConfig(),void this.requestUpdate()}this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1},Nr(t)}}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,n=("line"===s.kind?s.x1:s.x)*Pr,o=("line"===s.kind?s.y1:s.y)*this._decorH,r=this._snap([n+(i[0]-e.start[0]),o+(i[1]-e.start[1])],t);let a=(r[0]-n)/Pr,l=(r[1]-o)/this._decorH;const c=e.orig,h="line"===c.kind?Math.min(c.x1,c.x2):c.x,d="line"===c.kind?Math.min(c.y1,c.y2):c.y,p="line"===c.kind?Math.abs(c.x2-c.x1):c.w||0,u="line"===c.kind?Math.abs(c.y2-c.y1):c.h||0,_=Os;a=Math.max(-_-h,Math.min(_-h-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){"decor"===this._mode&&"select"===this._decorTool&&"text"===t.kind&&this._decorOpenText(t)}_decorOpenText(t){this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:t.text,color:t.color,entity:t.entity||"",attr:t.attr||"",unit:t.unit||""}}_decorTextLink(t){const e={},i=(t.entity||"").trim();if(!i)return e;e.entity=i;const s=(t.attr||"").trim();s&&(e.attr=s);const n=(t.unit||"").trim();return n&&(e.unit=n),e}_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._curSpaceCfg,s=this._decorTextLink(t);if(t.id)i.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;const{entity:n,attr:o,unit:r,...a}=i;return{...a,text:e,color:t.color,...s}});else{const n="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);i.decor=[...this._decorList,{id:n,kind:"text",x:t.x,y:t.y,text:e,color:t.color,...s}],this._decorSel=n}this._decorTextDialog=null,this._saveConfig(),this.requestUpdate()}get _dtSel(){if("decor"!==this._mode||"select"!==this._decorTool||!this._decorSel)return null;const t=this._decorList.find(t=>t.id===this._decorSel);return!t||"text"!==t.kind&&"furniture"!==t.kind?null:t}_dtPivot(t){return"furniture"===t.kind?[(t.x+t.w/2)*Pr,(t.y+t.h/2)*this._decorH]:[t.x*Pr,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{size:s,...n}=i,o={...n};return void 0!==e.scale?o.scale=Number(e.scale.toFixed(4)):void 0===i.scale&&void 0!==s&&(o.scale=pi(i)),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){const s=this._dtSel;if(!s)return;t.stopPropagation(),t.preventDefault();const[n,o]=this._dtPivot(s),r=this._svgPoint(t),a="furniture"===s.kind;this._dtDrag={id:s.id,kind:e,pid:t.pointerId,ax:n,ay:o,r0:Math.hypot(r[0]-n,r[1]-o),a0:180*Math.atan2(r[1]-o,r[0]-n)/Math.PI,scale0:a?1:pi(s),angle0:Number(s.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:a?{x:s.x*Pr,y:s.y*this._decorH,w:s.w*Pr,h:s.h*this._decorH,angle:Number(s.angle)||0}:void 0,moved:!1},Nr(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if("scale"===e.kind&&e.orig){const s=t.shiftKey?0:this._gridPitch,n=s>0?s:.25*this._gridPitch,o=function(t,e,i,s,n,o=0,r=1e-6){const a=(Number(t.angle)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=-Math.sin(a),d=Math.cos(a),p=t.x+t.w/2,u=t.y+t.h/2,_=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,m=p+_*l+g*h,f=u+_*c+g*d,v=s-m,b=n-f;let y=(v*l+b*c)*(e>0?1:-1),w=(v*h+b*d)*(i>0?1:-1);return o>0&&(y=Math.round(y/o)*o,w=Math.round(w/o)*o),y=Math.max(r,y),w=Math.max(r,w),{x:m+(e>0?y/2:-y/2)*l+(i>0?w/2:-w/2)*h-y/2,y:f+(e>0?y/2:-y/2)*c+(i>0?w/2:-w/2)*d-w/2,w:y,h:w}}(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],s,n);return(Math.abs(o.w-e.orig.w)>1e-6||Math.abs(o.h-e.orig.h)>1e-6)&&(e.moved=!0),void this._furnApplyBox(e.id,o)}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.min(20,Math.max(.15,e.scale0*(t/e.r0)));return Math.abs(s-e.scale0)>1e-6&&(e.moved=!0),void this._dtApply(e.id,{scale:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let n=e.angle0+(s-e.a0);t.shiftKey||(n=5*Math.round(n/5)),n=(n%360+360)%360,n>180&&(n-=360),Math.abs(n-e.angle0)>1e-6&&(e.moved=!0),this._dtApply(e.id,{angle:n})}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&this._saveConfig(),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("furniture"===t.kind)e={id:t.id,x:t.x*Pr,y:t.y*this._decorH,w:t.w*Pr,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())}_decorDeleteSel(){if(!this._decorSel)return;this._curSpaceCfg.decor=this._decorList.filter(t=>t.id!==this._decorSel),this._decorSel=null,this._saveConfig(),this.requestUpdate()}get _furnWalls(){return this._segments}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=dn(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){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const n=Pr,o=this._decorH,r=gn(pn(i.w,this._cellCm,this._gridPitch,n)),a=gn(pn(i.h,this._cellCm,this._gridPitch,n)),l=this._snap(t,e);let c=l[0],h=l[1],d=0;if(!e.shiftKey){const t=bn(c,h,a*o,this._furnWalls,this._furnWallReach,this._gridPitch);t&&(c=t.cx,h=t.cy,d=t.angle)}const p="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),u={id:p,kind:"furniture",symbol:i.symbol,x:Bs(c/n-r/2),y:Bs(h/o-a/2),w:r,h:a,color:this._decorStyle.color,width:this._decorStyle.width};d&&(u.angle=Number(d.toFixed(2))),s.decor=[...this._decorList,u],this._decorSel=p,this._decorTool="select",this._furnPalette=null,this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(t){const e=this._decorMove,i=e.orig,s=this._curSpaceCfg;if(!s)return;const n=Pr,o=this._decorH,r=this._svgPoint(t),a=(i.x+i.w/2)*n+(r[0]-e.start[0]),l=(i.y+i.h/2)*o+(r[1]-e.start[1]);let c,h,d=Number(i.angle)||0;const p=t.shiftKey?null:bn(a,l,i.h*o,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)c=p.cx/n-i.w/2,h=p.cy/o-i.h/2,d=p.angle;else{const e=this._snap([a-i.w/2*n,l-i.h/2*o],t);c=e[0]/n,h=e[1]/o}c=Bs(c),h=Bs(h),(Math.abs(c-i.x)>1e-9||Math.abs(h-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:c,y:h};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_furnApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=Pr,n=this._decorH;i.decor=this._decorList.map(i=>i.id===t?{...i,x:Bs(e.x/s),y:Bs(e.y/n),w:gn(e.w/s),h:gn(e.h/n)}:i),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=Pr,s=this._decorH,n=e.w*i,o=e.h*s,r=function(t,e,i,s,n){const o=t+i/2,r=e+s/2,a=(Number(n)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=(t,e)=>{const i=t-o,s=e-r;return[o+i*l-s*c,r+i*c+s*l]};return[h(t,e),h(t+i,e),h(t+i,e+s),h(t,e+s)]}(e.x*i,e.y*s,n,o,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],l=a(r[0],r[1]),c=a(r[0],r[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[n,0])},{x:c[0],y:c[1],text:this._fmtLen([0,0],[0,o])}]}_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)}>`),o.push(B``)}return B`${o}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH;return(t?.openings||[]).map(t=>({...t,rx:t.x*Ar,ry:t.y*e,rlen:t.length*Ar}))}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return Ar}_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._snap(this._svgPoint(t),t);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},Or(t),!0}if("text"===e){const e=this._snap(this._svgPoint(t),t);return this._decorTextDialog={x:Bs(e[0]/Ar),y:Bs(e[1]/this._decorH),text:"",color:this._decorStyle.color},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t),!0);if(this._decorSel=null,this._bdMovable){const e=this._bdRect,i=this._svgPoint(t);if(i[0]>=e.x&&i[0]<=e.x+e.w&&i[1]>=e.y&&i[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(Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])t.id!==e.id),this._decorSel===e.id&&(this._decorSel=null),this._saveConfig(),void this.requestUpdate()}this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1},Or(t)}}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,n=("line"===s.kind?s.x1:s.x)*Ar,o=("line"===s.kind?s.y1:s.y)*this._decorH,r=this._snap([n+(i[0]-e.start[0]),o+(i[1]-e.start[1])],t);let a=(r[0]-n)/Ar,l=(r[1]-o)/this._decorH;const c=e.orig,h="line"===c.kind?Math.min(c.x1,c.x2):c.x,d="line"===c.kind?Math.min(c.y1,c.y2):c.y,p="line"===c.kind?Math.abs(c.x2-c.x1):c.w||0,u="line"===c.kind?Math.abs(c.y2-c.y1):c.h||0,_=Os;a=Math.max(-_-h,Math.min(_-h-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){"decor"===this._mode&&"select"===this._decorTool&&"text"===t.kind&&this._decorOpenText(t)}_decorOpenText(t){this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:t.text,color:t.color,entity:t.entity||"",attr:t.attr||"",unit:t.unit||""}}_decorTextLink(t){const e={},i=(t.entity||"").trim();if(!i)return e;e.entity=i;const s=(t.attr||"").trim();s&&(e.attr=s);const n=(t.unit||"").trim();return n&&(e.unit=n),e}_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._curSpaceCfg,s=this._decorTextLink(t);if(t.id)i.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;const{entity:n,attr:o,unit:r,...a}=i;return{...a,text:e,color:t.color,...s}});else{const n="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);i.decor=[...this._decorList,{id:n,kind:"text",x:t.x,y:t.y,text:e,color:t.color,...s}],this._decorSel=n}this._decorTextDialog=null,this._saveConfig(),this.requestUpdate()}get _dtSel(){if("decor"!==this._mode||"select"!==this._decorTool||!this._decorSel)return null;const t=this._decorList.find(t=>t.id===this._decorSel);return!t||"text"!==t.kind&&"furniture"!==t.kind?null:t}_dtPivot(t){return"furniture"===t.kind?[(t.x+t.w/2)*Ar,(t.y+t.h/2)*this._decorH]:[t.x*Ar,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{size:s,...n}=i,o={...n};return void 0!==e.scale?o.scale=Number(e.scale.toFixed(4)):void 0===i.scale&&void 0!==s&&(o.scale=pi(i)),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){const s=this._dtSel;if(!s)return;t.stopPropagation(),t.preventDefault();const[n,o]=this._dtPivot(s),r=this._svgPoint(t),a="furniture"===s.kind;this._dtDrag={id:s.id,kind:e,pid:t.pointerId,ax:n,ay:o,r0:Math.hypot(r[0]-n,r[1]-o),a0:180*Math.atan2(r[1]-o,r[0]-n)/Math.PI,scale0:a?1:pi(s),angle0:Number(s.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:a?{x:s.x*Ar,y:s.y*this._decorH,w:s.w*Ar,h:s.h*this._decorH,angle:Number(s.angle)||0}:void 0,moved:!1},Or(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if("scale"===e.kind&&e.orig){const s=t.shiftKey?0:this._gridPitch,n=s>0?s:.25*this._gridPitch,o=function(t,e,i,s,n,o=0,r=1e-6){const a=(Number(t.angle)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=-Math.sin(a),d=Math.cos(a),p=t.x+t.w/2,u=t.y+t.h/2,_=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,m=p+_*l+g*h,f=u+_*c+g*d,v=s-m,b=n-f;let y=(v*l+b*c)*(e>0?1:-1),w=(v*h+b*d)*(i>0?1:-1);return o>0&&(y=Math.round(y/o)*o,w=Math.round(w/o)*o),y=Math.max(r,y),w=Math.max(r,w),{x:m+(e>0?y/2:-y/2)*l+(i>0?w/2:-w/2)*h-y/2,y:f+(e>0?y/2:-y/2)*c+(i>0?w/2:-w/2)*d-w/2,w:y,h:w}}(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],s,n);return(Math.abs(o.w-e.orig.w)>1e-6||Math.abs(o.h-e.orig.h)>1e-6)&&(e.moved=!0),void this._furnApplyBox(e.id,o)}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.min(20,Math.max(.15,e.scale0*(t/e.r0)));return Math.abs(s-e.scale0)>1e-6&&(e.moved=!0),void this._dtApply(e.id,{scale:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let n=e.angle0+(s-e.a0);t.shiftKey||(n=5*Math.round(n/5)),n=(n%360+360)%360,n>180&&(n-=360),Math.abs(n-e.angle0)>1e-6&&(e.moved=!0),this._dtApply(e.id,{angle:n})}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&this._saveConfig(),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("furniture"===t.kind)e={id:t.id,x:t.x*Ar,y:t.y*this._decorH,w:t.w*Ar,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())}_decorDeleteSel(){if(!this._decorSel)return;this._curSpaceCfg.decor=this._decorList.filter(t=>t.id!==this._decorSel),this._decorSel=null,this._saveConfig(),this.requestUpdate()}get _furnWalls(){return this._segments}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=dn(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){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const n=Ar,o=this._decorH,r=gn(pn(i.w,this._cellCm,this._gridPitch,n)),a=gn(pn(i.h,this._cellCm,this._gridPitch,n)),l=this._snap(t,e);let c=l[0],h=l[1],d=0;if(!e.shiftKey){const t=bn(c,h,a*o,this._furnWalls,this._furnWallReach,this._gridPitch);t&&(c=t.cx,h=t.cy,d=t.angle)}const p="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),u={id:p,kind:"furniture",symbol:i.symbol,x:Bs(c/n-r/2),y:Bs(h/o-a/2),w:r,h:a,color:this._decorStyle.color,width:this._decorStyle.width};d&&(u.angle=Number(d.toFixed(2))),s.decor=[...this._decorList,u],this._decorSel=p,this._decorTool="select",this._furnPalette=null,this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(t){const e=this._decorMove,i=e.orig,s=this._curSpaceCfg;if(!s)return;const n=Ar,o=this._decorH,r=this._svgPoint(t),a=(i.x+i.w/2)*n+(r[0]-e.start[0]),l=(i.y+i.h/2)*o+(r[1]-e.start[1]);let c,h,d=Number(i.angle)||0;const p=t.shiftKey?null:bn(a,l,i.h*o,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)c=p.cx/n-i.w/2,h=p.cy/o-i.h/2,d=p.angle;else{const e=this._snap([a-i.w/2*n,l-i.h/2*o],t);c=e[0]/n,h=e[1]/o}c=Bs(c),h=Bs(h),(Math.abs(c-i.x)>1e-9||Math.abs(h-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:c,y:h};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_furnApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=Ar,n=this._decorH;i.decor=this._decorList.map(i=>i.id===t?{...i,x:Bs(e.x/s),y:Bs(e.y/n),w:gn(e.w/s),h:gn(e.h/n)}:i),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=Ar,s=this._decorH,n=e.w*i,o=e.h*s,r=function(t,e,i,s,n){const o=t+i/2,r=e+s/2,a=(Number(n)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=(t,e)=>{const i=t-o,s=e-r;return[o+i*l-s*c,r+i*c+s*l]};return[h(t,e),h(t+i,e),h(t+i,e+s),h(t,e+s)]}(e.x*i,e.y*s,n,o,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],l=a(r[0],r[1]),c=a(r[0],r[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[n,0])},{x:c[0],y:c[1],text:this._fmtLen([0,0],[0,o])}]}_renderFurnPalette(){const t=this._furnPalette,e=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return W`
t.stopPropagation()}>
${this._t("furn.title")} @@ -2680,7 +2680,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?{...Ps(t.plan_aspect,Pr)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?Rs(t,Pr):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale);return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,k:Number.isFinite(s)&&s>0?s:1}}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&("select"===this._decorTool||"backdrop"===this._decorTool)}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(t,e,i){const s=this._curSpaceCfg;s&&(s.plan_x=Number(Bs(t).toFixed(6)),s.plan_y=Number(Bs(e).toFixed(6)),s.plan_scale=Number(Math.min(100,Math.max(As,i)).toFixed(6)),this._cfgEpoch++,this.requestUpdate())}_bdStart(t,e){const i=this._bdBase,s=this._bdRect;if(!i||!s)return!1;const n=this._svgPoint(t),o=e?e[0]:0,r=e?e[1]:0,a=o>0?s.x:s.x+s.w,l=r>0?s.y:s.y+s.h;return this._bdDrag={kind:e?"scale":"move",pid:t.pointerId,sx:n[0],sy:n[1],base:i,p0:this._bdParams,fx:a,fy:l,sgx:o,sgy:r,moved:!1},Nr(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const n=s.x+e.p0.dx*Pr,o=s.y+e.p0.dy*Pr,r=this._snap([n+(i[0]-e.sx),o+(i[1]-e.sy)],t);return(Math.abs(r[0]-n)>1e-9||Math.abs(r[1]-o)>1e-9)&&(e.moved=!0),void this._bdApply((r[0]-s.x)/Pr,(r[1]-s.y)/Pr,e.p0.k)}const n=s.w||1,o=s.h||1;let r=Math.max(Math.abs(i[0]-e.fx)/n,Math.abs(i[1]-e.fy)/o);if(!t.shiftKey){const t=n>=o,i=ke(t?e.fx+e.sgx*r*n:e.fy+e.sgy*r*o,this._gridPitch),s=Math.abs(i-(t?e.fx:e.fy))/(t?n:o);s>0&&(r=s)}r=Math.min(100,Math.max(As,r)),Math.abs(r-e.p0.k)>1e-9&&(e.moved=!0);const a=e.sgx>0?e.fx:e.fx-r*n,l=e.sgy>0?e.fy:e.fy-r*o;this._bdApply((a-s.x)/Pr,(l-s.y)/Pr,r)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.k}_bdReset(){const t=this._curSpaceCfg;t&&(delete t.plan_x,delete t.plan_y,delete t.plan_scale,this._bdDrag=null,this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate())}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&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 V;const i=.02*Math.max(t.w,t.h),s=i/4;return B` +
`}get _bdBase(){const t=this._curSpaceCfg;return t?.plan_url?{...Ps(t.plan_aspect,Ar)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?Rs(t,Ar):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale);return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,k:Number.isFinite(s)&&s>0?s:1}}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&("select"===this._decorTool||"backdrop"===this._decorTool)}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(t,e,i){const s=this._curSpaceCfg;s&&(s.plan_x=Number(Bs(t).toFixed(6)),s.plan_y=Number(Bs(e).toFixed(6)),s.plan_scale=Number(Math.min(100,Math.max(As,i)).toFixed(6)),this._cfgEpoch++,this.requestUpdate())}_bdStart(t,e){const i=this._bdBase,s=this._bdRect;if(!i||!s)return!1;const n=this._svgPoint(t),o=e?e[0]:0,r=e?e[1]:0,a=o>0?s.x:s.x+s.w,l=r>0?s.y:s.y+s.h;return this._bdDrag={kind:e?"scale":"move",pid:t.pointerId,sx:n[0],sy:n[1],base:i,p0:this._bdParams,fx:a,fy:l,sgx:o,sgy:r,moved:!1},Or(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const n=s.x+e.p0.dx*Ar,o=s.y+e.p0.dy*Ar,r=this._snap([n+(i[0]-e.sx),o+(i[1]-e.sy)],t);return(Math.abs(r[0]-n)>1e-9||Math.abs(r[1]-o)>1e-9)&&(e.moved=!0),void this._bdApply((r[0]-s.x)/Ar,(r[1]-s.y)/Ar,e.p0.k)}const n=s.w||1,o=s.h||1;let r=Math.max(Math.abs(i[0]-e.fx)/n,Math.abs(i[1]-e.fy)/o);if(!t.shiftKey){const t=n>=o,i=ke(t?e.fx+e.sgx*r*n:e.fy+e.sgy*r*o,this._gridPitch),s=Math.abs(i-(t?e.fx:e.fy))/(t?n:o);s>0&&(r=s)}r=Math.min(100,Math.max(As,r)),Math.abs(r-e.p0.k)>1e-9&&(e.moved=!0);const a=e.sgx>0?e.fx:e.fx-r*n,l=e.sgy>0?e.fy:e.fy-r*o;this._bdApply((a-s.x)/Ar,(l-s.y)/Ar,r)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.k}_bdReset(){const t=this._curSpaceCfg;t&&(delete t.plan_x,delete t.plan_y,delete t.plan_scale,this._bdDrag=null,this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate())}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&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 V;const i=.02*Math.max(t.w,t.h),s=i/4;return B` ${[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]].map(([t,n,o])=>{const r=t<0?e.x:e.x+e.w,a=n<0?e.y:e.y+e.h;return B`this._dtStart(i,"scale",[t,e])}>`)} - `}_renderDecorLayer(){const t=Pr,e=this._decorH,i="decor"===this._mode,s=this._decorList.map(s=>{const n="dshape"+(i&&this._decorSel===s.id?" dsel":""),o=t=>this._decorShapeDown(t,s),r=()=>this._decorShapeDbl(s);if("line"===s.kind)return B``}_renderDecorLayer(){const t=Ar,e=this._decorH,i="decor"===this._mode,s=this._decorList.map(s=>{const n="dshape"+(i&&this._decorSel===s.id?" dsel":""),o=t=>this._decorShapeDown(t,s),r=()=>this._decorShapeDbl(s);if("line"===s.kind)return B``;if("rect"===s.kind)return B``)} ${i?i.segs.map(t=>B``):V} - `}_openCuts(){const t=this._curSpaceCfg;return mo(this._spaceModel().rooms,t?.open_spans,Pr,.02*this._gridPitch)}_openPairs(){const t=this._openCuts();if(!t.length)return[];const e=this._spaceModel().rooms.filter(t=>t.id),i=.02*this._gridPitch,s=[];for(let n=0;n{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return l.some(t=>Bi(e,t)<4*i)});c.length&&s.push({a:e[n],b:e[o],segs:c})}return s}_commitOpenSpans(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch;this._cfgEpoch++;const s=this._spaceModel().rooms;let n=lo(e.open_spans);n.length&&t?.old.length?n=function(t,e,i,s){const n=lo(t);if(!n.length)return[];if(e.length!==i.length)return n;const o=[];for(const t of n){const n=ro(t,s),r=[n[0],n[1]],a=[n[2],n[3]];let l=r,c=a;for(let t=0;t{const e=co(t,[s[0],s[1],n[0],n[1]]);if(e.d>.001)return t;const i=h[0]-o[0],r=h[1]-o[1];return[o[0]+i*e.t,o[1]+r*e.t]};if(co([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],n[0],n[1]]).d<.01){l=d(r),c=d(a);break}}o.push(oo(l,c,s))}return o}(n,t.old,t.next,Pr):n.length||(n=fo(mo(s,null,Pr,i),Pr)),n=function(t,e,i,s){const n=lo(t);if(!n.length)return[];const o=uo(e,s);if(!o.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of n){const e=ro(t,i),n=e[0],l=e[1],c=e[2]-n,h=e[3]-l,d=Math.hypot(c,h);if(d4*s||o>4*s)continue;const r=(t[0]-n)*p+(t[1]-l)*u,c=(t[2]-n)*p+(t[3]-l)*u,h=Math.max(0,Math.min(r,c)),g=Math.min(d,Math.max(r,c));if(g-ht.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t){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 t of e){const e=[n+p*t.lo,l+u*t.lo],s=[n+p*t.hi,l+u*t.hi];Math.hypot(s[0]-e[0],s[1]-e[1])ro(t,Pr)))}_persistOpenCuts(t){const e=this._curSpaceCfg;if(!e)return;const i=fo(t,Pr);i.length?e.open_spans=i:delete e.open_spans,function(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const n=(e||[]).filter(t=>t?.id);new Map(n.map(t=>[t.id,t]));const o=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=o.get(t),s=o.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;tBi(i,t)<4*s)){r(n[t].id,n[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}(e.rooms||[],this._spaceModel().rooms,t,.02*this._gridPitch)}_closeOpenSpan(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch,s=function(t,e,i){const s=[(e[0]+e[2])/2,(e[1]+e[3])/2];return t.filter(t=>{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return Math.hypot(e[0]-s[0],e[1]-s[1])>4*i})}(this._openCuts(),t,i);let n=this._normalizeWalls(e.walls,s);if(!(Zn(this._spaceModel().rooms,n,s,t,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)>0)){const e=[];for(const t of Xn(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr))t.open||e.push([t.a[0],t.a[1],t.b[0],t.b[1]]);n=bo(n,t,e,this._wallKeyPitch,Pr,15),n=this._normalizeWalls(n,s)}n.length?e.walls=n:delete e.walls,this._persistOpenCuts(s),this._showToast(this._t("toast.openwall_closed_span")),this._saveConfig(),this.requestUpdate()}_openWallClick(t){const e=6*this._gridPitch,i=.02*this._gridPitch,s=this._openCuts();if(this._openWallAnchor){const{p:n,edge:o}=this._openWallAnchor,r=ho(po(t,o,yo(o,s,i),this._gridPitch,1.5*this._gridPitch),o),a=Math.hypot(r[0]-n[0],r[1]-n[1]);if(this._openWallAnchor=null,a<.5*this._gridPitch)return void this._showToast(this._t("toast.openwall_short"));if(Bi(t,o)>e)return void this._showToast(this._t("toast.openwall_pick"));const l=[n[0],n[1],r[0],r[1]],c=[...s,l],h=this._curSpaceCfg;if(!h)return;const d=this._normalizeWalls(h.walls,c);d.length?h.walls=d:delete h.walls;const p=(h.openings||[]).length;return h.openings=function(t,e,i,s){return t?.length?t.filter(t=>Bi([Number(t.x)*i,Number(t.y)*i],e)>s||!Nn([e[0],e[1]],[e[2],e[3]],Number(t.angle)||0)):t?t.slice():[]}(h.openings,l,Pr,e),(h.openings||[]).lengththis._pointInRoom(t,e));if(!a)return;if(!confirm(this._t("confirm.delete_room",{name:a.name})))return;const l=this._curSpaceCfg;l&&(l.rooms=l.rooms.filter(t=>t.id!==a.id),this._commitOpenSpans(),this._saveConfig(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate())}get _wallKeyPitch(){return Hs}get _spaceWalls(){const t=this._curSpaceCfg?.walls;return Array.isArray(t)?t:[]}_cfgOpenCuts(){return lo(this._curSpaceCfg?.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_intervalCm(t){return Zn(this._spaceModel().rooms,this._spaceWalls,this._openCuts(),t,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)}_normalizeWalls(t,e){const i=function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=[],l=new Set;for(const c of Xn(t,e,i,s,n,o,r))!c.open&&c.cm>0&&!l.has(c.key)&&(l.add(c.key),a.push({key:c.key,cm:c.cm}));return a}(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr);return En(i,this._curSpaceCfg?.rooms||[],Hs,1,e.map(t=>[t[0]/Pr,t[1]/Pr,t[2]/Pr,t[3]/Pr]))}_paperShapes(t){const e=this._spaceWalls;if(!e.length)return Me(t);const i=this._openPairs().flatMap(t=>t.segs);return so(t,e,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)}_thickWallCuts(){const t=this._spaceWalls;if(!t.length)return[];const e=this._openPairs().flatMap(t=>t.segs);return function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=new Set,l=[];for(const c of t||[]){if(!c?.id)continue;const h=Yn(t,c.id,e,i,s,n,o,r);if(!h)continue;const d=h.poly;for(let t=0;t0))continue;const u=Dn(e,i,s,r);if(a.has(u))continue;a.add(u);const _=xn(p,n,o),[g,m]=qn(d,t),f=-g,v=-m,b=_/2,y=[[e[0]+f*b,e[1]+v*b],[i[0]+f*b,i[1]+v*b],[i[0]+g*b,i[1]+m*b],[e[0]+g*b,e[1]+m*b]];l.push({key:u,kind:c,cm:p,quad:y,a:[e[0],e[1]],b:[i[0],i[1]],depthUnits:_})}}return l}(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_wallThickHit(t){const e=6*this._gridPitch,i=this._openCuts();let s=null;for(const n of Xn(this._spaceModel().rooms,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)){const i=Bi(t,[n.a[0],n.a[1],n.b[0],n.b[1]]);i<=e&&(!s||i0?xn(e,this._cellCm,this._gridPitch):3*this._gridPitch,s=Math.max(i/2,1.25*this._gridPitch);let n="";for(const e of t.segs)n+=(n?" ":"")+Fn([[e[0],e[1]],[e[2],e[3]]],s,!1);return{segs:t.segs,open:t.open,d:n}}_wallThickClick(t){const e=this._wallThickHit(t);if(!e)return void this._showToast(this._t("toast.wallthick_pick"));if(e.open)return void this._showToast(this._t("toast.wallthick_open"));const i=e.cm,s=this._viewOr(this._baseVb()),n=(e.a[0]+e.b[0])/2,o=(e.a[1]+e.b[1])/2;this._wallDialog={a:e.a,b:e.b,value:kn(i,this._imperial),roomId:e.roomId,sx:(n-s.x)/s.w*100,sy:(o-s.y)/s.h*100}}_wallThickApply(t){const e=this._wallDialog;if(!e)return;const i=this._curSpaceCfg;if(!i)return;const s=$n(e.value,this._imperial),n=this._openCuts();let o;o=t&&e.roomId?In(i.walls,this._spaceModel().rooms,e.roomId,s,this._wallKeyPitch,n,Pr):On(i.walls,e.a,e.b,s,this._wallKeyPitch,Pr),o=this._normalizeWalls(o,n),o.length?i.walls=o:delete i.walls,this._wallDialog=null,this._showToast(this._t(null==s?"toast.wallthick_cleared":"toast.wallthick_set")),this._saveConfig(),this.requestUpdate()}_wallHatchDefs(t){if(!this._spaceWalls.length&&!this._markup)return B``;const e=Math.max(.4,1/Math.max(this._zoom,.4)),i=t||"var(--hp-muted, #607d8b)";return B` + `}_openCuts(){const t=this._curSpaceCfg;return mo(this._spaceModel().rooms,t?.open_spans,Ar,.02*this._gridPitch)}_openPairs(){const t=this._openCuts();if(!t.length)return[];const e=this._spaceModel().rooms.filter(t=>t.id),i=.02*this._gridPitch,s=[];for(let n=0;n{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return l.some(t=>Bi(e,t)<4*i)});c.length&&s.push({a:e[n],b:e[o],segs:c})}return s}_commitOpenSpans(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch;this._cfgEpoch++;const s=this._spaceModel().rooms;let n=lo(e.open_spans);n.length&&t?.old.length?n=wo(n,t.old,t.next,Ar):n.length||(n=fo(mo(s,null,Ar,i),Ar)),n=function(t,e,i,s){const n=lo(t);if(!n.length)return[];const o=uo(e,s);if(!o.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of n){const e=ro(t,i),n=e[0],l=e[1],c=e[2]-n,h=e[3]-l,d=Math.hypot(c,h);if(d4*s||o>4*s)continue;const r=(t[0]-n)*p+(t[1]-l)*u,c=(t[2]-n)*p+(t[3]-l)*u,h=Math.max(0,Math.min(r,c)),g=Math.min(d,Math.max(r,c));if(g-ht.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t){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 t of e){const e=[n+p*t.lo,l+u*t.lo],s=[n+p*t.hi,l+u*t.hi];Math.hypot(s[0]-e[0],s[1]-e[1])ro(t,Ar)))}_persistOpenCuts(t){const e=this._curSpaceCfg;if(!e)return;const i=fo(t,Ar);i.length?e.open_spans=i:delete e.open_spans,function(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const n=(e||[]).filter(t=>t?.id);new Map(n.map(t=>[t.id,t]));const o=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=o.get(t),s=o.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;tBi(i,t)<4*s)){r(n[t].id,n[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}(e.rooms||[],this._spaceModel().rooms,t,.02*this._gridPitch)}_closeOpenSpan(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch,s=function(t,e,i){const s=[(e[0]+e[2])/2,(e[1]+e[3])/2];return t.filter(t=>{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return Math.hypot(e[0]-s[0],e[1]-s[1])>4*i})}(this._openCuts(),t,i);let n=this._normalizeWalls(e.walls,s);if(!(Zn(this._spaceModel().rooms,n,s,t,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)>0)){const e=[];for(const t of Xn(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar))t.open||e.push([t.a[0],t.a[1],t.b[0],t.b[1]]);n=bo(n,t,e,this._wallKeyPitch,Ar,15),n=this._normalizeWalls(n,s)}n.length?e.walls=n:delete e.walls,this._persistOpenCuts(s),this._showToast(this._t("toast.openwall_closed_span")),this._saveConfig(),this.requestUpdate()}_openWallClick(t){const e=6*this._gridPitch,i=.02*this._gridPitch,s=this._openCuts();if(this._openWallAnchor){const{p:n,edge:o}=this._openWallAnchor,r=ho(po(t,o,yo(o,s,i),this._gridPitch,1.5*this._gridPitch),o),a=Math.hypot(r[0]-n[0],r[1]-n[1]);if(this._openWallAnchor=null,a<.5*this._gridPitch)return void this._showToast(this._t("toast.openwall_short"));if(Bi(t,o)>e)return void this._showToast(this._t("toast.openwall_pick"));const l=[n[0],n[1],r[0],r[1]],c=[...s,l],h=this._curSpaceCfg;if(!h)return;const d=this._normalizeWalls(h.walls,c);d.length?h.walls=d:delete h.walls;const p=(h.openings||[]).length;return h.openings=function(t,e,i,s){return t?.length?t.filter(t=>Bi([Number(t.x)*i,Number(t.y)*i],e)>s||!Nn([e[0],e[1]],[e[2],e[3]],Number(t.angle)||0)):t?t.slice():[]}(h.openings,l,Ar,e),(h.openings||[]).lengththis._pointInRoom(t,e));if(!a)return;if(!confirm(this._t("confirm.delete_room",{name:a.name})))return;const l=this._curSpaceCfg;l&&(l.rooms=l.rooms.filter(t=>t.id!==a.id),this._commitOpenSpans(),this._saveConfig(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate())}get _wallKeyPitch(){return qs}get _spaceWalls(){const t=this._curSpaceCfg?.walls;return Array.isArray(t)?t:[]}_cfgOpenCuts(){return lo(this._curSpaceCfg?.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_intervalCm(t){return Zn(this._spaceModel().rooms,this._spaceWalls,this._openCuts(),t,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)}_normalizeWalls(t,e){const i=function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=[],l=new Set;for(const c of Xn(t,e,i,s,n,o,r))!c.open&&c.cm>0&&!l.has(c.key)&&(l.add(c.key),a.push({key:c.key,cm:c.cm}));return a}(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar);return En(i,this._curSpaceCfg?.rooms||[],qs,1,e.map(t=>[t[0]/Ar,t[1]/Ar,t[2]/Ar,t[3]/Ar]))}_paperShapes(t){const e=this._spaceWalls;if(!e.length)return Me(t);const i=this._openPairs().flatMap(t=>t.segs);return so(t,e,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)}_thickWallCuts(){const t=this._spaceWalls;if(!t.length)return[];const e=this._openPairs().flatMap(t=>t.segs);return function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=new Set,l=[];for(const c of t||[]){if(!c?.id)continue;const h=Yn(t,c.id,e,i,s,n,o,r);if(!h)continue;const d=h.poly;for(let t=0;t0))continue;const u=Dn(e,i,s,r);if(a.has(u))continue;a.add(u);const _=xn(p,n,o),[g,m]=Hn(d,t),f=-g,v=-m,b=_/2,y=[[e[0]+f*b,e[1]+v*b],[i[0]+f*b,i[1]+v*b],[i[0]+g*b,i[1]+m*b],[e[0]+g*b,e[1]+m*b]];l.push({key:u,kind:c,cm:p,quad:y,a:[e[0],e[1]],b:[i[0],i[1]],depthUnits:_})}}return l}(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_wallThickHit(t){const e=6*this._gridPitch,i=this._openCuts();let s=null;for(const n of Xn(this._spaceModel().rooms,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)){const i=Bi(t,[n.a[0],n.a[1],n.b[0],n.b[1]]);i<=e&&(!s||i0?xn(e,this._cellCm,this._gridPitch):3*this._gridPitch,s=Math.max(i/2,1.25*this._gridPitch);let n="";for(const e of t.segs)n+=(n?" ":"")+Fn([[e[0],e[1]],[e[2],e[3]]],s,!1);return{segs:t.segs,open:t.open,d:n}}_wallThickClick(t){const e=this._wallThickHit(t);if(!e)return void this._showToast(this._t("toast.wallthick_pick"));if(e.open)return void this._showToast(this._t("toast.wallthick_open"));const i=e.cm,s=this._viewOr(this._baseVb()),n=(e.a[0]+e.b[0])/2,o=(e.a[1]+e.b[1])/2;this._wallDialog={a:e.a,b:e.b,value:kn(i,this._imperial),roomId:e.roomId,sx:(n-s.x)/s.w*100,sy:(o-s.y)/s.h*100}}_wallThickApply(t){const e=this._wallDialog;if(!e)return;const i=this._curSpaceCfg;if(!i)return;const s=$n(e.value,this._imperial),n=this._openCuts();let o;o=t&&e.roomId?In(i.walls,this._spaceModel().rooms,e.roomId,s,this._wallKeyPitch,n,Ar):On(i.walls,e.a,e.b,s,this._wallKeyPitch,Ar),o=this._normalizeWalls(o,n),o.length?i.walls=o:delete i.walls,this._wallDialog=null,this._showToast(this._t(null==s?"toast.wallthick_cleared":"toast.wallthick_set")),this._saveConfig(),this.requestUpdate()}_wallHatchDefs(t){if(!this._spaceWalls.length&&!this._markup)return B``;const e=Math.max(.4,1/Math.max(this._zoom,.4)),i=t||"var(--hp-muted, #607d8b)";return B` - `}_renderWallBodies(t){if(t&&!t.showBorders&&!this._markup)return B``;const e=this._spaceWalls;if(!e.length)return B``;const i=this._openPairs().flatMap(t=>t.segs),s=(this._curSpaceCfg?.openings||[]).map(t=>({x:Number(t.x)*Pr,y:Number(t.y)*Pr,angle:Number(t.angle)||0,length:(Number(t.length)>0?Number(t.length):.9)*Pr})),n=eo(this._spaceModel().rooms,e,i,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr);if(!n)return B``;const o=this._stageEl,r=this._viewOr(this._baseVb()),a=o&&o.clientWidth&&r.w?o.clientWidth/r.w:1,l=t?.color||"var(--hp-muted)",c=yn(n.depthUnits,a),h=this._fillColors.wall_fill;return B` + `}_renderWallBodies(t){if(t&&!t.showBorders&&!this._markup)return B``;const e=this._spaceWalls;if(!e.length)return B``;const i=this._openPairs().flatMap(t=>t.segs),s=(this._curSpaceCfg?.openings||[]).map(t=>({x:Number(t.x)*Ar,y:Number(t.y)*Ar,angle:Number(t.angle)||0,length:(Number(t.length)>0?Number(t.length):.9)*Ar})),n=eo(this._spaceModel().rooms,e,i,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar);if(!n)return B``;const o=this._stageEl,r=this._viewOr(this._baseVb()),a=o&&o.clientWidth&&r.w?o.clientWidth/r.w:1,l=t?.color||"var(--hp-muted)",c=yn(n.depthUnits,a),h=this._fillColors.wall_fill;return B`
-
`:W``}_openingClick(t,e=!1){const i=1.5*this._gridPitch,s=this._openingsR.find(e=>Math.hypot(t[0]-e.rx,t[1]-e.ry)<=Math.max(e.rlen/2,i));if(s)return void this._editOpening(s);const n=De(t,this._spaceModel().rooms,i);if(!n)return void this._showToast(this._t("toast.opening_no_wall"));if(function(t,e,i,s,n){for(const o of s)if(!(Bi([t,e],o)>n)&&Nn([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(n.x,n.y,n.angle,this._openCuts(),i))return void this._showToast(this._t("toast.opening_on_virtual"));const o=this._opRuler(n,this._cmToUnits(90),e);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{Nr(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1}}}_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=De(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,n=s?.openings?.find(t=>t.id===e.id);if(!n)return;const o=this._opRuler(i,n.length*Pr,t.shiftKey);this._opMeasure=o.measure;const r=o.x/Pr,a=o.y/this._spaceH;n.x===r&&n.y===a&&n.angle===i.angle||(this._opDrag.dirty=!0),n.x=r,n.y=a,n.angle=i.angle,this.requestUpdate()}_opRuler(t,e,i){const s=this._spaceModel().rooms,n=this._gridPitch/2;let o=t.x,r=t.y,a=Te([o,r],t.angle,e,s,n);if(!a||!a.centered||i||o===a.wallCenter[0]&&r===a.wallCenter[1]){if(a&&!i){const[i,l]=a.wallA,[c,h]=a.wallB,d=c-i,p=h-l,u=Math.hypot(d,p);if(u>0){const c=this._gridPitch,h=Math.min(e/2,u/2);let _=Math.round(((o-i)*d+(r-l)*p)/u/c)*c;_=Math.max(h,Math.min(u-h,_)),o=i+_/u*d,r=l+_/u*p,a=Te([o,r],t.angle,e,s,n)||a}}}else[o,r]=a.wallCenter,a=Te([o,r],t.angle,e,s,n);if(!a)return{x:o,y:r,angle:t.angle,measure:null};const l="mi"===this.hass?.config?.unit_system?.length,c=(t,e)=>({x:e[0],y:e[1],text:$e(t/this._gridPitch*this._cellCm,l)});return{x:o,y:r,angle:t.angle,measure:{labels:[c(a.sideA,a.midA),c(a.sideB,a.midB)],guide:a.centered&&!i?{x:a.wallCenter[0],y:a.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;const i=this._opDrag.moved;this._opMeasure=null,i&&this._opDrag.dirty&&this._saveConfig(),i?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._spaceH,s={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Pr,y:t.y/i,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/Pr,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===s.id);n>=0?e.openings[n]=s:e.openings.push(s),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;t?.id&&e?.openings&&(e.openings=e.openings.filter(e=>e.id!==t.id),this._saveConfig(),this._openingDialog=null,this.requestUpdate())}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&t.push([e,s?.attributes?.friendly_name||e,n?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_mergeClick(t){const e=this._spaceModel().rooms,i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const s=i.id;if(!this._mergeSel||this._mergeSel===s)return void(this._mergeSel=this._mergeSel===s?null:s);const n=e.find(t=>t.id===this._mergeSel),o=n?Se(n):null,r=Se(i),a=o&&r?Ve(o,r):null;if(!a)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:s,poly:a,pick:"a"},this._mergeSel=null}_commitMerge(){const t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._spaceH,s="a"===t.pick?t.aId:t.bId,n="a"===t.pick?t.bId:t.aId,o=e.rooms.find(t=>t.id===s);o?(o.poly=t.poly.map(t=>[t[0]/Pr,t[1]/i]),delete o.x,delete o.y,delete o.w,delete o.h,e.rooms=e.rooms.filter(t=>t.id!==n),this._commitOpenSpans(),this._saveConfig(),this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.rooms_merged",{name:o.name||""}))):this._mergeDialog=null}_splitClick(t,e=!1){const i=this._spaceModel().rooms;if(!this._splitSel){const e=[...i].reverse().find(e=>this._pointInRoom(t,e));if(!e?.id)return;return void(this._splitSel={roomId:e.id,pts:[]})}const s=i.find(t=>t.id===this._splitSel.roomId),n=s?Se(s):null;if(!s||!n)return void(this._splitSel=null);const o=.02*this._gridPitch,r=6*this._gridPitch,a=Ee(t,n),l=a&&!e&&function(t,e,i){let s=null,n=1/0;for(let o=0;o=n)continue;n=_;const g=Math.sqrt(p),m=(i>0?Math.max(0,Math.min(g,Math.round(u*g/i)*i)):u*g)/g;s=[r+m*h,a+m*d]}return s}(a,n,this._gridPitch)||a,c=a&&l&&Math.hypot(a[0]-t[0],a[1]-t[1])<=r?l:null,h=!!c&&Ne(c,n,o),d=this._splitSel.pts;if(!d.length)return h?void(this._splitSel={...this._splitSel,pts:[c]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const i=this._snap(t,e);return Oe(i,n,o)?void(this._splitSel={...this._splitSel,pts:[...d,i]}):void this._showToast(this._t("toast.split_pick_inside"))}const p=function(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(Pe(s,n,i))return null;const o=Ge(t,s,i),r=Ge(t,n,i);if(o<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!Oe(e,t,i))return null;for(let i=0;i{const r=[e];let a=(s+1)%t.length;for(let e=0;e<=t.length&&(r.push(t[a]),a!==o);e++)a=(a+1)%t.length;return r.push(n),Ke(r,i)};let c,h;if(o===r){const r=Ke([...e],i);if(r.length<3||Be(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}c=Ke(a,i),h=r}else c=Ke([...l(s,o,n,r),...[...a].reverse()],i),h=Ke([...l(n,r,s,o),...a],i);return c.length<3||h.length<3||Be(c)<=i||Be(h)<=i||Math.abs(Be(c)+Be(h)-Be(t))>Math.max(i,1e-6*Be(t))?null:[c,h]}(n,[...d,c],o);if(!p)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,g=Be(u)>=Be(_)?u:_,m=g===u?_:u;this._pendingSplit={roomId:s.id,mainPoly:g,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("opening"===this._tool||"openwall"===this._tool||"wallthick"===this._tool)return this._opShift=!!t.shiftKey,void(this._cursorPt=this._svgPoint(t));const e="draw"===this._tool&&this._path.length&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;(e||i)&&(this._cursorPt=this._snap(this._svgPoint(t),t))}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),n=De(t,this._spaceModel().rooms,e);if(!n)return null;const o=this._opRuler(n,s,this._opShift);return{x:o.x,y:o.y,angle:o.angle,rlen:s,measure:o.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._spaceH,i=!!this._pendingSplit;let s;if(this._pendingSplit){const i=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!i)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);i.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Pr,t[1]/e]),delete i.x,delete i.y,delete i.w,delete i.h,s=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;s=this._path.slice(0,-1)}const n=this._areaSel?this.hass.areas[this._areaSel]?.name:"",o={id:"r"+Date.now().toString(36),name:this._nameSel||n||this._t("room.default_name"),area:this._areaSel||null,poly:s.map(t=>[t[0]/Pr,t[1]/e]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(o),i&&this._commitOpenSpans(),!i){const e=this._drawWallCm;if(null!=e){this._cfgEpoch++;const i=this._openCuts();let s=function(t,e,i,s,n,o=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=Gn(e,i,o,n,r);if(!a)return t?t.slice():[];const l=Kn(t,a,n,r);let c=t?t.slice():[];for(let t=0;t0||(c=On(c,e,i,s,n,r))}return c}(t.walls,this._spaceModel().rooms,o.id,e,this._wallKeyPitch,i,Pr);s=this._normalizeWalls(s,i),s.length?t.walls=s:delete t.walls}}this._saveConfig(),this._path=[],this._pendingSplit=null,this._splitSel=null;const r=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let a=0;if(r){const t=Pr,e={...this._layout};for(const i of this._devices){if(i.area!==r||i.space!==this._space)continue;if(a++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/Pr,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const l=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(r?this._t("toast.room_saved",{n:l,added:a}):this._t("toast.room_saved_no_area",{n:l}))}_cancelPath(){this._path=[],this._cursorPt=null,this._roomDialog=!1,this._pendingSplit=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._openWallAnchor=null}_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: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:[...t.marker?.controls||[]],controlsFilter:"",isLight:!0===t.marker?.is_light,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 li)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=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&e.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&e.add("entity:"+t.bindingRef));const i=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&i.add(t.name.trim()+"|"+(t.area||""));const s=[];for(const n of Object.values(t.devices)){if("service"===n.entry_type)continue;const t="device:"+n.id;if(e.has(t))continue;const o=(n.name_by_user||n.name||n.id).trim();t!==this._markerDialog?.binding&&i.has(o+"|"+(n.area_id||""))||s.push({value:t,label:o,sub:(n.model||this._t("marker.sub_device"))+("Group"===n.model?this._t("marker.sub_z2m_group"):"")})}const n=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[i,o]of Object.entries(t.entities)){const r="entity:"+i;if(e.has(r))continue;const a=n.has(o.platform),l="group"===o.platform;if(!a&&!l)continue;if(o.hidden)continue;const c=t.states[i];s.push({value:r,label:o.name||c?.attributes?.friendly_name||i,sub:i.split(".")[0]+" · "+("group"===o.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const i=new Set(s.map(t=>t.value));for(const[n,o]of Object.entries(t.entities)){const r="entity:"+n;if(e.has(r)||i.has(r)||o.hidden)continue;const a=t.states[n],l=o.name||a?.attributes?.friendly_name||n,c=o.device_id?t.devices[o.device_id]:null,h=c&&(c.name_by_user||c.name)||"";s.push({value:r,label:l,sub:n.split(".")[0]+" · "+this._t("marker.sub_entity")+(h?" · "+h:"")})}}const o=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),r=o?s.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(o)):s;return r.sort((t,e)=>t.label.localeCompare(e.label)),r.slice(0,200)}_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",n=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const t={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||t.name,url:o.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}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 n=s?.space||null,o=s?.area||null;const r=s?.roomId||null;"virtual"!==t.binding||n||(n=this._space),i=function(t,e,i){const[s,n]=t.split(":");return"device"===s?n:"entity"===s?"lg_"+n:e&&e.startsWith("v_")?e:i()}(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const a=t.devId,l=e.markers.find(t=>t.id===i||t.id===a)?.vacuum||null,c={id:i,vacuum:l,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"badge"!==t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"badge"!==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:t.controls.length?t.controls:null,is_light:!!t.isLight||null,use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=parseFloat(t.glowRadius);return!Number.isFinite(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)&&(c.space=n,c.area=o,c.room_id=r);const h=a?this._devices.find(t=>t.id===a):null,d=h?.marker?.room_id??null,p=!!t.room&&null!=h&&(h.space!==n||h.area!==o||d!==r);let u=!1;const _=t.uploadId||a;if(_&&_!==i&&c.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:_,to_id:i}),e=t?.mapping||{};c.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const n="/files/"+e+"/",o="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(n))return t;const e=t.url.split(n)[1]||"",[i,r]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(n+i)[0]+o+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(n).join(o)}})}(c.pdfs,_,i,e),u=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),e.markers.push(c);let g=null;const m=n||h?.space||this._space,f=a?this._layout[a]:null,v=f?{s:f.s||h?.space||this._space,x:f.x,y:f.y}:a&&h&&this._defPos[a]?this._normPos(h.space,this._defPos[a].x,this._defPos[a].y):null;if(v&&v.s===m)i===a&&this._layout[i]&&!p||(g={s:v.s,x:v.x,y:v.y},this._layout={...this._layout,[i]:g});else if(!this._layout[i]||p){const t=this._spaceModel(n||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):o?t.rooms.find(t=>t.area===o):void 0;a&&([e,s]=this._roomCenter(a)),g=this._normPos(n||this._space,e,s),this._layout={...this._layout,[i]:g}}await this._saveConfigNow(),g&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:i,pos:g})),a&&a!==i&&(delete this._layout[a],await this.hass.callWS({type:"houseplan/layout/delete",device_id:a}).then(t=>this._noteLayoutRev(t)).catch(()=>{})),u&&_&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:_}).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)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null,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||[],e&&"virtual"===e.bindingKind?s.markers=s.markers.filter(t=>t.id!==e.id):e&&e.marker?(s.markers=s.markers.filter(t=>t.id!==e.id),"device"===e.bindingKind&&e.bindingRef?s.markers.push({id:e.id,binding:"device:"+e.bindingRef,hidden:!0}):"entity"===e.bindingKind&&e.bindingRef&&s.markers.push({id:e.id,binding:"entity:"+e.bindingRef,hidden:!0})):e&&"device"===e.bindingKind&&e.bindingRef?s.markers.push({id:e.id,binding:"device:"+e.bindingRef,hidden:!0}):e&&"entity"===e.bindingKind&&e.bindingRef&&s.markers.push({id:e.id,binding:"entity:"+e.bindingRef,hidden:!0});try{await this._saveConfigNow(),e&&"virtual"===e.bindingKind&&this._layout[e.id]&&(delete this._layout[e.id],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e.id}).then(t=>this._noteLayoutRev(t)).catch(()=>{})),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Pr,y:i/Pr}}_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=fi(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:Ss({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:gi,roomOpacity:mi,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 n=new Uint8Array(await i.arrayBuffer());let o="";for(let t=0;t{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=a});URL.revokeObjectURL(a),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:r,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return W`
${this._t("space.loading")}
`;const e=t.saved||[];if(!e.length)return W`
${this._t("space.no_saved")}
`;return W`
+
`:W``}_openingClick(t,e=!1){const i=1.5*this._gridPitch,s=this._openingsR.find(e=>Math.hypot(t[0]-e.rx,t[1]-e.ry)<=Math.max(e.rlen/2,i));if(s)return void this._editOpening(s);const n=De(t,this._spaceModel().rooms,i);if(!n)return void this._showToast(this._t("toast.opening_no_wall"));if(function(t,e,i,s,n){for(const o of s)if(!(Bi([t,e],o)>n)&&Nn([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(n.x,n.y,n.angle,this._openCuts(),i))return void this._showToast(this._t("toast.opening_on_virtual"));const o=this._opRuler(n,this._cmToUnits(90),e);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{Or(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1}}}_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=De(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,n=s?.openings?.find(t=>t.id===e.id);if(!n)return;const o=this._opRuler(i,n.length*Ar,t.shiftKey);this._opMeasure=o.measure;const r=o.x/Ar,a=o.y/this._spaceH;n.x===r&&n.y===a&&n.angle===i.angle||(this._opDrag.dirty=!0),n.x=r,n.y=a,n.angle=i.angle,this.requestUpdate()}_opRuler(t,e,i){const s=this._spaceModel().rooms,n=this._gridPitch/2;let o=t.x,r=t.y,a=Te([o,r],t.angle,e,s,n);if(!a||!a.centered||i||o===a.wallCenter[0]&&r===a.wallCenter[1]){if(a&&!i){const[i,l]=a.wallA,[c,h]=a.wallB,d=c-i,p=h-l,u=Math.hypot(d,p);if(u>0){const c=this._gridPitch,h=Math.min(e/2,u/2);let _=Math.round(((o-i)*d+(r-l)*p)/u/c)*c;_=Math.max(h,Math.min(u-h,_)),o=i+_/u*d,r=l+_/u*p,a=Te([o,r],t.angle,e,s,n)||a}}}else[o,r]=a.wallCenter,a=Te([o,r],t.angle,e,s,n);if(!a)return{x:o,y:r,angle:t.angle,measure:null};const l="mi"===this.hass?.config?.unit_system?.length,c=(t,e)=>({x:e[0],y:e[1],text:$e(t/this._gridPitch*this._cellCm,l)});return{x:o,y:r,angle:t.angle,measure:{labels:[c(a.sideA,a.midA),c(a.sideB,a.midB)],guide:a.centered&&!i?{x:a.wallCenter[0],y:a.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;const i=this._opDrag.moved;this._opMeasure=null,i&&this._opDrag.dirty&&this._saveConfig(),i?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._spaceH,s={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Ar,y:t.y/i,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/Ar,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===s.id);n>=0?e.openings[n]=s:e.openings.push(s),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;t?.id&&e?.openings&&(e.openings=e.openings.filter(e=>e.id!==t.id),this._saveConfig(),this._openingDialog=null,this.requestUpdate())}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&t.push([e,s?.attributes?.friendly_name||e,n?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_mergeClick(t){const e=this._spaceModel().rooms,i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const s=i.id;if(!this._mergeSel||this._mergeSel===s)return void(this._mergeSel=this._mergeSel===s?null:s);const n=e.find(t=>t.id===this._mergeSel),o=n?Se(n):null,r=Se(i),a=o&&r?Ve(o,r):null;if(!a)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:s,poly:a,pick:"a"},this._mergeSel=null}_commitMerge(){const t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._spaceH,s="a"===t.pick?t.aId:t.bId,n="a"===t.pick?t.bId:t.aId,o=e.rooms.find(t=>t.id===s);o?(o.poly=t.poly.map(t=>[t[0]/Ar,t[1]/i]),delete o.x,delete o.y,delete o.w,delete o.h,e.rooms=e.rooms.filter(t=>t.id!==n),this._commitOpenSpans(),this._saveConfig(),this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.rooms_merged",{name:o.name||""}))):this._mergeDialog=null}_splitClick(t,e=!1){const i=this._spaceModel().rooms;if(!this._splitSel){const e=[...i].reverse().find(e=>this._pointInRoom(t,e));if(!e?.id)return;return void(this._splitSel={roomId:e.id,pts:[]})}const s=i.find(t=>t.id===this._splitSel.roomId),n=s?Se(s):null;if(!s||!n)return void(this._splitSel=null);const o=.02*this._gridPitch,r=6*this._gridPitch,a=Ee(t,n),l=a&&!e&&function(t,e,i){let s=null,n=1/0;for(let o=0;o=n)continue;n=_;const g=Math.sqrt(p),m=(i>0?Math.max(0,Math.min(g,Math.round(u*g/i)*i)):u*g)/g;s=[r+m*h,a+m*d]}return s}(a,n,this._gridPitch)||a,c=a&&l&&Math.hypot(a[0]-t[0],a[1]-t[1])<=r?l:null,h=!!c&&Ne(c,n,o),d=this._splitSel.pts;if(!d.length)return h?void(this._splitSel={...this._splitSel,pts:[c]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const i=this._snap(t,e);return Oe(i,n,o)?void(this._splitSel={...this._splitSel,pts:[...d,i]}):void this._showToast(this._t("toast.split_pick_inside"))}const p=function(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(Pe(s,n,i))return null;const o=Ge(t,s,i),r=Ge(t,n,i);if(o<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!Oe(e,t,i))return null;for(let i=0;i{const r=[e];let a=(s+1)%t.length;for(let e=0;e<=t.length&&(r.push(t[a]),a!==o);e++)a=(a+1)%t.length;return r.push(n),Ke(r,i)};let c,h;if(o===r){const r=Ke([...e],i);if(r.length<3||Be(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}c=Ke(a,i),h=r}else c=Ke([...l(s,o,n,r),...[...a].reverse()],i),h=Ke([...l(n,r,s,o),...a],i);return c.length<3||h.length<3||Be(c)<=i||Be(h)<=i||Math.abs(Be(c)+Be(h)-Be(t))>Math.max(i,1e-6*Be(t))?null:[c,h]}(n,[...d,c],o);if(!p)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,g=Be(u)>=Be(_)?u:_,m=g===u?_:u;this._pendingSplit={roomId:s.id,mainPoly:g,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("opening"===this._tool||"openwall"===this._tool||"wallthick"===this._tool)return this._opShift=!!t.shiftKey,void(this._cursorPt=this._svgPoint(t));const e="draw"===this._tool&&this._path.length&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;(e||i)&&(this._cursorPt=this._snap(this._svgPoint(t),t))}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),n=De(t,this._spaceModel().rooms,e);if(!n)return null;const o=this._opRuler(n,s,this._opShift);return{x:o.x,y:o.y,angle:o.angle,rlen:s,measure:o.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._spaceH,i=!!this._pendingSplit;let s;if(this._pendingSplit){const i=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!i)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);i.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Ar,t[1]/e]),delete i.x,delete i.y,delete i.w,delete i.h,s=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;s=this._path.slice(0,-1)}const n=this._areaSel?this.hass.areas[this._areaSel]?.name:"",o={id:"r"+Date.now().toString(36),name:this._nameSel||n||this._t("room.default_name"),area:this._areaSel||null,poly:s.map(t=>[t[0]/Ar,t[1]/e]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(o),i&&this._commitOpenSpans(),!i){const e=this._drawWallCm;if(null!=e){this._cfgEpoch++;const i=this._openCuts();let s=function(t,e,i,s,n,o=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=Gn(e,i,o,n,r);if(!a)return t?t.slice():[];const l=Kn(t,a,n,r);let c=t?t.slice():[];for(let t=0;t0||(c=On(c,e,i,s,n,r))}return c}(t.walls,this._spaceModel().rooms,o.id,e,this._wallKeyPitch,i,Ar);s=this._normalizeWalls(s,i),s.length?t.walls=s:delete t.walls}}this._saveConfig(),this._path=[],this._pendingSplit=null,this._splitSel=null;const r=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let a=0;if(r){const t=Ar,e={...this._layout};for(const i of this._devices){if(i.area!==r||i.space!==this._space)continue;if(a++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/Ar,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const l=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(r?this._t("toast.room_saved",{n:l,added:a}):this._t("toast.room_saved_no_area",{n:l}))}_cancelPath(){this._path=[],this._cursorPt=null,this._roomDialog=!1,this._pendingSplit=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._openWallAnchor=null}_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: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:[...t.marker?.controls||[]],controlsFilter:"",isLight:!0===t.marker?.is_light,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 li)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=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&e.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&e.add("entity:"+t.bindingRef));const i=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&i.add(t.name.trim()+"|"+(t.area||""));const s=[];for(const n of Object.values(t.devices)){if("service"===n.entry_type)continue;const t="device:"+n.id;if(e.has(t))continue;const o=(n.name_by_user||n.name||n.id).trim();t!==this._markerDialog?.binding&&i.has(o+"|"+(n.area_id||""))||s.push({value:t,label:o,sub:(n.model||this._t("marker.sub_device"))+("Group"===n.model?this._t("marker.sub_z2m_group"):"")})}const n=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[i,o]of Object.entries(t.entities)){const r="entity:"+i;if(e.has(r))continue;const a=n.has(o.platform),l="group"===o.platform;if(!a&&!l)continue;if(o.hidden)continue;const c=t.states[i];s.push({value:r,label:o.name||c?.attributes?.friendly_name||i,sub:i.split(".")[0]+" · "+("group"===o.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const i=new Set(s.map(t=>t.value));for(const[n,o]of Object.entries(t.entities)){const r="entity:"+n;if(e.has(r)||i.has(r)||o.hidden)continue;const a=t.states[n],l=o.name||a?.attributes?.friendly_name||n,c=o.device_id?t.devices[o.device_id]:null,h=c&&(c.name_by_user||c.name)||"";s.push({value:r,label:l,sub:n.split(".")[0]+" · "+this._t("marker.sub_entity")+(h?" · "+h:"")})}}const o=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),r=o?s.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(o)):s;return r.sort((t,e)=>t.label.localeCompare(e.label)),r.slice(0,200)}_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",n=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const t={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||t.name,url:o.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}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 n=s?.space||null,o=s?.area||null;const r=s?.roomId||null;"virtual"!==t.binding||n||(n=this._space),i=function(t,e,i){const[s,n]=t.split(":");return"device"===s?n:"entity"===s?"lg_"+n:e&&e.startsWith("v_")?e:i()}(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const a=t.devId,l=e.markers.find(t=>t.id===i||t.id===a)?.vacuum||null,c={id:i,vacuum:l,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"badge"!==t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"badge"!==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:t.controls.length?t.controls:null,is_light:!!t.isLight||null,use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=parseFloat(t.glowRadius);return!Number.isFinite(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)&&(c.space=n,c.area=o,c.room_id=r);const h=a?this._devices.find(t=>t.id===a):null,d=h?.marker?.room_id??null,p=!!t.room&&null!=h&&(h.space!==n||h.area!==o||d!==r);let u=!1;const _=t.uploadId||a;if(_&&_!==i&&c.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:_,to_id:i}),e=t?.mapping||{};c.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const n="/files/"+e+"/",o="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(n))return t;const e=t.url.split(n)[1]||"",[i,r]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(n+i)[0]+o+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(n).join(o)}})}(c.pdfs,_,i,e),u=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),e.markers.push(c);let g=null;const m=n||h?.space||this._space,f=a?this._layout[a]:null,v=f?{s:f.s||h?.space||this._space,x:f.x,y:f.y}:a&&h&&this._defPos[a]?this._normPos(h.space,this._defPos[a].x,this._defPos[a].y):null;if(v&&v.s===m)i===a&&this._layout[i]&&!p||(g={s:v.s,x:v.x,y:v.y},this._layout={...this._layout,[i]:g});else if(!this._layout[i]||p){const t=this._spaceModel(n||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):o?t.rooms.find(t=>t.area===o):void 0;a&&([e,s]=this._roomCenter(a)),g=this._normPos(n||this._space,e,s),this._layout={...this._layout,[i]:g}}await this._saveConfigNow(),g&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:i,pos:g})),a&&a!==i&&(delete this._layout[a],await this.hass.callWS({type:"houseplan/layout/delete",device_id:a}).then(t=>this._noteLayoutRev(t)).catch(()=>{})),u&&_&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:_}).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)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null,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||[],e&&"virtual"===e.bindingKind?s.markers=s.markers.filter(t=>t.id!==e.id):e&&e.marker?(s.markers=s.markers.filter(t=>t.id!==e.id),"device"===e.bindingKind&&e.bindingRef?s.markers.push({id:e.id,binding:"device:"+e.bindingRef,hidden:!0}):"entity"===e.bindingKind&&e.bindingRef&&s.markers.push({id:e.id,binding:"entity:"+e.bindingRef,hidden:!0})):e&&"device"===e.bindingKind&&e.bindingRef?s.markers.push({id:e.id,binding:"device:"+e.bindingRef,hidden:!0}):e&&"entity"===e.bindingKind&&e.bindingRef&&s.markers.push({id:e.id,binding:"entity:"+e.bindingRef,hidden:!0});try{await this._saveConfigNow(),e&&"virtual"===e.bindingKind&&this._layout[e.id]&&(delete this._layout[e.id],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e.id}).then(t=>this._noteLayoutRev(t)).catch(()=>{})),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Ar,y:i/Ar}}_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=fi(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:Ss({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:gi,roomOpacity:mi,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 n=new Uint8Array(await i.arrayBuffer());let o="";for(let t=0;t{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=a});URL.revokeObjectURL(a),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:r,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return W`
${this._t("space.loading")}
`;const e=t.saved||[];if(!e.length)return W`
${this._t("space.no_saved")}
`;return W`
${e.map(e=>W`
@@ -2858,7 +2858,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
- `}_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 Ss(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Ms(this._sunGlobal(),this._sunSpace())}_effSunRays(){return Cs(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Ts(this.hass):null}_renderSunRays(t){const e=B``;if(this._editing||!this._effSunRays())return this._sunFadeReset(),e;const i=this._effNorth(),s=null!==i?Ts(this.hass):null;if(!s||s.elevation<=0)return this._sunFadeReset(),e;const n=Ds(this._sunGlobal()),o=function(t){if(!t)return 1;const e=$s[String(t).toLowerCase()];return void 0===e?1:e}(n?this.hass?.states?.[n]?.state:null),r=function(t=1){return.3*gs(t)}(o);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:Se(t)})).filter(t=>!!t.id&&!!t.poly),n=this._openingsR.filter(t=>"window"===t.type).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),o=this._spaceWalls,r=this._openPairs().flatMap(t=>t.segs),a={},c={};if(o.length){for(const i of e){const e=Qn(t.rooms,i.id,o,r,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr);e&&(a[i.id]=e)}for(const e of n){const i=no(t.rooms,{x:e.x,y:e.y,angle:e.angle,length:e.length},o,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr);i.cm>0&&(c[e.id]=xn(i.cm,this._cellCm,this._gridPitch))}}const h=ws(e,n,s.azimuth,s.elevation,i,o.length?a:void 0,o.length?c:void 0);this._sunRaysCache={key:l,rays:h,rims:h.map(t=>function(t,e=1e-4){const[i,s]=t.dir,n=-s,o=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((h[0]-a[0])*n+(h[1]-a[1])*o)>e)continue;const d=(c[0]-a[0])*i+(c[1]-a[1])*s,p=(h[0]-a[0])*i+(h[1]-a[1])*s;Math.abs(p-d)<=e||l.push(dt[0]-e[0]);const c=[];for(const t of l){const i=c[c.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):c.push([t[0],t[1]])}for(const[t,e]of c)r.push([[a[0]+i*t,a[1]+s*t],[a[0]+i*e,a[1]+s*e]])}return r}(t))}}const c=this._sunRaysCache.rays,h=this._sunRaysCache.rims;if(!c.length)return e;const d=(p=ms(s.elevation).warmth,ki("#ffe9c2","#ff9a45",gs(p)));var p;const u=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],_=function(t=1){return.42*gs(t)}(o),g=[[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 Ss(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Ms(this._sunGlobal(),this._sunSpace())}_effSunRays(){return Cs(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Ts(this.hass):null}_renderSunRays(t){const e=B``;if(this._editing||!this._effSunRays())return this._sunFadeReset(),e;const i=this._effNorth(),s=null!==i?Ts(this.hass):null;if(!s||s.elevation<=0)return this._sunFadeReset(),e;const n=Ds(this._sunGlobal()),o=function(t){if(!t)return 1;const e=$s[String(t).toLowerCase()];return void 0===e?1:e}(n?this.hass?.states?.[n]?.state:null),r=function(t=1){return.3*gs(t)}(o);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:Se(t)})).filter(t=>!!t.id&&!!t.poly),n=this._openingsR.filter(t=>"window"===t.type).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),o=this._spaceWalls,r=this._openPairs().flatMap(t=>t.segs),a={},c={};if(o.length){for(const i of e){const e=Qn(t.rooms,i.id,o,r,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar);e&&(a[i.id]=e)}for(const e of n){const i=no(t.rooms,{x:e.x,y:e.y,angle:e.angle,length:e.length},o,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar);i.cm>0&&(c[e.id]=xn(i.cm,this._cellCm,this._gridPitch))}}const h=ws(e,n,s.azimuth,s.elevation,i,o.length?a:void 0,o.length?c:void 0);this._sunRaysCache={key:l,rays:h,rims:h.map(t=>function(t,e=1e-4){const[i,s]=t.dir,n=-s,o=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((h[0]-a[0])*n+(h[1]-a[1])*o)>e)continue;const d=(c[0]-a[0])*i+(c[1]-a[1])*s,p=(h[0]-a[0])*i+(h[1]-a[1])*s;Math.abs(p-d)<=e||l.push(dt[0]-e[0]);const c=[];for(const t of l){const i=c[c.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):c.push([t[0],t[1]])}for(const[t,e]of c)r.push([[a[0]+i*t,a[1]+s*t],[a[0]+i*e,a[1]+s*e]])}return r}(t))}}const c=this._sunRaysCache.rays,h=this._sunRaysCache.rims;if(!c.length)return e;const d=(p=ms(s.elevation).warmth,ki("#ffe9c2","#ff9a45",gs(p)));var p;const u=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],_=function(t=1){return.42*gs(t)}(o),g=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]];return B` ${c.map((t,e)=>{const i=(t.a[0]+t.b[0])/2,s=(t.a[1]+t.b[1])/2,n=i+t.normal[0]*t.depth,o=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 ms(ks(t.elevation)).bg}const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":vi(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._serverCfg={...this._serverCfg,spaces:t.spaces},this._layout=t.layout;for(const e of Object.keys(t.layout))this._dirtyPos.add(e);this._modelCache=null,this._frame=null,await this._saveConfigNow(),await this._persistLayoutNow(),this._alignDialog=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}async _persistLayoutNow(){if(!this._serverStorage)return localStorage.setItem(Mr,JSON.stringify(this._layout)),void this._dirtyPos.clear();const t=[...this._dirtyPos];this._dirtyPos.clear(),await Promise.all(t.map(t=>{const e=this._layout[t];return e?(this._sentPos.set(t,e),this.hass.callWS({type:"houseplan/layout/update",device_id:t,pos:e}).then(t=>this._noteLayoutRev(t)).finally(()=>{this._sentPos.get(t)===e&&this._sentPos.delete(t)})):Promise.resolve()})),this._cacheSnapshot()}_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(bi),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const n=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(n)&&n>0&&300!==Math.round(n)?s.glow_radius_cm=Math.round(n):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 o=(t.weatherEntity||"").trim();o?s.weather_entity=o: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,n){const o=t=>{const e=Number(t.target.value);Number.isFinite(e)&&n(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 ms(ks(t.elevation)).bg}const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":vi(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._serverCfg={...this._serverCfg,spaces:t.spaces},this._layout=t.layout;for(const e of Object.keys(t.layout))this._dirtyPos.add(e);this._modelCache=null,this._frame=null,await this._saveConfigNow(),await this._persistLayoutNow(),this._alignDialog=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}async _persistLayoutNow(){if(!this._serverStorage)return localStorage.setItem(Cr,JSON.stringify(this._layout)),void this._dirtyPos.clear();const t=[...this._dirtyPos];this._dirtyPos.clear(),await Promise.all(t.map(t=>{const e=this._layout[t];return e?(this._sentPos.set(t,e),this.hass.callWS({type:"houseplan/layout/update",device_id:t,pos:e}).then(t=>this._noteLayoutRev(t)).finally(()=>{this._sentPos.get(t)===e&&this._sentPos.delete(t)})):Promise.resolve()})),this._cacheSnapshot()}_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(bi),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const n=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(n)&&n>0&&300!==Math.round(n)?s.glow_radius_cm=Math.round(n):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 o=(t.weatherEntity||"").trim();o?s.weather_entity=o: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,n){const o=t=>{const e=Number(t.target.value);Number.isFinite(e)&&n(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,n=t.rooms.map(t=>({r:t,poly:Se(t)})).filter(t=>!!t.poly),o=this._openingsR.filter(t=>"door"===t.type),r=[];for(const a of this._devices){if(a.space!==t.id)continue;if(a.hidden)continue;const l=Lo(this.hass,a);if(!l)continue;const c=Ti(this.hass.states[l],e.glow_light.c);if(!c)continue;const h=Number(a.marker?.glow_radius_cm),d=Number.isFinite(h)&&h>0?h/this._cellCm*this._gridPitch:i,p=this._pos(a),u=[...n].reverse().find(t=>this._pointInRoom([p.x,p.y],t.r));let _=null;if(u){const e=u.r.id?Ei(u.r.id,t.rooms):new Set([u.r.id]),i=n.filter(t=>t.r.id&&e.has(t.r.id)),r=i.length?i:[u],a=this._spaceWalls,l=this._openPairs().flatMap(t=>t.segs),c=r.map(e=>"M "+(a.length&&e.r.id&&Qn(t.rooms,e.r.id,a,l,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)||e.poly).map(t=>t[0]+" "+t[1]).join(" L ")+" Z"),h=n.filter(t=>!r.includes(t)).map(t=>t.poly);for(const t of o){const e=r.some(e=>{const i=Ee([t.rx,t.ry],e.poly);return i&&Math.hypot(i[0]-t.rx,i[1]-t.ry)<=.75*s});if(!e)continue;const i=t.angle*Math.PI/180,n=Math.cos(i)*t.rlen/2,o=Math.sin(i)*t.rlen/2;if(!Pi([t.rx,t.ry],t.angle,[p.x,p.y],h,.6*s))continue;const a=zi([p.x,p.y],[t.rx-n,t.ry-o],[t.rx+n,t.ry+o],d);a&&c.push("M "+a.map(t=>t[0]+" "+t[1]).join(" L ")+" Z")}_=c}r.push({pos:p,c:c.c,alpha:e.glow_light.a*c.bri,clip:_,r:d})}return r.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,n=t.rooms.map(t=>({r:t,poly:Se(t)})).filter(t=>!!t.poly),o=this._openingsR.filter(t=>"door"===t.type),r=[];for(const a of this._devices){if(a.space!==t.id)continue;if(a.hidden)continue;const l=qo(this.hass,a);if(!l)continue;const c=Ti(this.hass.states[l],e.glow_light.c);if(!c)continue;const h=Number(a.marker?.glow_radius_cm),d=Number.isFinite(h)&&h>0?h/this._cellCm*this._gridPitch:i,p=this._pos(a),u=[...n].reverse().find(t=>this._pointInRoom([p.x,p.y],t.r));let _=null;if(u){const e=u.r.id?Ei(u.r.id,t.rooms):new Set([u.r.id]),i=n.filter(t=>t.r.id&&e.has(t.r.id)),r=i.length?i:[u],a=this._spaceWalls,l=this._openPairs().flatMap(t=>t.segs),c=r.map(e=>"M "+(a.length&&e.r.id&&Qn(t.rooms,e.r.id,a,l,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)||e.poly).map(t=>t[0]+" "+t[1]).join(" L ")+" Z"),h=n.filter(t=>!r.includes(t)).map(t=>t.poly);for(const t of o){const e=r.some(e=>{const i=Ee([t.rx,t.ry],e.poly);return i&&Math.hypot(i[0]-t.rx,i[1]-t.ry)<=.75*s});if(!e)continue;const i=t.angle*Math.PI/180,n=Math.cos(i)*t.rlen/2,o=Math.sin(i)*t.rlen/2;if(!Pi([t.rx,t.ry],t.angle,[p.x,p.y],h,.6*s))continue;const a=zi([p.x,p.y],[t.rx-n,t.ry-o],[t.rx+n,t.ry+o],d);a&&c.push("M "+a.map(t=>t[0]+" "+t[1]).join(" L ")+" Z")}_=c}r.push({pos:p,c:c.c,alpha:e.glow_light.a*c.bri,clip:_,r:d})}return r.length?B` ${r.map((t,e)=>B` @@ -3009,7 +3009,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow -
${this._t("gs.about_version",{v:yr})}
+
${this._t("gs.about_version",{v:wr})}
${this._t("gs.about_github")} @@ -3069,7 +3069,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow - `}_saveKioskScale(t){this._kioskScale={...this._kioskScale,...t};try{localStorage.setItem(zr,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(Pr,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])}% @@ -3172,7 +3172,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${e.bg&&this._display(e.bg.href)?B``:V} ${""} ${o.hideDecor&&"decor"!==this._mode?V:this._renderDecorLayer()} - ${(()=>{const t=this._openPairs(),i=new Map,s=t=>(i.has(t)||i.set(t,Se(t)),i.get(t));return e.rooms.filter(t=>t.area||this._markup||o.showBorders).map(i=>{let n="room "+(e.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||i.id!==this._mergeSel&&i.id!==this._splitSel?.roomId||(n+=" picked");let a="";const l=Hi(o.fill,i);if(!this._markup&&(o.showBorders||"none"!==l)){n+=" styled";const t=[];t.push(`--room-stroke:${o.color}`,`--room-stroke-op:${o.showBorders?o.opacity:0}`);const e="glow"===l?this._fillColors.glow_base:"temp"===l?$i("temp",null,"none",this._roomTemp(i),o.tempMin,o.tempMax,this._fillColors):i.area?$i(l,"lqi"===l?this._roomLqi(i.area):null,"light"===l?Yo(this.hass,this._devices,i.area):"none",null,o.tempMin,o.tempMax,this._fillColors):null;e?(n+=" 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(";")}const c=t=>this._showTip(t,i.name,"",r?this._roomLqi(i.area):null,this._roomTemp(i)),h=!e.bg&&!o.showNames&&!this._markup,d=this._roomCenter(i),p=this._markup&&(i.id===this._mergeSel||i.id===this._splitSel?.roomId),u=i.id&&!p?t.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):[],_=p?[]:this._thickWallCuts(),g=u.concat(_);g.length&&(n+=" noedge");const m=s(i),f=this._spaceWalls,v=f.length&&i.id&&m&&Qn(e.rooms,i.id,f,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,Pr)||m,b=v?function(t,e,i=1e-6){const s=e.filter(e=>Ue(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ue(e,t,i)))}(v,(y=i,e.rooms.filter(t=>t!==y).map(s).filter(Boolean))):[];var y;const w=i.id||V,k=i.area||V,$=b.length&&v?B`{const t=this._openPairs(),i=new Map,s=t=>(i.has(t)||i.set(t,Se(t)),i.get(t));return e.rooms.filter(t=>t.area||this._markup||o.showBorders).map(i=>{let n="room "+(e.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||i.id!==this._mergeSel&&i.id!==this._splitSel?.roomId||(n+=" picked");let a="";const l=qi(o.fill,i);if(!this._markup&&(o.showBorders||"none"!==l)){n+=" styled";const t=[];t.push(`--room-stroke:${o.color}`,`--room-stroke-op:${o.showBorders?o.opacity:0}`);const e="glow"===l?this._fillColors.glow_base:"temp"===l?$i("temp",null,"none",this._roomTemp(i),o.tempMin,o.tempMax,this._fillColors):i.area?$i(l,"lqi"===l?this._roomLqi(i.area):null,"light"===l?Xo(this.hass,this._devices,i.area):"none",null,o.tempMin,o.tempMax,this._fillColors):null;e?(n+=" 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(";")}const c=t=>this._showTip(t,i.name,"",r?this._roomLqi(i.area):null,this._roomTemp(i)),h=!e.bg&&!o.showNames&&!this._markup,d=this._roomCenter(i),p=this._markup&&(i.id===this._mergeSel||i.id===this._splitSel?.roomId),u=i.id&&!p?t.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):[],_=p?[]:this._thickWallCuts(),g=u.concat(_);g.length&&(n+=" noedge");const m=s(i),f=this._spaceWalls,v=f.length&&i.id&&m&&Qn(e.rooms,i.id,f,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,Ar)||m,b=v?function(t,e,i=1e-6){const s=e.filter(e=>Ue(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ue(e,t,i)))}(v,(y=i,e.rooms.filter(t=>t!==y).map(s).filter(Boolean))):[];var y;const w=i.id||V,k=i.area||V,$=b.length&&v?B`t[0]+" "+t[1]).join(" L ")+" Z").join(" ")}" @mousemove=${c} @@ -3193,12 +3193,13 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow x="${d[0]}" y="${d[1]}">${i.name}`:V}`})})()} ${"glow"!==o.fill||this._markup?V:this._renderGlowLayer(e)} ${this._renderSunRays(e)} - ${this._renderOpenWalls(o)} ${this._editing?this._renderAlignGuides():V} ${u?.guide?this._renderOpeningCenterTick(u.guide):V} ${this._markup?this._renderMarkupLayer(i):V} ${""} ${this._renderWallBodies(o)} + ${""} + ${this._renderOpenWalls(o)} ${o.hideOpenings&&!this._markup?V:this._renderOpenings(o)} ${this._renderWallThickUi()} ${this._markup&&"resize"===this._tool?this._renderResizeLayer(c):V} @@ -3284,7 +3285,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
`:V} ${this._toast?W`
${this._toast}
`:V} - `}_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(Mo(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)}_senseTick(){if(this.hass)for(const t of this._devices){if(t.hidden||!t.primary||!t.primary.startsWith("binary_sensor."))continue;const e=this.hass.states[t.primary];if("motion"!==e?.attributes?.device_class)continue;const i=this._senseRt.get(t.id);i?("on"===e.state&&"off"===i.last&&(i.flashTs=Date.now(),i.gen++,clearTimeout(i.timer),i.timer=window.setTimeout(()=>this.requestUpdate(),3360)),i.last=e.state):this._senseRt.set(t.id,{last:e.state,flashTs:0,timer:0,gen:0})}}_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=zo(this.hass.states[i||""]?.state),n=So(this.hass.states[e]?.attributes);let o=this._vacRt.get(t.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(t.id,o)),s&&!o.moving&&(o.trail=[],o.lastPos=null);const r="never"!==Eo(t.marker?.vacuum)&&!n?.path;!s&&o.moving&&(o.endedTs=Date.now(),r&&o.lastPos&&(o.trail=To(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=s;const a=n?.pos;if(s&&a){const t=a.x+":"+a.y;if(t!==o.lastKey){const e=Date.now();o.jump=o.lastTs>0&&e-o.lastTs>1e4,o.lastKey=t,o.lastTs=e,r&&o.lastPos&&(o.trail=To(o.trail,o.lastPos,40)),o.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 V;const i=e.marker?.vacuum||{},s=this._vacSource(e),n=s?So(this.hass?.states[s]?.attributes):null,o=!!(n&&n.rooms.length>=3),r=n?.pos?_i(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(Co(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)}_senseTick(){if(this.hass)for(const t of this._devices){if(t.hidden||!t.primary||!t.primary.startsWith("binary_sensor."))continue;const e=this.hass.states[t.primary];if("motion"!==e?.attributes?.device_class)continue;const i=this._senseRt.get(t.id);i?("on"===e.state&&"off"===i.last&&(i.flashTs=Date.now(),i.gen++,clearTimeout(i.timer),i.timer=window.setTimeout(()=>this.requestUpdate(),3360)),i.last=e.state):this._senseRt.set(t.id,{last:e.state,flashTs:0,timer:0,gen:0})}}_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=Po(this.hass.states[i||""]?.state),n=Mo(this.hass.states[e]?.attributes);let o=this._vacRt.get(t.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(t.id,o)),s&&!o.moving&&(o.trail=[],o.lastPos=null);const r="never"!==No(t.marker?.vacuum)&&!n?.path;!s&&o.moving&&(o.endedTs=Date.now(),r&&o.lastPos&&(o.trail=zo(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=s;const a=n?.pos;if(s&&a){const t=a.x+":"+a.y;if(t!==o.lastKey){const e=Date.now();o.jump=o.lastTs>0&&e-o.lastTs>1e4,o.lastKey=t,o.lastTs=e,r&&o.lastPos&&(o.trail=zo(o.trail,o.lastPos,40)),o.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 V;const i=e.marker?.vacuum||{},s=this._vacSource(e),n=s?Mo(this.hass?.states[s]?.attributes):null,o=!!(n&&n.rooms.length>=3),r=n?.pos?_i(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}
@@ -3301,19 +3302,19 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${a.length?W`
${_i(this._t("vac.cal_maps"),{maps:a.join(", ")})}
`:V} `:V} -
`}_vacMapId(t,e){const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return n=e.mapId,o=s,"default"!==n?n:null!=o?String(o):"default";var n,o}_vacSaveMatrix(t,e,i,s){const n=this._devices.find(e=>e.id===t),o=n?this._vacEnsureMarker(n):this._serverCfg?.markers?.find(e=>e.id===t);if(!o)return!1;const r={...o.vacuum||{}};return r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},o.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate(),!0}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?So(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),n=(s?.rooms||[]).map(t=>({r:t,poly:Se(t)})).filter(({r:t,poly:e})=>t.name&&e).map(({r:t,poly:e})=>{const i=Le(e);return{name:t.name,cx:i[0],cy:i[1]}}),o=Do(i.rooms,n);o?this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),o.matrix)&&(o.residual>50&&this._showToast(_i(this._t("vac.autocal_res_warn"),{rooms:String(o.matched.length)})),this._showToast(_i(this._t("vac.autocal_done"),{rooms:String(o.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartFit(t){const e=this._vacSource(t),i=e?So(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),n=t.marker?.vacuum?.calibration?.[s],o=this._spaceModel(t.space),r=o?.vb||[0,0,Pr,Pr],a=n&&6===n.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 n=180*Math.atan2(-t[1],t[4])/Math.PI;return n=(90*Math.round(n/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:n,mir:i}}(n)||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 n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1,c={ox:0,oy:0,s:.6*Math.min(e[2],e[3])/l,rot:0,mir:!0},h=Ao(c),[d,p]=ko(h,(n+o)/2,(r+a)/2);return c.ox=e[0]+e[2]/2-d,c.oy=e[1]+e[3]/2-p,c}(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,Ao(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=So(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),n={...e.p,...t};this._vacFit={...e,p:Ro(n,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,n=s?.clientWidth||1,o=s?.clientHeight||1;return[e/n*t.w,i/o*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[n,o]=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+n,oy:s.p0.oy+o}};else{const t=So(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),r=Ao(s.p0),[a,l]=ko(r,e[0],e[1]),[c,h]=ko(r,s.fx,s.fy),d=Math.hypot(a-c,l-h)||1,[p,u]=[2*a-c,2*l-h],_=Math.hypot(p+2*n-c,u+2*o-h)/2,g=Math.max(.05,_/d),m={...s.p0,s:s.p0.s*g};this._vacFit={...i,p:Ro(m,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 V;const i=So(this.hass?.states[e.source]?.attributes);if(!i)return V;const s=Ao(e.p),n=[],o=[],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])=>ko(s,t,e));e.forEach(([t,e])=>{o.push(t),r.push(e)});const[i,a]=ko(s,t.cx,t.cy);n.push(B` - ${t.name}`)}let a=V;if(i.pos){const[e,n]=ko(s,i.pos.x,i.pos.y);a=B``}const l=[];if(o.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(...o),n=Math.max(...o),a=Math.min(...r),c=Math.max(...r),h=.022*t.w,d=h/4;for(const[t,s,o,r]of[[i,a,n,c],[n,a,i,c],[n,c,i,a],[i,c,n,a]]){const i=e(o,r);l.push(B``}_vacMapId(t,e){const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return n=e.mapId,o=s,"default"!==n?n:null!=o?String(o):"default";var n,o}_vacSaveMatrix(t,e,i,s){const n=this._devices.find(e=>e.id===t),o=n?this._vacEnsureMarker(n):this._serverCfg?.markers?.find(e=>e.id===t);if(!o)return!1;const r={...o.vacuum||{}};return r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},o.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate(),!0}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?Mo(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),n=(s?.rooms||[]).map(t=>({r:t,poly:Se(t)})).filter(({r:t,poly:e})=>t.name&&e).map(({r:t,poly:e})=>{const i=Le(e);return{name:t.name,cx:i[0],cy:i[1]}}),o=To(i.rooms,n);o?this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),o.matrix)&&(o.residual>50&&this._showToast(_i(this._t("vac.autocal_res_warn"),{rooms:String(o.matched.length)})),this._showToast(_i(this._t("vac.autocal_done"),{rooms:String(o.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartFit(t){const e=this._vacSource(t),i=e?Mo(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),n=t.marker?.vacuum?.calibration?.[s],o=this._spaceModel(t.space),r=o?.vb||[0,0,Ar,Ar],a=n&&6===n.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 n=180*Math.atan2(-t[1],t[4])/Math.PI;return n=(90*Math.round(n/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:n,mir:i}}(n)||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 n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1,c={ox:0,oy:0,s:.6*Math.min(e[2],e[3])/l,rot:0,mir:!0},h=Ro(c),[d,p]=$o(h,(n+o)/2,(r+a)/2);return c.ox=e[0]+e[2]/2-d,c.oy=e[1]+e[3]/2-p,c}(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,Ro(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=Mo(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),n={...e.p,...t};this._vacFit={...e,p:Eo(n,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,n=s?.clientWidth||1,o=s?.clientHeight||1;return[e/n*t.w,i/o*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[n,o]=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+n,oy:s.p0.oy+o}};else{const t=Mo(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),r=Ro(s.p0),[a,l]=$o(r,e[0],e[1]),[c,h]=$o(r,s.fx,s.fy),d=Math.hypot(a-c,l-h)||1,[p,u]=[2*a-c,2*l-h],_=Math.hypot(p+2*n-c,u+2*o-h)/2,g=Math.max(.05,_/d),m={...s.p0,s:s.p0.s*g};this._vacFit={...i,p:Eo(m,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 V;const i=Mo(this.hass?.states[e.source]?.attributes);if(!i)return V;const s=Ro(e.p),n=[],o=[],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])=>$o(s,t,e));e.forEach(([t,e])=>{o.push(t),r.push(e)});const[i,a]=$o(s,t.cx,t.cy);n.push(B` + ${t.name}`)}let a=V;if(i.pos){const[e,n]=$o(s,i.pos.x,i.pos.y);a=B``}const l=[];if(o.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(...o),n=Math.max(...o),a=Math.min(...r),c=Math.max(...r),h=.022*t.w,d=h/4;for(const[t,s,o,r]of[[i,a,n,c],[n,a,i,c],[n,c,i,a],[i,c,n,a]]){const i=e(o,r);l.push(B` `)}}return W`this._vacFitPointer(e,t)} @pointermove=${e=>this._vacFitPointer(e,t)} @pointerup=${e=>this._vacFitPointer(e,t)} - @pointercancel=${e=>this._vacFitPointer(e,t)}>${n}${a}${l}`}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const t=this.renderRoot,e=this._stageEl,i=this._vacLastView,s=t?.querySelectorAll?.(".vacpuck")||[];if(!e||!i||!s.length)return void(this._vacRaf=0);const n=e.getBoundingClientRect();for(const e of s){const s=e.getAttribute("data-mid"),o=e.getBoundingClientRect(),r=i.x+(o.left+o.width/2-n.left)/n.width*i.w,a=i.y+(o.top+o.height/2-n.top)/n.height*i.h;for(const e of t.querySelectorAll(`line.tip[data-mid="${s}"]`))e.setAttribute("x2",r.toFixed(1)),e.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return V;const i=this._space+"|"+e.x+"|"+e.y+"|"+e.w+"|"+e.h,s=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const n=[],o=[];for(const i of t){if(i.hidden||!this._isVacDev(i))continue;const t=this._vacSource(i);if(!t)continue;const r=So(this.hass?.states[t]?.attributes);if(!r)continue;const a=i.marker?.vacuum?.calibration?.[this._vacMapId(i,r)];if(!a||6!==a.length)continue;const l=this._vacRt.get(i.id),c=l?.moving??!1,h=Eo(i.marker?.vacuum),d="always"===h||"cleaning"===h&&c,p=this._vacSrvTrails[i.id],u=this._vacMapId(i,r),_=p?.current?.map_id===u&&Array.isArray(p.current.points)?p.current:null,g=p?.previous?.map_id===u&&Array.isArray(p.previous.points)?p.previous:null;if("always"===h&&g&&g.points.length>1){const t=g.points.map(([t,e])=>{const[i,s]=ko(a,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");o.push(B``)}if(d&&(c||_)){const t=_?.points||r.path||l?.trail||[],e=c&&(_||r.path)&&t.length>1?t.slice(0,-1):t;if(e.length>1){const t=e.map(([t,e])=>{const[i,s]=ko(a,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");if(o.push(B``),c){const[t,s]=ko(a,e[e.length-1][0],e[e.length-1][1]),n=t.toFixed(1),r=s.toFixed(1);o.push(B``)}}}if(!c||!r.pos)continue;const[m,f]=ko(a,r.pos.x,r.pos.y),v=(m-e.x)/e.w*100,b=(f-e.y)/e.h*100,y=l&&l.lastTs>0&&Date.now()-l.lastTs>6e4,w=i.marker?.icon||i.icon||"mdi:robot-vacuum";n.push(W`
this._vacFitPointer(e,t)}>${n}${a}${l}`}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const t=this.renderRoot,e=this._stageEl,i=this._vacLastView,s=t?.querySelectorAll?.(".vacpuck")||[];if(!e||!i||!s.length)return void(this._vacRaf=0);const n=e.getBoundingClientRect();for(const e of s){const s=e.getAttribute("data-mid"),o=e.getBoundingClientRect(),r=i.x+(o.left+o.width/2-n.left)/n.width*i.w,a=i.y+(o.top+o.height/2-n.top)/n.height*i.h;for(const e of t.querySelectorAll(`line.tip[data-mid="${s}"]`))e.setAttribute("x2",r.toFixed(1)),e.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return V;const i=this._space+"|"+e.x+"|"+e.y+"|"+e.w+"|"+e.h,s=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const n=[],o=[];for(const i of t){if(i.hidden||!this._isVacDev(i))continue;const t=this._vacSource(i);if(!t)continue;const r=Mo(this.hass?.states[t]?.attributes);if(!r)continue;const a=i.marker?.vacuum?.calibration?.[this._vacMapId(i,r)];if(!a||6!==a.length)continue;const l=this._vacRt.get(i.id),c=l?.moving??!1,h=No(i.marker?.vacuum),d="always"===h||"cleaning"===h&&c,p=this._vacSrvTrails[i.id],u=this._vacMapId(i,r),_=p?.current?.map_id===u&&Array.isArray(p.current.points)?p.current:null,g=p?.previous?.map_id===u&&Array.isArray(p.previous.points)?p.previous:null;if("always"===h&&g&&g.points.length>1){const t=g.points.map(([t,e])=>{const[i,s]=$o(a,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");o.push(B``)}if(d&&(c||_)){const t=_?.points||r.path||l?.trail||[],e=c&&(_||r.path)&&t.length>1?t.slice(0,-1):t;if(e.length>1){const t=e.map(([t,e])=>{const[i,s]=$o(a,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");if(o.push(B``),c){const[t,s]=$o(a,e[e.length-1][0],e[e.length-1][1]),n=t.toFixed(1),r=s.toFixed(1);o.push(B``)}}}if(!c||!r.pos)continue;const[m,f]=$o(a,r.pos.x,r.pos.y),v=(m-e.x)/e.w*100,b=(f-e.y)/e.h*100,y=l&&l.lastTs>0&&Date.now()-l.lastTs>6e4,w=i.marker?.icon||i.icon||"mdi:robot-vacuum";n.push(W`
`)}return this._vacLastView=e,n.length&&!this._vacRaf&&this._vacRafLoop(),n.length||o.length?W` ${o.length?B`${o}`:V} - ${n}`:V}_renderDevice(t,e,i=!0,s=!1){const n=this._pos(t),o=(n.x-e.x)/e.w*100,r=(n.y-e.y)/e.h*100;let a=t.hidden?"":this._stateClass(t);s&&"on"===a&&Lo(this.hass,t)&&(a="");const l=t.hidden?null:this._liveTemp(t),c=t.hidden?null:this._liveHum(t),h=!i||t.virtual||t.hidden?null:Ho(this.hass,t.entities),d=t.marker,p=d?.display||"badge",u=("ripple"===p||"icon_ripple"===p)&&!t.hidden,_=this._actEntity(t),g=_?this.hass.states[_]:void 0,m="value"!==p||t.hidden||null!=l||null!=c||!g||isNaN(parseFloat(g.state))?null:ci(this.hass,_),f="value"!==p||t.hidden?null:null!=l?l+"°":null!=c?c+"%":m?m.formatted?hi(m,g.attributes?.unit_of_measurement):parseFloat(g.state)+(g.attributes?.unit_of_measurement?" "+g.attributes.unit_of_measurement:""):null,v=_?_.split(".")[0]:null,b=this._config?.live_states&&!t.hidden?Ci(t.icon,v,g?.attributes?.device_class,g?.state,!!d?.icon):t.icon,y=(d?.controls||[]).filter(Ai),w=this._config?.live_states&&!t.hidden?y.length?y.map(t=>Di(this.hass.states[t])).find(t=>t)||null:"light"===v?Di(g):null:null,k=this._config?.live_states&&!t.hidden&&function(t,e,i){return"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&ji.has(e))}(v,g?.attributes?.device_class,g?.state),$=u&&!t.hidden&&!!_&&ze(this.hass.states[_]?.state),x=Number(d?.size)>0?Number(d.size):1,S=Number(d?.angle)||0,M=Number(d?.ripple_size)>0?Number(d.ripple_size):3,C=[`left:${o}%`,`top:${r}%`];return 1!==x&&C.push(`--dev-scale:${x}`),u&&(C.push(`--ripple-scale:${M}`),d?.ripple_color?C.push(`--ripple-color:${d.ripple_color}`):w&&C.push(`--ripple-color:${w}`)),W`
Di(this.hass.states[t])).find(t=>t)||null:"light"===v?Di(g):null:null,k=this._config?.live_states&&!t.hidden&&function(t,e,i){return"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&ji.has(e))}(v,g?.attributes?.device_class,g?.state),$=u&&!t.hidden&&!!_&&ze(this.hass.states[_]?.state),x=Number(d?.size)>0?Number(d.size):1,S=Number(d?.angle)||0,M=Number(d?.ripple_size)>0?Number(d.ripple_size):3,C=[`left:${o}%`,`top:${r}%`];return 1!==x&&C.push(`--dev-scale:${x}`),u&&(C.push(`--ripple-scale:${M}`),d?.ripple_color?C.push(`--ripple-color:${d.ripple_color}`):w&&C.push(`--ripple-color:${w}`)),W`
${l}°`:V} ${null!=c&&null==f?W`${c}%`:V} ${null!=h?W`${h}`:V} -
`}_roomTemp(t){const e=t.settings?.temp_source;return e?Xo(this.hass,e,"temp"):t.area?this._climate().get(t.area)?.temp??null:null}_roomHum(t){const e=t.settings?.hum_source;return e?Xo(this.hass,e,"hum"):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 n=new Set;for(const t of i||[]){if(!0!==t?.use_climate_temp)continue;const e=(t.binding||"").indexOf(":");e>0&&n.add(t.binding.slice(e+1))}const o=new Map;for(const[e,i]of Object.entries(t.entities)){const s=i.device_id?t.devices?.[i.device_id]:null,r=i.area_id||s?.area_id||null;if(!r)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(ht.has(i.platform))continue;if(Zo.test(e))continue}let a=o.get(r);a||(a=new Map,o.set(r,a));const l=i.device_id||e;let c=a.get(l);if(!c){const n=t.states?.[e];c={name:(s?s.name_by_user||s.name:i.name||n?.attributes?.friendly_name||e)||e,model:s?.model,ents:[]},a.set(l,c)}c.ents.push(e)}for(const[i,r]of o){const o=[],a=[];for(const[i,s]of r){const r=Vo(t,s.name,s.model,s.ents,e),l="mdi:thermometer"===r||"mdi:air-filter"===r;if(l){const e=qo(t,s.ents);null!=e&&o.push(e)}if(n.size>0&&(n.has(i)||s.ents.some(t=>n.has(t)))){const e=Uo(t,s.ents);null!=e&&o.push(e)}if(l||"mdi:water-percent"===r){const e=Bo(t,s.ents);null!=e&&a.push(e)}}(o.length||a.length)&&s.set(i,{temp:o.length?Math.round(o.reduce((t,e)=>t+e,0)/o.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=qi(t.settings?.name_scale),this._roomLabelScale=qi(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=this._roomSrcFilter.trim().toLowerCase(),i=[];for(const s of Object.values(t.devices)){if("service"===s.entry_type)continue;const t=(s.name_by_user||s.name||s.id).trim();e&&!t.toLowerCase().includes(e)||i.push({value:"device:"+s.id,label:t,sub:s.model||this._t("marker.sub_device")})}for(const[s,n]of Object.entries(t.entities)){if(!s.startsWith("sensor.")||n.hidden)continue;const o=n.name||t.states[s]?.attributes?.friendly_name||s;e&&!(o+" "+s).toLowerCase().includes(e)||i.push({value:"entity:"+s,label:o,sub:s})}return i.sort((t,e)=>t.label.localeCompare(e.label)),i.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*Pr,y:i.y*Pr};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},Nr(t),this._tip=null}_labelMove(t,e,i){const s="rl_"+(e.id||"");if(!this._drag||this._drag.id!==s)return;const n=this._stageEl;if(!n)return;const o=this._spaceModel(i).vb,r=n.getBoundingClientRect(),a=this._viewOr(o),l=(t.clientX-this._drag.sx)/r.width*a.w,c=(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 h=Ws(this._drag.ox+l),d=Ws(this._drag.oy+c);this._savePos({id:s,space:i},h,d,t.shiftKey)}_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 n=s.getBoundingClientRect(),o=n.left+n.width/2,r=n.top+n.height/2,a=Math.max(8,Math.hypot(t.clientX-o,t.clientY-r));this._rlResize={id:"rl_"+(e.id||""),space:i,k0:this._labelScale(e),cx:o,cy:r,d0:a},Nr(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))),n=this._layout[e.id];if(n)this._layout={...this._layout,[e.id]:{...n,k:s}};else{const t=e.id.slice(3),i=this._spaceModel(e.space).rooms.find(e=>e.id===t);if(!i)return;const n=this._labelPos(i,e.space);this._layout={...this._layout,[e.id]:{s:e.space,x:n.x/Pr,y:n.y/Pr,k:s}}}this._dirtyPos.add(e.id)}_rlResizeUp(){this._rlResize&&(this._rlResize=null,this._persistLayout())}_renderRoomGear(t,e,i){if(!t.id)return V;let s=null;if(t.poly?(s=this._gearPtCache.get(t.poly)||null,s||(s=Le(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 V;const n=(s[0]-i.x)/i.w*100,o=(s[1]-i.y)/i.h*100;return W``}_renderRoomLabel(t,e,i,s){if(!t.name&&!this._markup)return V;const n=this._labelPos(t,e.id),o=(n.x-i.x)/i.w*100,r=(n.y-i.y)/i.h*100,a=Math.min(1,s.opacity+.25),l=this._labelScale(t),c=[];if(t.area||t.settings?.temp_source||t.settings?.hum_source){if(s.labelTemp){const e=this._roomTemp(t);null!=e&&c.push(W`${e}°`)}if(s.labelHum){const e=this._roomHum(t);null!=e&&c.push(W`${e}%`)}if(s.labelLqi&&t.area){const e=this._roomLqi(t.area);null!=e&&c.push(W`${e}`)}if(s.labelLight&&t.area){const e=function(t,e,i){const s=new Set;let n=0;for(const o of e)if(o.area===i&&!o.hidden)for(const e of o.entities)e.startsWith("light.")&&!s.has(e)&&(s.add(e),"on"===t.states[e]?.state&&n++);return s.size?{on:n,total:s.size}:null}(this.hass,this._devices,t.area);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});c.push(W`${t}`)}}}return W`
this._labelDown(i,t,e.id)} @pointermove=${i=>this._labelMove(i,t,e.id)} @pointerup=${()=>this._labelUp(t)} @@ -3369,12 +3370,12 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @pointerup=${()=>this._rlResizeUp()} @pointercancel=${()=>this._rlResizeUp()}>`):V}
`}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"===this._tool&&this._path.length&&!this._contourClosed?this._path[this._path.length-1]:"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:null}_renderMeasureLabel(t){const e=this._measureAnchor,i=this._cursorPt,s=(i[0]-t.x)/t.w*100,n=(i[1]-t.y)/t.h*100,o=Ui(e,i),r=Math.round(10*o)/10,a=Wi(o);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,n]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-n)<1e-6)return null;const o=(e+s)/2,r=(i+n)/2;if("line"===t.kind){const e=Ui(t.a,t.b);return{x:o,y:r,on45:Wi(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}return{x:o,y:r,on45:!1,text:`${this._fmtLen([e,i],[s,i])} × ${this._fmtLen([s,i],[s,n])}`}}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=Pr,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=Se(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 i=Pr,s=this._decorH,n=this._decorMove?.id;for(const e of this._decorList)e.id!==n&&("line"===e.kind?t.push([e.x1*i,e.y1*s],[e.x2*i,e.y2*s]):"text"===e.kind?t.push([e.x*i,e.y*s]):t.push([e.x*i,e.y*s],[(e.x+e.w)*i,e.y*s],[e.x*i,(e.y+e.h)*s],[(e.x+e.w)*i,(e.y+e.h)*s]));this._decorDraft&&t.push(this._decorDraft.a);for(const i of e.rooms){const e=Se(i);if(e)for(const i of e)t.push(i)}return 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,n=null;for(const o of e)if(!(Math.abs(o[0]-t[0])<1e-6&&Math.abs(o[1]-t[1])<1e-6)){if(Math.abs(o[0]-t[0])<=i){const e=Math.abs(o[1]-t[1]);e>1e-6&&(!s||e1e-6&&(!n||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,n]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-n)<1e-6)return null;const o=(e+s)/2,r=(i+n)/2;if("line"===t.kind){const e=Ui(t.a,t.b);return{x:o,y:r,on45:Wi(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}return{x:o,y:r,on45:!1,text:`${this._fmtLen([e,i],[s,i])} × ${this._fmtLen([s,i],[s,n])}`}}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=Ar,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=Se(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 i=Ar,s=this._decorH,n=this._decorMove?.id;for(const e of this._decorList)e.id!==n&&("line"===e.kind?t.push([e.x1*i,e.y1*s],[e.x2*i,e.y2*s]):"text"===e.kind?t.push([e.x*i,e.y*s]):t.push([e.x*i,e.y*s],[(e.x+e.w)*i,e.y*s],[e.x*i,(e.y+e.h)*s],[(e.x+e.w)*i,(e.y+e.h)*s]));this._decorDraft&&t.push(this._decorDraft.a);for(const i of e.rooms){const e=Se(i);if(e)for(const i of e)t.push(i)}return 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,n=null;for(const o of e)if(!(Math.abs(o[0]-t[0])<1e-6&&Math.abs(o[1]-t[1])<1e-6)){if(Math.abs(o[0]-t[0])<=i){const e=Math.abs(o[1]-t[1]);e>1e-6&&(!s||e1e-6&&(!n||e ${i.map(e=>{const[i,o,r,a]="x"===e.axis?[e.at,e.from[1],e.at,t[1]+Math.sign(t[1]-e.from[1])*n]:[e.from[0],e.at,t[0]+Math.sign(t[0]-e.from[0])*n,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.hass.states[t.contact]?.state:null;return function(t,e,i=!1){return null==e||"unavailable"===e||"unknown"===e?"door"===t?1:0:ze(e)!==!!i?1:0}(t.type,e,!!t.invert)}_renderOpenings(t){const e=this._openingsR;if(!e.length)return B``;const i=t.color,s=this._spaceWalls,n=this._spaceModel().rooms;return B`${e.map(t=>{const e=t.rlen/2,o=this._openingAmt(t),r=o>0&&!!t.contact?"var(--hp-open)":i,a=s.length?no(n,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:t.flip_v},s,this._wallKeyPitch,this._cellCm,this._gridPitch,Pr):{ox:0,oy:0,cm:0},l=a.cm>0?a.cm/this._cellCm*this._gridPitch/2:4,c=t.flip_h?-1:1,h=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*=h,p*=c}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.hass.states[t.contact]?.state:null;return function(t,e,i=!1){return null==e||"unavailable"===e||"unknown"===e?"door"===t?1:0:ze(e)!==!!i?1:0}(t.type,e,!!t.invert)}_renderOpenings(t){const e=this._openingsR;if(!e.length)return B``;const i=t.color,s=this._spaceWalls,n=this._spaceModel().rooms;return B`${e.map(t=>{const e=t.rlen/2,o=this._openingAmt(t),r=o>0&&!!t.contact?"var(--hp-open)":i,a=s.length?no(n,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:t.flip_v},s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ar):{ox:0,oy:0,cm:0},l=a.cm>0?a.cm/this._cellCm*this._gridPitch/2:4,c=t.flip_h?-1:1,h=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*=h,p*=c}if("window"===t.type){const t=Math.PI/2*e,i=a.cm>0?B``:V;d=B` `}
- `}}Or.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},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{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},_decorTextDialog:{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}},Or.ZOOM_MAX=8,Or.ZOOM_MIN=1/3,Or._touchSeen=!1,Or._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,Or.styles=sr,customElements.get("houseplan-card")||customElements.define("houseplan-card",Or),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${yr} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); + `}}Ir.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},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{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},_decorTextDialog:{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}},Ir.ZOOM_MAX=8,Ir.ZOOM_MIN=1/3,Ir._touchSeen=!1,Ir._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,Ir.styles=nr,customElements.get("houseplan-card")||customElements.define("houseplan-card",Ir),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${wr} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 253f94e6..64ed1435 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,20 @@ ## Unreleased (dev) +## v1.59.0-beta.9 — 2026-08-05 + +Ninth pre-release of the 1.59 line: mixed virtual/thick resize integrity and +clean, visible virtual T-junctions. + +- **Resize keeps mixed virtual/thick walls intact.** A live edge drag now moves + `open_spans` and whole/atomic thickness keys together with the room polygons; + commit no longer drops the thickness of the solid remainders, and Undo + restores the complete transaction. +- **Clean virtual T-junctions.** Real wall arms owned by different room + contours receive the missing mitre at an open-span endpoint instead of a + stepped corner. Virtual dashes and the two-click drawing preview paint above + real wall bodies, so they stay visible right up to the junction centreline. + ## v1.59.0-beta.8 — 2026-08-05 Eighth pre-release of the 1.59 line: beta.7 audit follow-ups, a fail-closed diff --git a/docs/CHANGELOG.ru.md b/docs/CHANGELOG.ru.md index e55a2e50..b8b7d2d5 100755 --- a/docs/CHANGELOG.ru.md +++ b/docs/CHANGELOG.ru.md @@ -8,6 +8,20 @@ ## Не выпущено (dev) +## v1.59.0-beta.9 — 2026-08-05 + +Девятый pre-release линейки 1.59: целостный resize смешанных +виртуальных/толстых стен и чистые, видимые виртуальные Т-стыки. + +- **Resize сохраняет смешанную виртуальную/толстую стену.** Во время перетаскивания + `open_spans` и целые/атомарные ключи толщины теперь двигаются вместе с polygon + комнат; commit больше не удаляет толщину реальных остатков, а Undo + восстанавливает всю транзакцию целиком. +- **Чистые Т-стыки с виртуальной стеной.** Реальные плечи, принадлежащие разным + контурам комнат, получают недостающий митровый угол у конца `open_span` вместо + ступеньки. Пунктир и двухкликовое preview виртуальной стены рисуются поверх + реального тела стены и видны до самой осевой линии стыка. + ## v1.59.0-beta.8 — 2026-08-05 Восьмой pre-release линейки 1.59: правки после аудита beta.7, fail-closed diff --git a/docs/STATUS.md b/docs/STATUS.md index f115c948..a77f63fb 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -15,7 +15,7 @@ | Item | State | |---|---| -| Version | **v1.59.0-beta.8** everywhere (manifest, const.py, package.json, CARD_VERSION) — **pre-release**, tag `v1.59.0-beta.8` on **`dev`**, GitHub Release with `prerelease=true`; `main` is not touched and nothing is copied to the home instance by hand — HACS delivers it on the beta channel. On top of beta.7: Split preserves every room-pair piece of `open_spans`, the exact-tag release gate fails closed, smoke/inventory and full/static thin-wall parity are repaired, and sun rays start at the inner corners of thick window openings. Previous pre-release: v1.59.0-beta.7; previous stable: v1.58.0 | +| Version | **v1.59.0-beta.9** everywhere (manifest, const.py, package.json, CARD_VERSION) — **pre-release**, tag `v1.59.0-beta.9` on **`dev`**, GitHub Release with `prerelease=true`; `main` is not touched and nothing is copied to the home instance by hand — HACS delivers it on the beta channel. On top of beta.8: resize keeps partial `open_spans` and the atomic thickness keys of their solid remainders together in live preview, commit and Undo; real arms at virtual T-junctions receive the missing mitre, and the virtual dash/preview renders above thick real walls. Previous pre-release: v1.59.0-beta.8; previous stable: v1.58.0 | | Workflow | Since 2026-07-22: minor changes go to branch **`dev`** (build + smokes → deploy home → commit → push, NO release); releases are batched on the owner's command. **Since 2026-08-04 there is also a pre-release track:** bump to `X.Y.Z-beta.N`, tag the `dev` commit, publish a GitHub Release with `prerelease=true` — `main` is not touched and nothing is copied to the home instance by hand; HACS delivers it on the beta channel | | 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) | diff --git a/docs/TESTING.md b/docs/TESTING.md index f30843a8..28571e86 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -957,6 +957,10 @@ require hands on real hardware — they remain for the human pass. exception to «shared walls together»), growing into one stops - [ ] Ctrl+Z / ⌘Z after releasing a handle restores the previous geometry — one release = one undo step (rooms AND openings) +- [ ] A moved shared wall with a partial virtual middle and thick solid + remainders keeps the dash and both thickness values during live drag and + after release; Undo restores rooms, `open_spans` and `walls` together + [auto: smoke_resize_virtual_thick] - [ ] Device markers do not move; the room settings gear re-centres itself - [ ] Smoke: `node demo/smoke_room_resize.mjs` @@ -1460,7 +1464,14 @@ require hands on real hardware — they remain for the human pass. [auto: smoke_wall_thickness] - [ ] **Shared once / clear → line / resize re-keys**: one body for a shared wall; clearing thickness restores the centreline; resizing a thick wall - keeps the thickness on the moved stretch [auto: smoke_wall_thickness] + keeps the thickness on the moved stretch, including both atomic solid + remainders around a partial virtual span + [auto: smoke_wall_thickness + smoke_resize_virtual_thick] +- [ ] **Virtual T-junction**: when two real thick arms from different room + contours meet at an `open_span` endpoint, the outside corner is a clean + mitre with no stair-step; the saved dash and the two-click rubber band + paint above the real hatch right up to the centreline + [auto: test/wall-thickness.test.mjs + smoke_resize_virtual_thick] - [ ] **Unit + backend**: inset/mitre/bevel, key from either end, degrade, rekey, cm↔inches; `walls` schema bounds [auto: test/wall-thickness.test.mjs + tests_backend/test_validation.py] diff --git a/docs/WALL-THICKNESS.md b/docs/WALL-THICKNESS.md index 057a0bb1..1a6e904d 100644 --- a/docs/WALL-THICKNESS.md +++ b/docs/WALL-THICKNESS.md @@ -44,6 +44,9 @@ joins; bevel when the mitre spike exceeds `MITRE_LIMIT × thickness`. Openings cut the body full-depth; jambs cap the cut; window glass mid-tunnel; door swing from the **inner face**. Association uses wall direction ≈ opening angle (mod 180°), then nearest span — never a perpendicular neighbour at a T. +At an `open_span` endpoint, real arms owned by different room contours receive +the same bounded mitre patch as arms from one contour; a virtual T therefore +has one clean outer corner rather than two butt caps forming a step. ## 4. Floor, fills, light, area @@ -81,7 +84,9 @@ Decor-line thickness, per-side finish, auto-from-backdrop, plan-wide default. ## 8. Testing Unit: ring closed at corners; half-out; inner area; atomic partial shared; -angle-aware opening; rekey after edge/scale. Browser: seamless frame; fill not -in hatch; m² drops with thickness; real resize/undo keeps walls; sun starts at -the room-side opening corners; nav mode restores after `can_write`; a 1 cm body -uses solid-only in both full and static cards while a 20 cm body keeps its hatch. +virtual-T mitre; angle-aware opening; whole and atomic rekey after edge/scale. +Browser: seamless frame; fill not in hatch; m² drops with thickness; a partial +virtual stretch, its solid thick remainders and Undo move as one real resize; +the virtual rubber band paints above the real body; sun starts at the room-side +opening corners; nav mode restores after `can_write`; a 1 cm body uses +solid-only in both full and static cards while a 20 cm body keeps its hatch. diff --git a/package-lock.json b/package-lock.json index 00cbed0b..c20eaa05 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "houseplan-card", - "version": "1.59.0-beta.8", + "version": "1.59.0-beta.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "houseplan-card", - "version": "1.59.0-beta.8", + "version": "1.59.0-beta.9", "license": "MIT", "dependencies": { "lit": "^3.1.3", diff --git a/package.json b/package.json index a5309389..a07a842f 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "houseplan-card", - "version": "1.59.0-beta.8", + "version": "1.59.0-beta.9", "description": "Interactive house plan Lovelace card for Home Assistant", "license": "MIT", "type": "module", diff --git a/src/houseplan-card.ts b/src/houseplan-card.ts index 4b5c379e..8b485cde 100755 --- a/src/houseplan-card.ts +++ b/src/houseplan-card.ts @@ -85,7 +85,7 @@ import { import { alignAllToGrid, type AlignReport } from './align-grid'; import { langOf, t, type I18nKey } from './i18n'; -const CARD_VERSION = '1.59.0-beta.8'; +const CARD_VERSION = '1.59.0-beta.9'; /** HP-1552 boot-veil timing (AUD-1552-02). The veil holds for at least * BOOT_MIN_MS; every stage-height change restarts a BOOT_QUIET_MS * trailing-quiescence requirement (chrome still settling near the cap @@ -3620,7 +3620,14 @@ class HouseplanCard extends LitElement { const real = this._serverCfg?.spaces.find((s: any) => s.id === this._space); if (!g || !real) return; const s = JSON.parse(g.snap); // fresh deep copies every move — free to mutate - const sp = { ...real, rooms: s.rooms, openings: s.openings, walls: s.walls }; + const sp = { + ...real, + rooms: s.rooms, + openings: s.openings, + walls: s.walls, + open_spans: s.open_spans, + }; + if (!Array.isArray(s.open_spans) || !s.open_spans.length) delete (sp as any).open_spans; const H = this._spaceH; for (const [id, poly] of Object.entries(polys)) { const r = sp.rooms.find((x: any) => x.id === id); @@ -3634,6 +3641,36 @@ class HouseplanCard extends LitElement { o.x = c[0] / NORM_W; o.y = c[1] / H; } + // Geometry that belongs to a wall must ride in the SAME live overlay as + // its room polygons. Map from the immutable snapshot on every move (never + // from the previous preview), so partial virtual stretches and the atomic + // thickness keys on their solid remainders cannot lag behind or accumulate + // rounding error during a long drag. + const oldSpans: [number[], number[]][] = []; + const newSpans: [number[], number[]][] = []; + for (const id of g.changed) { + const oldR = g.rooms.find((r) => r.id === id); + const nr = sp.rooms.find((x: any) => x.id === id); + if (!oldR || !nr?.poly) continue; + const newPoly = nr.poly.map((p: number[]) => [p[0] * NORM_W, p[1] * H] as number[]); + if (oldR.poly.length !== newPoly.length) continue; + for (let i = 0; i < oldR.poly.length; i++) { + oldSpans.push([oldR.poly[i], oldR.poly[(i + 1) % oldR.poly.length]]); + newSpans.push([newPoly[i], newPoly[(i + 1) % newPoly.length]]); + } + } + if (oldSpans.length) { + const movedOpen = rekeyOpenSpansAfterMove( + sanitizeOpenSpans((sp as any).open_spans), oldSpans, newSpans, NORM_W, + ); + if (movedOpen.length) (sp as any).open_spans = movedOpen; + else delete (sp as any).open_spans; + if (Array.isArray(sp.walls) && sp.walls.length) { + sp.walls = rekeyWallsAfterMove( + sp.walls, oldSpans, newSpans, this._wallKeyPitch, NORM_W, + ); + } + } this._rszPreview = { space: this._space, sp }; this._cfgEpoch++; } @@ -3730,35 +3767,21 @@ class HouseplanCard extends LitElement { if (preview.sp.walls.length) sp.walls = preview.sp.walls; else delete sp.walls; } + if (Array.isArray(preview.sp.open_spans) && preview.sp.open_spans.length) { + (sp as any).open_spans = preview.sp.open_spans; + } else { + delete (sp as any).open_spans; + } for (const id of g.changed) { const r = sp.rooms.find((x: any) => x.id === id); if (r?.poly) r.poly = simplifyPoly(r.poly, 1e-9); } - // ONE geometry transaction (docs/WALL-THICKNESS.md, AUD-159B6-02): - // old→new edge pairs are built once, then the open spans move (they - // define where a wall is cut into atomic pieces) and only after that the - // thickness keys are rewritten and the dead ones dropped. - const H = this._spaceH; - const oldSpans: [number[], number[]][] = []; - const newSpans: [number[], number[]][] = []; - for (const id of g.changed) { - const oldR = g.rooms.find((r) => r.id === id); - const nr = sp.rooms.find((x: any) => x.id === id); - if (!oldR || !nr?.poly) continue; - const newPoly = nr.poly.map((p: number[]) => [p[0] * NORM_W, p[1] * H] as number[]); - if (oldR.poly.length !== newPoly.length) continue; - for (let i = 0; i < oldR.poly.length; i++) { - oldSpans.push([oldR.poly[i], oldR.poly[(i + 1) % oldR.poly.length]]); - newSpans.push([newPoly[i], newPoly[(i + 1) % newPoly.length]]); - } - } - this._commitOpenSpans({ old: oldSpans, next: newSpans }); + // The preview was derived from the immutable snapshot and already moved + // both explicit spans and every whole/atomic thickness key. Commit only + // clips that geometry to the simplified final rooms, rebuilds open_to, + // and drops keys that genuinely no longer name a live interval. + this._commitOpenSpans(); if (Array.isArray(sp.walls) && sp.walls.length) { - if (oldSpans.length) { - sp.walls = rekeyWallsAfterMove( - sp.walls, oldSpans, newSpans, this._wallKeyPitch, NORM_W, - ); - } sp.walls = degradeWalls(sp.walls, sp.rooms || [], GRID_STEP_N, 1, this._cfgOpenCuts()); if (!sp.walls.length) delete sp.walls; } @@ -8097,7 +8120,6 @@ class HouseplanCard extends LitElement { })()} ${disp.fill === 'glow' && !this._markup ? this._renderGlowLayer(space) : nothing} ${this._renderSunRays(space)} - ${this._renderOpenWalls(disp)} ${this._editing ? this._renderAlignGuides() : nothing} ${opMeasure?.guide ? this._renderOpeningCenterTick(opMeasure.guide) : nothing} ${this._markup ? this._renderMarkupLayer(vb) : nothing} @@ -8108,6 +8130,11 @@ class HouseplanCard extends LitElement { it, the sun still comes in at its window, and the contact sensor still opens it. */} ${this._renderWallBodies(disp)} + ${''/* Virtual boundaries (including the live two-click preview) + deliberately paint AFTER real wall bodies. At a T-junction + their first dash must remain visible all the way to the + centreline instead of disappearing underneath the hatch. */} + ${this._renderOpenWalls(disp)} ${disp.hideOpenings && !this._markup ? nothing : this._renderOpenings(disp)} ${this._renderWallThickUi()} ${this._markup && this._tool === 'resize' ? this._renderResizeLayer(view) : nothing} diff --git a/src/wall-thickness.ts b/src/wall-thickness.ts index c3c50442..102076bb 100644 --- a/src/wall-thickness.ts +++ b/src/wall-thickness.ts @@ -255,6 +255,11 @@ export function wallAngleMatches( /** * After an edge drag: rewrite keys whose old span mid/dir map to a moved * stretch. `oldSpans` / `newSpans` are parallel lists of [a,b] endpoints. + * + * A stored key may name either the whole polygon edge or one atomic remainder + * left by a partial shared/open stretch. The latter has a different midpoint, + * so an exact whole-edge key map is insufficient: project every unmatched key + * onto the old edge and carry that relative point onto the new one. */ export function rekeyWallsAfterMove( walls: WallEntry[] | null | undefined, @@ -273,11 +278,39 @@ export function rekeyWallsAfterMove( const nk = keyOf(na, nb, pitch, coordScale); if (ok !== nk) map.set(ok, nk); } - if (!map.size) return walls.slice(); + const scale = coordScale > 0 ? coordScale : 1; + const tol = Math.max(pitch * 0.5, 1e-9) * scale; const used = new Set(); const out: WallEntry[] = []; for (const w of walls) { - const nk = map.get(w.key) || w.key; + let nk = map.get(w.key) || ''; + if (!nk) { + const parsed = parseKeys([w], scale)[0]; + if (parsed) { + for (let i = 0; i < oldSpans.length; i++) { + const [oa, ob] = oldSpans[i]; + const [na, nb] = newSpans[i]; + if (!angleClose(parsed.ang, segAngle(oa, ob))) continue; + const dx = ob[0] - oa[0], dy = ob[1] - oa[1]; + const L2 = dx * dx + dy * dy; + if (L2 < 1e-18) continue; + const t = ((parsed.x - oa[0]) * dx + (parsed.y - oa[1]) * dy) / L2; + if (t < -1e-6 || t > 1 + 1e-6) continue; + if (distToSeg(parsed.x, parsed.y, oa[0], oa[1], ob[0], ob[1]) > tol) continue; + const mx = na[0] + (nb[0] - na[0]) * Math.max(0, Math.min(1, t)); + const my = na[1] + (nb[1] - na[1]) * Math.max(0, Math.min(1, t)); + const [ux, uy] = wallDir(na, nb); + const arm = Math.max(pitch * scale, 1e-6); + nk = keyOf( + [mx - ux * arm, my - uy * arm], + [mx + ux * arm, my + uy * arm], + pitch, scale, + ); + break; + } + } + } + if (!nk) nk = w.key; if (used.has(nk)) continue; used.add(nk); out.push({ key: nk, cm: w.cm }); @@ -972,6 +1005,80 @@ function polyclipToPathD(geom: any): string { return d; } +/** + * Mitre patches at an endpoint where a virtual stretch meets real walls that + * belong to different room contours. + * + * The normal per-room rings can only join adjacent thick edges of ONE room. + * At a virtual T, the two real arms may be owned by two point-touching rooms; + * each ring then ends with a butt cap and their union leaves a stair-step at + * the outer corner. The patch is the missing offset-line parallelogram. It is + * restricted to open-span endpoints, so ordinary corners keep the existing + * contour/mitre/bevel implementation unchanged. + */ +function virtualJunctionPatches( + rooms: any[], + walls: WallEntry[] | null | undefined, + openCuts: number[][], + pitch: number, + cellCm: number, + gridPitch: number, + coordScale: number, +): number[][][] { + if (!walls?.length || !openCuts?.length) return []; + const eps = openEps(pitch, coordScale) * 4; + const unique = new Map(); + for (const iv of wallIntervals(rooms, walls, openCuts, pitch, cellCm, gridPitch, coordScale)) { + if (iv.open || !(iv.half > 0) || unique.has(iv.key)) continue; + unique.set(iv.key, iv); + } + const intervals = [...unique.values()]; + if (intervals.length < 2) return []; + + const nodes: number[][] = []; + for (const cut of openCuts) { + for (const p of [[cut[0], cut[1]], [cut[2], cut[3]]]) { + if (!nodes.some((q) => Math.hypot(q[0] - p[0], q[1] - p[1]) <= eps)) nodes.push(p); + } + } + const out: number[][][] = []; + const awayFrom = (iv: WallInterval, v: number[]): number[] | null => { + let dx = 0, dy = 0; + if (Math.hypot(iv.a[0] - v[0], iv.a[1] - v[1]) <= eps) { + dx = iv.b[0] - iv.a[0]; dy = iv.b[1] - iv.a[1]; + } else if (Math.hypot(iv.b[0] - v[0], iv.b[1] - v[1]) <= eps) { + dx = iv.a[0] - iv.b[0]; dy = iv.a[1] - iv.b[1]; + } else { + return null; + } + const L = Math.hypot(dx, dy); + return L > eps ? [dx / L, dy / L] : null; + }; + + for (const v of nodes) { + const touching = intervals + .map((iv) => ({ iv, u: awayFrom(iv, v) })) + .filter((x): x is { iv: WallInterval; u: number[] } => !!x.u); + for (let i = 0; i < touching.length; i++) { + for (let j = i + 1; j < touching.length; j++) { + const a = touching[i], b = touching[j]; + const cross = a.u[0] * b.u[1] - a.u[1] * b.u[0]; + const sin = Math.abs(cross); + if (sin < 1e-3) continue; // one straight wall, no corner to fill + const da = b.iv.half / sin; + const db = a.iv.half / sin; + const pa = [v[0] - a.u[0] * da, v[1] - a.u[1] * da]; + const pb = [v[0] - b.u[0] * db, v[1] - b.u[1] * db]; + const far = [pa[0] + pb[0] - v[0], pa[1] + pb[1] - v[1]]; + const maxHalf = Math.max(a.iv.half, b.iv.half, 1e-9); + if (Math.hypot(far[0] - v[0], far[1] - v[1]) > MITRE_LIMIT * maxHalf) continue; + out.push(cross > 0 ? [v.slice(), pa, far, pb] : [v.slice(), pb, far, pa]); + } + } + } + return out; +} + /** * One evenodd ring path per room: outset(half) − inset(half). Shared walls * meet as two half-rings; callers may union them via wallBodiesUnionPath. @@ -1042,6 +1149,9 @@ export function wallBodiesUnionPath( if (inC) insets.push(inC); } if (!outsets.length) return null; + const junctions = virtualJunctionPatches( + rooms, walls, openCuts, pitch, cellCm, gridPitch, coordScale, + ); try { let Uout: any = closedRing(outsets[0]); for (let i = 1; i < outsets.length; i++) { @@ -1055,6 +1165,10 @@ export function wallBodiesUnionPath( } body = difference(Uout, Uin); } + // The room-ring subtraction above cannot infer a mitre between real arms + // owned by different contours at a virtual T. Add only those missing + // junction pieces, then let physical openings cut through them as usual. + for (const patch of junctions) body = union(body, closedRing(patch) as any); // cut opening tunnels (axis-aligned to opening angle) for (const o of openings) { if (!(o.length > 0)) continue; diff --git a/test/wall-thickness.test.mjs b/test/wall-thickness.test.mjs index b8bedba3..145c81ec 100644 --- a/test/wall-thickness.test.mjs +++ b/test/wall-thickness.test.mjs @@ -93,6 +93,20 @@ test('rekeyWallsAfterMove rewrites the key when a span shifts by one cell', () = assert.equal(next[0].cm, 18); }); +test('rekeyWallsAfterMove carries atomic remainders of a partially virtual wall', () => { + const oldA = [0.5, 0.1], oldB = [0.5, 0.7]; + const newA = [0.6, 0.1], newB = [0.6, 0.7]; + const walls = [ + { key: wallKey([0.5, 0.1], [0.5, 0.3], pitch), cm: 20 }, + { key: wallKey([0.5, 0.5], [0.5, 0.7], pitch), cm: 25 }, + ]; + const next = rekeyWallsAfterMove(walls, [[oldA, oldB]], [[newA, newB]], pitch); + assert.deepEqual(next, [ + { key: wallKey([0.6, 0.1], [0.6, 0.3], pitch), cm: 20 }, + { key: wallKey([0.6, 0.5], [0.6, 0.7], pitch), cm: 25 }, + ]); +}); + test('setWallThickness upserts and removes', () => { const a = [0, 0], b = [1, 0]; let walls = setWallThickness([], a, b, 12, pitch); @@ -278,6 +292,31 @@ test('wallBodyRings / union: outset − inset forms a closed ring', () => { assert.ok(polygonArea(inner) < polygonArea(rooms[0].poly)); }); +test('wallBodiesUnionPath mitres real arms owned by different rooms at a virtual T', () => { + const scale = 1000; + const rooms = [ + { id: 'a', poly: [[100, 100], [500, 100], [500, 500], [100, 500]] }, + { id: 'b', poly: [[500, 500], [900, 500], [900, 900], [500, 900]] }, + { id: 'c', poly: [[500, 100], [900, 100], [900, 500], [500, 500]] }, + ]; + const open = [[500, 500, 900, 500]]; + const walls = [ + { key: wallKey([0.1, 0.5], [0.5, 0.5], pitch), cm: 20 }, + { key: wallKey([0.5, 0.5], [0.5, 0.9], pitch), cm: 20 }, + ]; + const united = wallBodiesUnionPath(rooms, walls, open, [], pitch, cellCm, GRID_PITCH, scale); + assert.ok(united); + const nums = (united.d.match(/-?\d+(?:\.\d+)?/g) || []).map(Number); + const pts = []; + for (let i = 0; i + 1 < nums.length; i += 2) pts.push([nums[i], nums[i + 1]]); + const half = wallCmToUnits(20, cellCm, GRID_PITCH) / 2; + assert.ok( + pts.some((p) => Math.abs(p[0] - (500 + half)) < 1e-6 + && Math.abs(p[1] - (500 - half)) < 1e-6), + `missing outer mitre corner in ${united.d}`, + ); +}); + test('wallBodiesUnionPath: single fully-thick room keeps a floor hole', () => { const room = { id: 'n', poly: [[100, 100], [300, 100], [300, 300], [100, 300]] }; const walls = applyWallThicknessToNewRoom([], [room], 'n', 15, 0.01, [], 1000);