diff --git a/CODE-REVIEW-issue-068-2026-08-12.md b/CODE-REVIEW-issue-068-2026-08-12.md new file mode 100644 index 00000000..7a08fa39 --- /dev/null +++ b/CODE-REVIEW-issue-068-2026-08-12.md @@ -0,0 +1,82 @@ +# Code review — issue #68 (contextual help) + +Date: 2026-08-12 + +Branch: `dev` + +Scope: `hp-help`, Houseplan help factory, localization contract, dialog/overlay lifecycle, +keyboard and touch interaction, responsive placement, consumers and regression coverage. + +## Outcome + +The overlay, focus, Escape, outside-click, scroll, Popover/fallback and visual-viewport +paths are internally consistent. The shared dialog overlay registry is used correctly, +the trigger remains reachable in disabled fieldsets through `legend`, and all seven +current call sites have non-empty RU/EN body and ARIA strings. + +Four hardening findings were accepted and fixed locally. No model, saved configuration +or user data contract changed. + +## Findings and resolutions + +### CR68-01 — dead trigger is rendered without help content (P1) + +`hp-help` blocked `_openHelp()` when `text` was empty, but `render()` still returned a +focusable button. The result was the exact reported defect: a visible help glyph that +could not open any explanation. + +Resolution: `hp-help` renders nothing unless both trimmed `text` and `ariaLabel` exist. +The same predicate guards opening and closes an already-open surface if either value is +removed dynamically. + +### CR68-02 — missing translation could be displayed as a key (P1) + +The card factory called `t()` directly. Its intended generic fallback returns the key +name when neither dictionary contains a value, so a future incomplete help pair could +produce a real trigger with implementation text such as `marker.foo.help`. + +Resolution: the factory now checks the localized value and its derived `.aria` value +through `hasTranslation()` before it creates `hp-help`. English fallback remains valid; +a genuinely absent or whitespace-only pair produces no host and no layout gap. + +### CR68-03 — accessible name had a hard-coded English fallback (P1) + +Direct use without `ariaLabel` produced `aria-label="Help"`. This violated the issue +contract requiring a complete localized accessible name and made an incomplete component +look valid to keyboard and screen-reader users. + +Resolution: the fallback was removed. Missing ARIA copy suppresses the affordance just +like missing visible copy. + +### CR68-04 — icon did not follow the product icon system (P2) + +The trigger used a font `?`, whose shape and optical alignment depended on the platform +font and did not visually mean “question in a circle”. + +Resolution: the glyph is now the shared MDI `help-circle-outline` vector inside the same +32/40 px target. It remains decorative because the button already has a full ARIA label. + +### CR68-05 — regression coverage missed incomplete content (P2) + +The smoke covered all open/close and overlay paths but never instantiated an empty or +half-configured component, so CR68-01/03 could pass the release gate. + +Resolution: the #68 smoke now asserts that empty body and empty ARIA copy create no +trigger, and that restoring a complete pair creates the circled-question SVG. + +## Reviewed without changes + +- Mouse hover timing, keyboard focus, touch click and the second-Escape dialog path. +- `aria-describedby` only while open; the visible bubble stays hidden from the + accessibility tree to avoid duplicate announcements. +- Exclusive transient-surface ownership with the colour/opacity picker and toast. +- Popover API path and dialog-owned fallback portal. +- Cached dialog scroll-listener cleanup and disconnect cleanup. +- Visual viewport placement, flipping and edge clamping. +- Existing call sites and RU/EN localization parity. + +## Verification policy + +Per project policy, no tests were run during this local edit. Static type checking, +syntax checking and whitespace validation are recorded in the handoff; the updated +targeted smoke is intended for the next prerelease gate. diff --git a/CODE-REVIEW-issue-094-2026-08-12.md b/CODE-REVIEW-issue-094-2026-08-12.md new file mode 100644 index 00000000..7ed3d4df --- /dev/null +++ b/CODE-REVIEW-issue-094-2026-08-12.md @@ -0,0 +1,180 @@ +# Код-ревью issue #94 — универсальное «Переключить состояние» + +- **Дата:** 2026-08-12 +- **Issue:** https://github.com/Matysh/houseplan-card/issues/94 +- **Проверенная версия:** локальный `dev` после `v1.62.0-beta.3`, включая + незакоммиченные исправления #95–#97 +- **Итог ревью до правок:** changes requested — 2 high, 4 medium, 1 minor +- **Итог после локальных правок:** замечания устранены; проверки отложены до + ближайшего pre-release по принятому правилу владельца + +## 1. Охват + +Проверены: + +1. нормативный алгоритм и acceptance criteria в + `docs/specs/094-universal-state-toggle.md`; +2. pure resolver `src/device-toggle.ts`; +3. target selection через exact binding, device role и `controls`; +4. capability/security/service guards; +5. dialog projection, hint, lossless Save и preview; +6. обычный click, confirmation re-resolve и обработка ошибок; +7. общий cover target для действия и presentation; +8. backend schema и import/export round-trip; +9. unit/smoke-матрица и архитектурная документация; +10. совместимость с визуальной непрерывностью #73 и локальными правками + #95–#97. + +## 2. Найденные и исправленные замечания + +### CR94-01 — High: domain service ошибочно считался capability конкретной entity + +**Было:** `POWER_DOMAINS` разрешал `climate`, `water_heater`, `siren` и +`camera`, если нужный service существовал на уровне domain. Но HA публикует +services для всего domain; неподдерживающая их конкретная entity всё равно +оставалась «исполняемой» в hint, а вызов затем отклонялся Home Assistant. + +Это прямо противоречило §9.1 и mutation gate 6 ТЗ. Home Assistant Core +подтверждает entity-level guards: + +- Climate `TURN_OFF=128`, `TURN_ON=256`: + https://github.com/home-assistant/core/blob/dev/homeassistant/components/climate/const.py +- Water heater `ON_OFF=8`: + https://github.com/home-assistant/core/blob/dev/homeassistant/components/water_heater/__init__.py +- Siren `TURN_ON=1`, `TURN_OFF=2`: + https://github.com/home-assistant/core/blob/dev/homeassistant/components/siren/const.py +- Camera `ON_OFF=1`: + https://github.com/home-assistant/core/blob/dev/homeassistant/components/camera/__init__.py + +**Исправлено:** введён декларативный `POWER_ADAPTERS` с state semantics, +unknown policy и точными feature masks. Feature-gated entity теперь получает +команду только при наличии требуемых bits; service catalog остаётся вторым +guard. Media player и legacy vacuum включены в тот же реестр. + +**Покрытие:** параметрические unit-матрицы для всех базовых power adapters и +для climate, media player, siren, water heater, camera, legacy vacuum — как +разрешённые, так и запрещённые/missing-feature варианты; отдельная матрица +`unknown` проверяет полный/неполный capability mask. + +### CR94-02 — High: click мог использовать target из сохранённого визуального frame + +**Было:** #73 намеренно может некоторое время показывать последний цельный +`_renderDevices` snapshot, но `_clickDevice(ev, d)` разрешал action прямо по +переданному `d`. Если binding/controls изменились до атомарной смены frame, +нажатие без confirmation могло вызвать прежнюю цель. Confirmation уже делал +повторное разрешение, обычный click — нет. + +**Исправлено:** в View действие сначала находит текущий `DevItem` в +`this._devices` по стабильному marker id. Action, binding, controls и command +разрешаются только из него; исчезнувший marker даёт no-op. Локальная +House Plan info-card по-прежнему может использовать видимый snapshot — это +безопасная read-only поверхность и намеренный контракт исправления #96. + +**Покрытие:** smoke сохраняет старый `DevItem`, меняет controls, перестраивает +live devices и проверяет, что click вызывает только новую группу. + +### CR94-03 — Medium: неизвестный persisted action расходил UI и runtime + +**Было:** неизвестный token на light проецировался как default `toggle`, тогда +как `toggleOriginOf()` правильно не признавал его toggle-origin. Селектор мог +показать «Переключить состояние», hint оставался пустым, а click был no-op. + +**Исправлено:** light default применяется только к действительно отсутствующему +token (`null`, `undefined`, пустая legacy-строка). Неизвестное значение fail- +closed проецируется в локальную карточку; backend по-прежнему отклоняет его при +записи. + +### CR94-04 — Medium: legacy cover терял identity после disable в HA + +**Было:** legacy `tap_action: cover` искал cover только в active +`device.entities`, если рядом оставался хотя бы один активный sibling. После +disable cover в HA старое явное намерение превращалось в анонимный no-target и +presentation переставал знать прежнюю cover entity. + +**Исправлено:** legacy-cover origin сначала сохраняет приоритет активной cover, +а при её отсутствии ищет историческую цель в `allEntities`. Общий resolver +возвращает `ha-disabled` и сохраняет тот же cover identity для hint/presentation, +но более ранняя disabled registry row не может заслонить рабочую cover. Новый +device-role toggle по-прежнему исключает disabled rows. + +### CR94-05 — Medium: пустой service catalog считался поддержкой всех services + +**Было:** отсутствие/пустой `hass.services` давало optimistic `true` для любого +service. Это нарушало runtime guard из ТЗ и позволяло построить команду без +доказательства её существования. + +**Исправлено:** отсутствующий catalog/domain/service теперь означает +`unsupported`. После появления актуального HA snapshot resolver автоматически +пересчитывает hint и command. Синтетический HA в `demo/srv/demo.html` теперь +публикует явный service catalog, поэтому smoke-среда проверяет тот же fail-closed +контракт и не создаёт ложные no-op. + +### CR94-06 — Medium: device binding не выбирал первую действительно поддерживаемую entity роли + +**Было:** resolver выбирал первую entity «подходящего domain», а затем мог +остановиться на `unsupported`, хотя следующая равноправная entity той же +functional role имела требуемую capability. Это не соответствовало формулировке +§8.1 «первая поддерживаемая entity». + +**Исправлено:** проверка идёт по уже выбранной shared functional role. +Capability-unsupported peer можно пропустить только внутри неё; missing, +unavailable и secure identity сохраняются без retarget. Config/diagnostic +switch более слабой роли по-прежнему никогда не подставляется. + +### CR94-07 — Minor: статус ТЗ оставался «готово к реализации» + +**Исправлено:** ТЗ, specs index, архитектура, STATUS, TESTING и RU/EN changelog +актуализированы под опубликованную beta.3 и этот локальный hardening pass. + +## 3. Проверенные инварианты без изменений + +- exact `entity:` binding не ищет sibling при unsupported/missing/unavailable; +- raw external controls владеют tap только у explicit toggle и не дают fallback + на собственную entity контроллера; +- passive forced-light marker сохраняет единственное документированное driver- + исключение и дедупликацию; +- partial group вызывает только отображённое доступное подмножество; +- any-on/all-off group semantics соответствует ТЗ; +- lock, alarm и cover classes `garage`/`door`/`gate` остаются secure no-op; +- cover/valve open/close/stop используют одновременно feature bit и service; +- confirmation сравнивает target set, а направление намеренно пересчитывается + по текущему state; +- legacy `cover` и отсутствующий default-light action сохраняются lossless до + явного изменения select; +- backend принимает текущие actions и legacy `cover`, неизвестные tokens + отклоняет; import/export сохраняет action-поля без преобразования; +- отдельного `cover` в текущем UI нет; +- right-click, long press, touch/pinch и confirmation UX этим проходом не + менялись. + +## 4. Изменённые файлы + +- `src/device-toggle.ts` +- `src/houseplan-card.ts` +- `test/device-toggle.test.mjs` +- `demo/smoke_controls.mjs` +- `demo/srv/demo.html` +- `docs/specs/094-universal-state-toggle.md` +- `docs/specs/README.md` +- `docs/ARCHITECTURE.md` +- `docs/STATUS.md` +- `docs/TESTING.md` +- `docs/CHANGELOG.md` +- `docs/CHANGELOG.ru.md` + +## 5. Проверка + +Локально выполнены только read-only/static проверки ревью: + +- `git diff --check`; +- `npm run typecheck`; +- `node --check test/device-toggle.test.mjs`; +- `node --check demo/smoke_controls.mjs`; +- поиск всех consumers `resolveToggleIntent`, `projectedTapAction`, + `toggleCoverEntity`, `sameToggleCommandTargets`; +- сверка backend schema/import-export; +- сверка capability flags с официальным Home Assistant Core. + +Unit, browser smoke, backend tests, build и generated bundles **не запускались** +по правилу проекта: локальные правки делаются без тестов, минимальный целевой +прогон выполняется при следующем pre-release. diff --git a/custom_components/houseplan/const.py b/custom_components/houseplan/const.py index bdf3236a..fdb047bd 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.62.0-beta.3" +VERSION = "1.62.0-beta.4" # Portable backup format. This is deliberately independent from the Home # Assistant Store version above: storage migrations and files exported by a diff --git a/custom_components/houseplan/frontend/houseplan-card.js b/custom_components/houseplan/frontend/houseplan-card.js index 036bec2f..70c2fa56 100755 --- a/custom_components/houseplan/frontend/houseplan-card.js +++ b/custom_components/houseplan/frontend/houseplan-card.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="653755f6bd2ba4e51138f9c02b343a4b1ca24336f2352a7e52cdbf4bf711e6f8";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)},a=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:r,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,_=p.trustedTypes,m=_?_.emptyScript:"",g=p.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!r(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=u(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(a(t))}else void 0!==t&&e.push(a(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??y)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,$=t=>t,x=k.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,T=`<${D}>`,P=document,R=()=>P.createComment(""),A=t=>null===t||"object"!=typeof t&&"function"!=typeof t,E=Array.isArray,I="[ \t\n\f\r]",z=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,F=/-->/g,N=/>/g,O=RegExp(`>|${I}(?:([^\\s"'>=/]+)(${I}*=${I}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,H=/"/g,q=/^(?:script|style|textarea|title)$/i,B=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),U=B(1),W=B(2),j=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=P.createTreeWalker(P,129);function Y(t,e){if(!E(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",a=z;for(let e=0;e"===l[0]?(a=n??z,c=-1):void 0===l[1]?c=-2:(c=a.lastIndex-l[2].length,r=l[1],a=void 0===l[3]?O:'"'===l[3]?H:L):a===H||a===L?a=O:a===F||a===N?a=z:(a=O,n=void 0);const d=a===O&&t[e+1].startsWith("/>")?" ":"";o+=a===z?i+T:c>=0?(s.push(r),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 a=t.length-1,r=this.parts,[l,c]=J(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&r.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iE(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==G&&A(this._$AH)?this._$AA.nextSibling.data=t:this.T(P.createTextNode(t)),this._$AH=t}$(t){const{values:e,_$litType$:i}=t,s="number"==typeof i?this._$AC(t):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(e);else{const t=new Q(s,this),i=t.u(this.options);t.p(e),this.T(i),this._$AH=t}}_$AC(t){let e=V.get(t.strings);return void 0===e&&V.set(t.strings,e=new Z(t)),e}k(t){E(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=X(this,t,e,0),o=!A(t)||t!==this._$AH&&t!==j,o&&(this._$AH=t);else{const s=t;let a,r;for(t=n[0],a=0;a{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n},ct=globalThis;let ht=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=lt(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return j}};ht._$litElement$=!0,ht.finalized=!0,ct.litElementHydrateSupport?.({LitElement:ht});const dt=ct.litElementPolyfillSupport;dt?.({LitElement:ht}),(ct.litElementVersions??=[]).push("4.2.2");const ut=2;let pt=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const{I:_t}=at,mt=t=>t,gt=()=>document.createComment(""),ft=(t,e,i)=>{const s=t._$AA.parentNode,n=void 0===e?t._$AB:e._$AA;if(void 0===i){const e=s.insertBefore(gt(),n),o=s.insertBefore(gt(),n);i=new _t(e,o,t,t.options)}else{const e=i._$AB.nextSibling,o=i._$AM,a=o!==t;if(a){let e;i._$AQ?.(t),i._$AM=t,void 0!==i._$AP&&(e=t._$AU)!==o._$AU&&i._$AP(e)}if(e!==n||a){let t=i._$AA;for(;t!==e;){const e=mt(t).nextSibling;mt(s).insertBefore(t,n),t=e}}}return i},vt=(t,e,i=t)=>(t._$AI(e,i),t),yt={},bt=(t,e=yt)=>t._$AH=e,wt=t=>{t._$AR(),t._$AA.remove()},kt=(t,e,i)=>{const s=new Map;for(let n=e;n<=i;n++)s.set(t[n],n);return s},$t=(t=>(...e)=>({_$litDirective$:t,values:e}))(class extends pt{constructor(t){if(super(t),t.type!==ut)throw Error("repeat() can only be used in text expressions")}dt(t,e,i){let s;void 0===i?i=e:void 0!==e&&(s=e);const n=[],o=[];let a=0;for(const e of t)n[a]=s?s(e,a):a,o[a]=i(e,a),a++;return{values:o,keys:n}}render(t,e,i){return this.dt(t,e,i).values}update(t,[e,i,s]){const n=(t=>t._$AH)(t),{values:o,keys:a}=this.dt(e,i,s);if(!Array.isArray(n))return this.ut=a,o;const r=this.ut??=[],l=[];let c,h,d=0,u=n.length-1,p=0,_=o.length-1;for(;d<=u&&p<=_;)if(null===n[d])d++;else if(null===n[u])u--;else if(r[d]===a[p])l[p]=vt(n[d],o[p]),d++,p++;else if(r[u]===a[_])l[_]=vt(n[u],o[_]),u--,_--;else if(r[d]===a[_])l[_]=vt(n[d],o[_]),ft(t,l[_+1],n[d]),d++,_--;else if(r[u]===a[p])l[p]=vt(n[u],o[p]),ft(t,n[d],n[u]),u--,p++;else if(void 0===c&&(c=kt(a,p,_),h=kt(r,d,u)),c.has(r[d]))if(c.has(r[u])){const e=h.get(a[p]),i=void 0!==e?n[e]:null;if(null===i){const e=ft(t,n[d]);vt(e,o[p]),l[p]=e}else l[p]=vt(i,o[p]),ft(t,n[d],i),n[e]=null;p++}else wt(n[u]),u--;else wt(n[d]),d++;for(;p<=_;){const e=ft(t,l[_+1]);vt(e,o[p]),l[p++]=e}for(;d<=u;){const t=n[d++];null!==t&&wt(t)}return this.ut=a,bt(t,l),j}}),xt=new WeakMap;let St=0;class Mt extends ht{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++St,this._focusInitial=()=>{const t=this._focusableElements(),e=t.find(t=>t.hasAttribute("autofocus"))||t[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");e?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key){t.preventDefault(),t.stopImmediatePropagation(),this._pruneOverlays();const e=this._overlays[this._overlays.length-1];return e?void this._closeOverlay(e,"escape"):void this._requestClose()}if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!t.shiftKey||s!==n&&i.includes(s)?t.shiftKey||s!==o||(t.preventDefault(),n.focus()):(t.preventDefault(),o.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=xt.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),xt.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=[...this._overlays];this._overlays=[];for(const e of t.reverse())e.close("disconnect");const e=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const s=e?xt.get(e):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),e&&s&&requestAnimationFrame(()=>{const t=xt.get(e);if(!t)return;if(!t.dialogs.size)return t.opener?.isConnected&&t.opener.focus({preventScroll:!0}),void xt.delete(e);const s=i?.closest("hp-dialog");i?.isConnected&&s&&t.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),e=[],i=s=>{if(s instanceof HTMLElement&&(s.matches(t)&&e.push(s),s.shadowRoot))for(const t of s.shadowRoot.childNodes)i(t);else for(const t of s.childNodes)i(t)};for(const t of this.childNodes)i(t);const s=this.overlayPortal();return s&&i(s),e.filter(t=>{const e=getComputedStyle(t);return!t.hidden&&"none"!==e.display&&"hidden"!==e.visibility})}_pruneOverlays(){this._overlays=this._overlays.filter(t=>t.owner.isConnected)}_closeOverlay(t,e){const i=this._overlays.findIndex(e=>e.token===t.token);i>=0&&this._overlays.splice(i,1),t.close(e)}registerOverlay(t){this._pruneOverlays();const e=this._overlays.find(e=>e.owner===t.owner);e&&this._overlays.splice(this._overlays.indexOf(e),1);const i=t.group||"transient";for(const t of[...this._overlays].reverse())t.group===i&&this._closeOverlay(t,"exclusive");const s={...t,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const t=this._overlays.findIndex(t=>t.token===s.token);t>=0&&this._overlays.splice(t,1)}}closeTransientOverlays(t="outside"){this._pruneOverlays();const e=[...this._overlays].filter(t=>"transient"===(t.group||"transient"));for(const i of e.reverse())this._closeOverlay(i,t);return e.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const t=U` +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7ac853d5b750b7f245bbbd86215be08f20fcebadbf0bbbb6e71d5cc38841fbf9";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)},a=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:r,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,_=p.trustedTypes,m=_?_.emptyScript:"",g=p.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!r(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=u(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(a(t))}else void 0!==t&&e.push(a(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??y)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,$=t=>t,x=k.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,T=`<${D}>`,P=document,R=()=>P.createComment(""),A=t=>null===t||"object"!=typeof t&&"function"!=typeof t,E=Array.isArray,I="[ \t\n\f\r]",z=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,F=/-->/g,N=/>/g,O=RegExp(`>|${I}(?:([^\\s"'>=/]+)(${I}*=${I}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,H=/"/g,q=/^(?:script|style|textarea|title)$/i,B=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),U=B(1),W=B(2),j=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=P.createTreeWalker(P,129);function Y(t,e){if(!E(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",a=z;for(let e=0;e"===l[0]?(a=n??z,c=-1):void 0===l[1]?c=-2:(c=a.lastIndex-l[2].length,r=l[1],a=void 0===l[3]?O:'"'===l[3]?H:L):a===H||a===L?a=O:a===F||a===N?a=z:(a=O,n=void 0);const d=a===O&&t[e+1].startsWith("/>")?" ":"";o+=a===z?i+T:c>=0?(s.push(r),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 a=t.length-1,r=this.parts,[l,c]=J(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&r.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iE(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==G&&A(this._$AH)?this._$AA.nextSibling.data=t:this.T(P.createTextNode(t)),this._$AH=t}$(t){const{values:e,_$litType$:i}=t,s="number"==typeof i?this._$AC(t):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(e);else{const t=new Q(s,this),i=t.u(this.options);t.p(e),this.T(i),this._$AH=t}}_$AC(t){let e=V.get(t.strings);return void 0===e&&V.set(t.strings,e=new Z(t)),e}k(t){E(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=X(this,t,e,0),o=!A(t)||t!==this._$AH&&t!==j,o&&(this._$AH=t);else{const s=t;let a,r;for(t=n[0],a=0;a{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n},ct=globalThis;let ht=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=lt(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return j}};ht._$litElement$=!0,ht.finalized=!0,ct.litElementHydrateSupport?.({LitElement:ht});const dt=ct.litElementPolyfillSupport;dt?.({LitElement:ht}),(ct.litElementVersions??=[]).push("4.2.2");const ut=2;let pt=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const{I:_t}=at,mt=t=>t,gt=()=>document.createComment(""),ft=(t,e,i)=>{const s=t._$AA.parentNode,n=void 0===e?t._$AB:e._$AA;if(void 0===i){const e=s.insertBefore(gt(),n),o=s.insertBefore(gt(),n);i=new _t(e,o,t,t.options)}else{const e=i._$AB.nextSibling,o=i._$AM,a=o!==t;if(a){let e;i._$AQ?.(t),i._$AM=t,void 0!==i._$AP&&(e=t._$AU)!==o._$AU&&i._$AP(e)}if(e!==n||a){let t=i._$AA;for(;t!==e;){const e=mt(t).nextSibling;mt(s).insertBefore(t,n),t=e}}}return i},vt=(t,e,i=t)=>(t._$AI(e,i),t),yt={},bt=(t,e=yt)=>t._$AH=e,wt=t=>{t._$AR(),t._$AA.remove()},kt=(t,e,i)=>{const s=new Map;for(let n=e;n<=i;n++)s.set(t[n],n);return s},$t=(t=>(...e)=>({_$litDirective$:t,values:e}))(class extends pt{constructor(t){if(super(t),t.type!==ut)throw Error("repeat() can only be used in text expressions")}dt(t,e,i){let s;void 0===i?i=e:void 0!==e&&(s=e);const n=[],o=[];let a=0;for(const e of t)n[a]=s?s(e,a):a,o[a]=i(e,a),a++;return{values:o,keys:n}}render(t,e,i){return this.dt(t,e,i).values}update(t,[e,i,s]){const n=(t=>t._$AH)(t),{values:o,keys:a}=this.dt(e,i,s);if(!Array.isArray(n))return this.ut=a,o;const r=this.ut??=[],l=[];let c,h,d=0,u=n.length-1,p=0,_=o.length-1;for(;d<=u&&p<=_;)if(null===n[d])d++;else if(null===n[u])u--;else if(r[d]===a[p])l[p]=vt(n[d],o[p]),d++,p++;else if(r[u]===a[_])l[_]=vt(n[u],o[_]),u--,_--;else if(r[d]===a[_])l[_]=vt(n[d],o[_]),ft(t,l[_+1],n[d]),d++,_--;else if(r[u]===a[p])l[p]=vt(n[u],o[p]),ft(t,n[d],n[u]),u--,p++;else if(void 0===c&&(c=kt(a,p,_),h=kt(r,d,u)),c.has(r[d]))if(c.has(r[u])){const e=h.get(a[p]),i=void 0!==e?n[e]:null;if(null===i){const e=ft(t,n[d]);vt(e,o[p]),l[p]=e}else l[p]=vt(i,o[p]),ft(t,n[d],i),n[e]=null;p++}else wt(n[u]),u--;else wt(n[d]),d++;for(;p<=_;){const e=ft(t,l[_+1]);vt(e,o[p]),l[p++]=e}for(;d<=u;){const t=n[d++];null!==t&&wt(t)}return this.ut=a,bt(t,l),j}}),xt=new WeakMap;let St=0;class Mt extends ht{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++St,this._focusInitial=()=>{const t=this._focusableElements(),e=t.find(t=>t.hasAttribute("autofocus"))||t[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");e?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key){t.preventDefault(),t.stopImmediatePropagation(),this._pruneOverlays();const e=this._overlays[this._overlays.length-1];return e?void this._closeOverlay(e,"escape"):void this._requestClose()}if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!t.shiftKey||s!==n&&i.includes(s)?t.shiftKey||s!==o||(t.preventDefault(),n.focus()):(t.preventDefault(),o.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=xt.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),xt.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=[...this._overlays];this._overlays=[];for(const e of t.reverse())e.close("disconnect");const e=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const s=e?xt.get(e):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),e&&s&&requestAnimationFrame(()=>{const t=xt.get(e);if(!t)return;if(!t.dialogs.size)return t.opener?.isConnected&&t.opener.focus({preventScroll:!0}),void xt.delete(e);const s=i?.closest("hp-dialog");i?.isConnected&&s&&t.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),e=[],i=s=>{if(s instanceof HTMLElement&&(s.matches(t)&&e.push(s),s.shadowRoot))for(const t of s.shadowRoot.childNodes)i(t);else for(const t of s.childNodes)i(t)};for(const t of this.childNodes)i(t);const s=this.overlayPortal();return s&&i(s),e.filter(t=>{const e=getComputedStyle(t);return!t.hidden&&"none"!==e.display&&"hidden"!==e.visibility})}_pruneOverlays(){this._overlays=this._overlays.filter(t=>t.owner.isConnected)}_closeOverlay(t,e){const i=this._overlays.findIndex(e=>e.token===t.token);i>=0&&this._overlays.splice(i,1),t.close(e)}registerOverlay(t){this._pruneOverlays();const e=this._overlays.find(e=>e.owner===t.owner);e&&this._overlays.splice(this._overlays.indexOf(e),1);const i=t.group||"transient";for(const t of[...this._overlays].reverse())t.group===i&&this._closeOverlay(t,"exclusive");const s={...t,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const t=this._overlays.findIndex(t=>t.token===s.token);t>=0&&this._overlays.splice(t,1)}}closeTransientOverlays(t="outside"){this._pruneOverlays();const e=[...this._overlays].filter(t=>"transient"===(t.group||"transient"));for(const i of e.reverse())this._closeOverlay(i,t);return e.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const t=U` ${this.icon?U``:G} ${this.title} `;return this._useHaDialog?U`{const t=this._window();this._closeTimer&&t?.clearTimeout(this._closeTimer),this._closeTimer=0},this._surfacePointerLeave=()=>{this._scheduleClose()},this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closeHelp(!1,"outside")},this._dialogScroll=t=>{const e=t.target;this._open&&e instanceof Node&&(e===this._dialog()||e.contains?.(this))&&this._closeHelp(!1,"scroll")},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closeHelp(this._floating.ownsActiveElement(),"escape"))},this._floating=new It(this,"help",this._keyDown),this._queuePosition=()=>{if(!this._open||this._positionRaf)return;const t=this._window();t&&(this._positionRaf=t.requestAnimationFrame(()=>{this._positionRaf=0,this._position()||this._closeHelp()}))}}connectedCallback(){super.connectedCallback(),this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0);const t=this.ownerDocument.defaultView;t?.addEventListener("resize",this._queuePosition),t?.addEventListener("orientationchange",this._queuePosition),t?.visualViewport?.addEventListener("resize",this._queuePosition),t?.visualViewport?.addEventListener("scroll",this._queuePosition),this._scrollDialog=this._dialog(),this._scrollDialog?.addEventListener("scroll",this._dialogScroll,!0),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0);const t=this.ownerDocument.defaultView;t?.removeEventListener("resize",this._queuePosition),t?.removeEventListener("orientationchange",this._queuePosition),t?.visualViewport?.removeEventListener("resize",this._queuePosition),t?.visualViewport?.removeEventListener("scroll",this._queuePosition),this._scrollDialog?.removeEventListener("scroll",this._dialogScroll,!0),this._scrollDialog=null,this.removeEventListener("keydown",this._keyDown,!0),this._clearTimers(),this._positionRaf&&t?.cancelAnimationFrame(this._positionRaf),this._positionRaf=0,this._closeHelp(!1,"disconnect"),super.disconnectedCallback()}updated(t){this.text||!this._open?this._open&&t.has("text")&&(this._usesPopover()||this._renderFallback(),this._queuePosition()):this._closeHelp()}_window(){return this._floating.window()}_dialog(){return this._floating.dialog()}_usesPopover(){return this._floating.usesPopover(this._forceFallback)}_clearTimers(){const t=this._window();this._openTimer&&t?.clearTimeout(this._openTimer),this._closeTimer&&t?.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=0}_scheduleOpen(){const t=this._window();!t||this._open||this._openTimer||(this._closeTimer&&t.clearTimeout(this._closeTimer),this._closeTimer=0,this._openTimer=t.setTimeout(()=>{this._openTimer=0,this.isConnected&&this._openHelp()},300))}_scheduleClose(){const t=this._window(),e=this.renderRoot.querySelector(".trigger");t&&!e?.matches(":focus-visible")&&(this._openTimer&&t.clearTimeout(this._openTimer),this._closeTimer&&t.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=t.setTimeout(()=>{this._closeTimer=0,this.isConnected&&this._closeHelp()},150))}_triggerPointerEnter(t){"mouse"===t.pointerType&&this._scheduleOpen()}_triggerPointerLeave(t){"mouse"===t.pointerType&&this._scheduleClose()}_triggerFocus(){queueMicrotask(()=>{const t=this.renderRoot.querySelector(".trigger");t?.matches(":focus-visible")&&this._openHelp()})}_triggerBlur(){this._scheduleClose()}_triggerClick(){this._open?this._closeHelp():this._openHelp()}async _openHelp(){!this._open&&this.text&&(this._clearTimers(),this._forceFallback=!1,this._open=!0,await this.updateComplete,this._open&&(this._usesPopover()||this._renderFallback(),this._position()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closeHelp("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closeHelp()))}_closeHelp(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;this._clearTimers();const i=this._overlayDispose;this._overlayDispose=null,i?.();const s=this.renderRoot.querySelector(".tooltip");if(s?.hidePopover)try{s.matches(":popover-open")&&s.hidePopover()}catch{}this._open=!1,this._floating.destroy(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_tooltipTemplate(t){return U`
{const t=this._window();this._closeTimer&&t?.clearTimeout(this._closeTimer),this._closeTimer=0},this._surfacePointerLeave=()=>{this._scheduleClose()},this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closeHelp(!1,"outside")},this._dialogScroll=t=>{const e=t.target;this._open&&e instanceof Node&&(e===this._dialog()||e.contains?.(this))&&this._closeHelp(!1,"scroll")},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closeHelp(this._floating.ownsActiveElement(),"escape"))},this._floating=new It(this,"help",this._keyDown),this._queuePosition=()=>{if(!this._open||this._positionRaf)return;const t=this._window();t&&(this._positionRaf=t.requestAnimationFrame(()=>{this._positionRaf=0,this._position()||this._closeHelp()}))}}connectedCallback(){super.connectedCallback(),this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0);const t=this.ownerDocument.defaultView;t?.addEventListener("resize",this._queuePosition),t?.addEventListener("orientationchange",this._queuePosition),t?.visualViewport?.addEventListener("resize",this._queuePosition),t?.visualViewport?.addEventListener("scroll",this._queuePosition),this._scrollDialog=this._dialog(),this._scrollDialog?.addEventListener("scroll",this._dialogScroll,!0),this.addEventListener("keydown",this._keyDown,!0)}willUpdate(){this.toggleAttribute("data-has-content",this._hasContent())}disconnectedCallback(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0);const t=this.ownerDocument.defaultView;t?.removeEventListener("resize",this._queuePosition),t?.removeEventListener("orientationchange",this._queuePosition),t?.visualViewport?.removeEventListener("resize",this._queuePosition),t?.visualViewport?.removeEventListener("scroll",this._queuePosition),this._scrollDialog?.removeEventListener("scroll",this._dialogScroll,!0),this._scrollDialog=null,this.removeEventListener("keydown",this._keyDown,!0),this._clearTimers(),this._positionRaf&&t?.cancelAnimationFrame(this._positionRaf),this._positionRaf=0,this._closeHelp(!1,"disconnect"),super.disconnectedCallback()}updated(t){this._hasContent()||!this._open?this._open&&t.has("text")&&(this._usesPopover()||this._renderFallback(),this._queuePosition()):this._closeHelp()}_window(){return this._floating.window()}_dialog(){return this._floating.dialog()}_usesPopover(){return this._floating.usesPopover(this._forceFallback)}_hasContent(){return"string"==typeof this.text&&this.text.trim().length>0&&"string"==typeof this.ariaLabel&&this.ariaLabel.trim().length>0}_clearTimers(){const t=this._window();this._openTimer&&t?.clearTimeout(this._openTimer),this._closeTimer&&t?.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=0}_scheduleOpen(){const t=this._window();!t||this._open||this._openTimer||(this._closeTimer&&t.clearTimeout(this._closeTimer),this._closeTimer=0,this._openTimer=t.setTimeout(()=>{this._openTimer=0,this.isConnected&&this._openHelp()},300))}_scheduleClose(){const t=this._window(),e=this.renderRoot.querySelector(".trigger");t&&!e?.matches(":focus-visible")&&(this._openTimer&&t.clearTimeout(this._openTimer),this._closeTimer&&t.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=t.setTimeout(()=>{this._closeTimer=0,this.isConnected&&this._closeHelp()},150))}_triggerPointerEnter(t){"mouse"===t.pointerType&&this._scheduleOpen()}_triggerPointerLeave(t){"mouse"===t.pointerType&&this._scheduleClose()}_triggerFocus(){queueMicrotask(()=>{const t=this.renderRoot.querySelector(".trigger");t?.matches(":focus-visible")&&this._openHelp()})}_triggerBlur(){this._scheduleClose()}_triggerClick(){this._open?this._closeHelp():this._openHelp()}async _openHelp(){!this._open&&this._hasContent()&&(this._clearTimers(),this._forceFallback=!1,this._open=!0,await this.updateComplete,this._open&&(this._usesPopover()||this._renderFallback(),this._position()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closeHelp("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closeHelp()))}_closeHelp(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;this._clearTimers();const i=this._overlayDispose;this._overlayDispose=null,i?.();const s=this.renderRoot.querySelector(".tooltip");if(s?.hidePopover)try{s.matches(":popover-open")&&s.hidePopover()}catch{}this._open=!1,this._floating.destroy(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_tooltipTemplate(t){return U``}_renderFallback(){const t=Nt.styles.cssText;this._floating.renderFallback(this._tooltipTemplate(!1),t)}_surface(){return this._floating.surface(".tooltip",this._usesPopover())}_position(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let i=this._surface();if(!t||!e?.isConnected||!i?.isConnected)return!1;const s=At(t);if(i.style.maxWidth=`${Math.max(0,Math.min(320,s.width-16))}px`,i.style.maxHeight=`${Math.max(0,s.height-16)}px`,i.style.visibility="hidden",this._usesPopover()&&i.showPopover)try{i.matches(":popover-open")||i.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),i=this._surface(),!i?.isConnected)return!1;i.style.visibility="hidden"}const n=e.getBoundingClientRect(),o=i.getBoundingClientRect();if(!(n.width&&n.height&&o.width&&o.height))return!1;const a=Et(n,o,s);return i.style.left=`${a.left}px`,i.style.top=`${a.top}px`,i.dataset.side=a.side,i.style.visibility="",!0}render(){const t=this.ariaLabel||"Help";return U` + @pointerenter=${this._surfacePointerEnter} @pointerleave=${this._surfacePointerLeave}>${this.text}
`}_renderFallback(){const t=Nt.styles.cssText;this._floating.renderFallback(this._tooltipTemplate(!1),t)}_surface(){return this._floating.surface(".tooltip",this._usesPopover())}_position(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let i=this._surface();if(!t||!e?.isConnected||!i?.isConnected)return!1;const s=At(t);if(i.style.maxWidth=`${Math.max(0,Math.min(320,s.width-16))}px`,i.style.maxHeight=`${Math.max(0,s.height-16)}px`,i.style.visibility="hidden",this._usesPopover()&&i.showPopover)try{i.matches(":popover-open")||i.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),i=this._surface(),!i?.isConnected)return!1;i.style.visibility="hidden"}const n=e.getBoundingClientRect(),o=i.getBoundingClientRect();if(!(n.width&&n.height&&o.width&&o.height))return!1;const a=Et(n,o,s);return i.style.left=`${a.left}px`,i.style.top=`${a.top}px`,i.dataset.side=a.side,i.style.visibility="",!0}render(){if(!this._hasContent())return G;const t=this.ariaLabel.trim();return U` ${this.text} ${this._usesPopover()?this._tooltipTemplate(!0):G} `}}Nt.properties={text:{type:String},ariaLabel:{type:String,attribute:"aria-label"},_open:{state:!0}},Nt.styles=o` :host { - display: inline-flex; + display: none; flex: none; vertical-align: middle; } + :host([data-has-content]) { + display: inline-flex; + } + .trigger { width: 32px; height: 32px; @@ -385,11 +391,17 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="653755f6bd2ba4e51138f9c02b343a4b1ca2 background: transparent; border: 0; border-radius: 50%; - font: 700 15px/1 system-ui, sans-serif; cursor: help; -webkit-tap-highlight-color: transparent; } + .trigger svg { + display: block; + width: 18px; + height: 18px; + fill: currentColor; + } + .trigger:hover, .trigger:focus-visible, .trigger[aria-expanded='true'] { @@ -3552,7 +3564,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="653755f6bd2ba4e51138f9c02b343a4b1ca2 >${t.valueBadge.text}
`:G} ${null!=t.lqiText?U`${t.lqiText}`:G} - `}const qt=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),Bt=[{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|thermometer|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 Ut(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 Wt=Ut(Bt),jt={temperature:"mdi:thermometer",humidity:"mdi:water-percent",motion:"mdi:motion-sensor",occupancy:"mdi:motion-sensor",presence:"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 Vt(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??Wt)if(t.test(s))return e;return Gt}const Kt=["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 Yt=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,Jt=Math.ceil,Zt=Math.floor,Xt="[BigNumber Error] ",Qt=Xt+"Number primitive has more than 15 significant digits: ",te=1e14,ee=14,ie=9007199254740991,se=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],ne=1e7,oe=1e9;function ae(t){var e=0|t;return t>0||t===e?e:e-1}function re(t){for(var e,i,s=1,n=t.length,o=t[0]+"";sc^i?1:-1;for(r=(l=n.length)<(c=o.length)?l:c,a=0;ao[a]^i?1:-1;return l==c?0:l>c^i?1:-1}function ce(t,e,i,s){if(ti||t!==Zt(t))throw Error(Xt+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function he(t){var e=t.c.length-1;return ae(t.e/ee)==e&&t.c[e]%2!=0}function de(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function ue(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 ey?u.c=u.e=null:t.e=10;l/=10,r++);return void(r>y?u.c=u.e=null:(u.e=r,u.c=[t]))}d=String(t)}else{if(!Yt.test(d=String(t)))return n(u,d,c);u.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(r=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(r<0&&(r=l),r+=+d.slice(l+1),d=d.substring(0,l)):r<0&&(r=d.length)}else{if(ce(e,2,x.length,"Base"),10==e&&S)return P(u=new M(t),_+u.e+1,m);if(d=String(t),c="number"==typeof t){if(0*t!=0)return n(u,d,c,e);if(u.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(Qt+t)}else u.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),r=l=0,h=d.length;lr){r=h;continue}}else if(!a&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){a=!0,l=-1,r=0;continue}return n(u,String(t),c,e)}c=!1,(r=(d=s(d,e,10,u.s)).indexOf("."))>-1?d=d.replace(".",""):r=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>ie||t!==Zt(t)))throw Error(Qt+u.s*t);if((r=r-l-1)>y)u.c=u.e=null;else if(r=f)?de(l,a):ue(l,a,"0");else if(o=(t=P(new M(t),e,i)).e,r=(l=re(t.c)).length,1==s||2==s&&(e<=o||o<=g)){for(;ra),l=ue(l,o,"0"),o+1>r){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=o-r)>0)for(o+1==r&&(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*ee-1)>y?t.c=t.e=null:i=10;r/=10,n++);if((o=e-n)<0)o+=ee,a=e,l=d[c=0],h=Zt(l/u[n-a-1]%10);else if((c=Jt((o+1)/ee))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,n=1,a=(o%=ee)-ee+1}else{for(l=r=d[c],n=1;r>=10;r/=10,n++);h=(a=(o%=ee)-ee+n)<0?0:Zt(l/u[n-a-1]%10)}if(s=s||e<0||null!=d[c+1]||(a<0?l:l%u[n-a-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?a>0?l/u[n-a]: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]=u[(ee-e%ee)%ee],t.e=-e||0):d[0]=t.e=0,t;if(0==o?(d.length=c,r=1,c--):(d.length=c+1,r=u[ee-o],d[c]=a>0?Zt(l/u[n-a]%u[a])*r:0),s)for(;;){if(0==c){for(o=1,a=d[0];a>=10;a/=10,o++);for(a=d[0]+=r,r=1;a>=10;a/=10,r++);o!=r&&(t.e++,d[0]==te&&(d[0]=1));break}if(d[c]+=r,d[c]!=te)break;d[c--]=0,r=1}for(o=d.length;0===d[--o];d.pop());}t.e>y?t.c=t.e=null:t.e=f?de(e,i):ue(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(Xt+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(ce(i=t[e],0,oe,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(ce(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(ce(i[0],-oe,0,e),ce(i[1],0,oe,e),g=i[0],f=i[1]):(ce(i,-oe,oe,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)ce(i[0],-oe,-1,e),ce(i[1],1,oe,e),v=i[0],y=i[1];else{if(ce(i,-oe,oe,e),!i)throw Error(Xt+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(Xt+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(Xt+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(ce(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(ce(i=t[e],0,oe,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(Xt+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(Xt+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:k,FORMAT:$,ALPHABET:x}},M.isBigNumber=function(t){if(!t||!0!==t._isBigNumber)return!1;if(!M.DEBUG)return!0;var e,i,s=t.c,n=t.e,o=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===o||-1===o)&&n>=-oe&&n<=oe&&n===Zt(n)){if(0===s[0]){if(0===n&&1===s.length)return!0;break t}if((e=(n+1)%ee)<1&&(e+=ee),String(s[0]).length==e){for(e=0;e=te||i!==Zt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===n&&(null===o||1===o||-1===o))return!0;throw Error(Xt+"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,a=Math.random()*o&2097151?function(){return Zt(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,n,o,r=0,l=[],c=new M(p);if(null==t?t=_:ce(t,0,oe),n=Jt(t/ee),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(n*=2));r>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[r]=i[0],e[r+1]=i[1]):(l.push(o%1e14),r+=2);r=n/2}else{if(!crypto.randomBytes)throw b=!1,Error(Xt+"crypto unavailable");for(e=crypto.randomBytes(n*=7);r=9e15?crypto.randomBytes(7).copy(e,r):(l.push(o%1e14),r+=7);r=n/7}if(!b)for(;r=10;o/=10,r++);ri-1&&(null==a[n+1]&&(a[n+1]=0),a[n+1]+=a[n]/i|0,a[n]%=i)}return a.reverse()}return function(s,n,o,a,r){var l,c,h,d,u,p,g,f,v=s.indexOf("."),y=_,b=m;for(v>=0&&(d=k,k=0,s=s.replace(".",""),p=(f=new M(n)).pow(s.length-v),k=d,f.c=e(ue(re(p.c),p.e,"0"),10,o,t),f.e=f.c.length),h=d=(g=e(s,n,o,r?(l=x,t):(l=t,x))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--h:(p.c=g,p.e=h,p.s=a,g=(p=i(p,f,y,b,o)).c,u=p.r,h=p.e),v=g[c=h+y+1],d=o/2,u=u||c<0||null!=g[c+1],u=b<4?(null!=v||u)&&(0==b||b==(p.s<0?3:2)):v>d||v==d&&(4==b||u||6==b&&1&g[c-1]||b==(p.s<0?8:7)),c<1||!g[0])s=u?ue(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=c,u)for(--o;++g[--c]>o;)g[c]=0,c||(++h,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,s="";v<=d;s+=l.charAt(g[v++]));s=ue(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,n,o,a,r=0,l=t.length,c=e%ne,h=e/ne|0;for(t=t.slice();l--;)r=((n=c*(o=t[l]%ne)+(s=h*o+(a=t[l]/ne|0)*c)%ne*ne+r)/i|0)+(s/ne|0)+h*a,t[l]=n%i;return r&&(t=[r].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,a,r){var l,c,h,d,u,p,_,m,g,f,v,y,b,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(g=(m=new M(S)).c=[],S=o+(c=s.e-n.e)+1,r||(r=te,c=ae(s.e/ee)-ae(n.e/ee),S=S/ee|0),h=0;D[h]==(C[h]||0);h++);if(D[h]>(C[h]||0)&&c--,S<0)g.push(1),d=!0;else{for(w=C.length,$=D.length,h=0,S+=2,(u=Zt(r/(D[0]+1)))>1&&(D=t(D,u,r),C=t(C,u,r),$=D.length,w=C.length),b=$,v=(f=C.slice(0,$)).length;v<$;f[v++]=0);x=D.slice(),x=[0].concat(x),k=D[0],D[1]>=r/2&&k++;do{if(u=0,(l=e(D,f,$,v))<0){if(y=f[0],$!=v&&(y=y*r+(f[1]||0)),(u=Zt(y/k))>1)for(u>=r&&(u=r-1),_=(p=t(D,u,r)).length,v=f.length;1==e(p,f,_,v);)u--,i(p,$<_?x:D,_,r),_=p.length,l=1;else 0==u&&(l=u=1),_=(p=D.slice()).length;if(_=10;S/=10,h++);P(m,o+(m.e=h+c*ee-1)+1,a,d)}else m.e=c,m.r=+d;return m}}(),r=/^(-?)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(r,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(Xt+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},u.absoluteValue=u.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},u.comparedTo=function(t,e){return le(this,new M(t,e))},u.decimalPlaces=u.dp=function(t,e){var i,s,n,o=this;if(null!=t)return ce(t,0,oe),null==e?e=m:ce(e,0,8),P(new M(o),t+o.e+1,e);if(!(i=o.c))return null;if(s=((n=i.length-1)-ae(this.e/ee))*ee,n=i[n])for(;n%10==0;n/=10,s--);return s<0&&(s=0),s},u.dividedBy=u.div=function(t,e){return i(this,new M(t,e),_,m)},u.dividedToIntegerBy=u.idiv=function(t,e){return i(this,new M(t,e),0,1)},u.exponentiatedBy=u.pow=function(t,e){var i,s,n,o,a,r,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(Xt+"Exponent not an integer: "+R(t));if(null!=e&&(e=new M(e)),a=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(+R(h),a?t.s*(2-he(t)):+R(t))),e?c.mod(e):c;if(r=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!r&&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||a&&h.c[1]>=24e7:h.c[0]<8e13||a&&h.c[0]<=9999975e7)))return o=h.s<0&&he(t)?-0:0,h.e>-1&&(o=1/o),new M(r?1/o:o);k&&(o=Jt(k/ee+2))}for(a?(i=new M(.5),r&&(t.s=1),l=he(t)):l=(n=Math.abs(+R(t)))%2,c=new M(p);;){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=Zt(n/2)))break;l=n%2}else if(P(t=t.times(i),t.e+1,1),t.e>14)l=he(t);else{if(0===(n=+R(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:(r&&(c=p.div(c)),e?c.mod(e):o?P(c,k,m,void 0):c)},u.integerValue=function(t){var e=new M(this);return null==t?t=m:ce(t,0,8),P(e,e.e+1,t)},u.isEqualTo=u.eq=function(t,e){return 0===le(this,new M(t,e))},u.isFinite=function(){return!!this.c},u.isGreaterThan=u.gt=function(t,e){return le(this,new M(t,e))>0},u.isGreaterThanOrEqualTo=u.gte=function(t,e){return 1===(e=le(this,new M(t,e)))||0===e},u.isInteger=function(){return!!this.c&&ae(this.e/ee)>this.c.length-2},u.isLessThan=u.lt=function(t,e){return le(this,new M(t,e))<0},u.isLessThanOrEqualTo=u.lte=function(t,e){return-1===(e=le(this,new M(t,e)))||0===e},u.isNaN=function(){return!this.s},u.isNegative=function(){return this.s<0},u.isPositive=function(){return this.s>0},u.isZero=function(){return!!this.c&&0==this.c[0]},u.minus=function(t,e){var i,s,n,o,a=this,r=a.s;if(e=(t=new M(t,e)).s,!r||!e)return new M(NaN);if(r!=e)return t.s=-e,a.plus(t);var l=a.e/ee,c=t.e/ee,h=a.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?a:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?a:3==m?-0:0)}if(l=ae(l),c=ae(c),h=h.slice(),r=l-c){for((o=r<0)?(r=-r,n=h):(c=l,n=d),n.reverse(),e=r;e--;n.push(0));n.reverse()}else for(s=(o=(r=h.length)<(e=d.length))?r:e,r=e=0;e0)for(;e--;h[i++]=0);for(e=te-1;s>r;){if(h[--s]=0;){for(i=0,u=y[n]%g,p=y[n]/g|0,o=n+(a=l);o>n;)i=((c=u*(c=v[--a]%g)+(r=p*c+(h=v[a]/g|0)*u)%g*g+_[o]+i)/m|0)+(r/g|0)+p*h,_[o--]=c%m;_[o]=i}return i?++s:_.splice(0,1),T(t,_,s)},u.negated=function(){var t=new M(this);return t.s=-t.s||null,t},u.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/ee,a=t.e/ee,r=s.c,l=t.c;if(!o||!a){if(!r||!l)return new M(n/0);if(!r[0]||!l[0])return l[0]?t:new M(r[0]?s:0*n)}if(o=ae(o),a=ae(a),r=r.slice(),n=o-a){for(n>0?(a=o,i=l):(n=-n,i=r),i.reverse();n--;i.push(0));i.reverse()}for((n=r.length)-(e=l.length)<0&&(i=l,l=r,r=i,e=n),n=0;e;)n=(r[--e]=r[e]+l[e]+n)/te|0,r[e]=te===r[e]?0:r[e]%te;return n&&(r=[n].concat(r),++a),T(t,r,a)},u.precision=u.sd=function(t,e){var i,s,n,o=this;if(null!=t&&t!==!!t)return ce(t,1,oe),null==e?e=m:ce(e,0,8),P(new M(o),t,e);if(!(i=o.c))return null;if(s=(n=i.length-1)*ee+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},u.shiftedBy=function(t){return ce(t,-9007199254740991,ie),this.times("1e"+t)},u.squareRoot=u.sqrt=function(){var t,e,s,n,o,a=this,r=a.c,l=a.s,c=a.e,h=_+4,d=new M("0.5");if(1!==l||!r||!r[0])return new M(!l||l<0&&(!r||r[0])?NaN:r?a:1/0);if(0==(l=Math.sqrt(+R(a)))||l==1/0?(((e=re(r)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=ae((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(a,o,h,1))),re(o.c).slice(0,l)===(e=re(s.c)).slice(0,l)){if(s.e0&&_>0){for(o=_%r||r,h=p.substr(0,o);o<_;o+=r)h+=c+p.substr(o,r);l>0&&(h+=c+p.slice(o)),u&&(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||"")},u.toFraction=function(t){var e,s,n,o,a,r,l,c,h,d,u,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(p)))throw Error(Xt+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+R(l));if(!f)return new M(g);for(e=new M(p),h=s=new M(p),n=c=new M(p),_=re(f),a=e.e=_.length-g.e-1,e.c[0]=se[(r=a%ee)<0?ee+r:r],t=!t||l.comparedTo(e)>0?a>0?e:h:l,r=y,y=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=g.s,u=i(h,n,a*=2,m).minus(g).abs().comparedTo(i(c,s,a,m).minus(g).abs())<1?[h,n]:[c,s],y=r,u},u.toNumber=function(){return+R(this)},u.toPrecision=function(t,e){return null!=t&&ce(t,1,oe),C(this,t,e,2)},u.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<=g||o>=f?de(re(i.c),o):ue(re(i.c),o,"0"):10===t&&S?e=ue(re((i=P(new M(i),_+o+1,m)).c),i.e,"0"):(ce(t,2,x.length,"Base"),e=s(ue(re(i.c),o,"0"),10,t,n,!0)),n<0&&i.c[0]&&(e="-"+e)),e},u.valueOf=u.toJSON=function(){return R(this)},u._isBigNumber=!0,u[Symbol.toStringTag]="BigNumber",u[Symbol.for("nodejs.util.inspect.custom")]=u.valueOf,null!=e&&M.set(e),M}(),_e=class{key;left=null;right=null;constructor(t){this.key=t}},me=class extends _e{constructor(t){super(t)}},ge=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,a=e;const r=this.compare;let l;for(;;)if(l=r(a.key,t),l>0){let e=a.left;if(null==e)break;if(l=r(e.key,t),l>0&&(a.left=e.right,e.right=a,a=e,e=a.left,null==e))break;null==i?s=a:i.left=a,i=a,a=e}else{if(!(l<0))break;{let e=a.right;if(null==e)break;if(l=r(e.key,t),l<0&&(a.right=e.left,e.left=a,a=e,e=a.right,null==e))break;null==n?o=a:n.right=a,n=a,a=e}}return null!=n&&(n.right=a.left,a.left=o),null!=i&&(i.left=a.right,a.right=s),this.root!==a&&(this.root=a,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)}}},fe=class t extends ge{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 me(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new me(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 me(t.key);return function t(e,i){let s,n;do{if(s=e.left,n=e.right,null!=s){const e=new me(s.key);i.left=e,t(s,e)}if(null!=n){const t=new me(n.key);i.right=t,e=n,i=t}}while(null!=n)}(t,e),e}toSet(){return this.clone()}entries(){return new be(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new ye(this.wrap())}[Symbol.toStringTag]="[object Set]"},ve=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}},ye=class extends ve{getValue(t){return t.key}},be=class extends ve{getValue(t){return[t.key,t.key]}},we=t=>()=>t,ke=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):we(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function $e(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)):we(!1);return(t,i,s)=>{const n=t.x,o=t.y,a=s.x,r=s.y,l=o.minus(r).times(i.x.minus(a)).minus(n.minus(a).times(i.y.minus(r)));return e(l,n,o,a,r)?0:l.comparedTo(0)}}var xe=t=>t,Se=t=>{if(t){const e=new fe(ke(t)),i=new fe(ke(t)),s=(t,e)=>e.addAndReturn(t),n=t=>({x:s(t.x,e),y:s(t.y,i)});return n({x:new pe(0),y:new pe(0)}),n}return xe},Me=t=>({set:t=>{Ce=Me(t)},reset:()=>Me(t),compare:ke(t),snap:Se(t),orient:$e(t)}),Ce=Me(),De=(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),Te=(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}}},Pe=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),Re=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),Ae=t=>Re(t,t).sqrt(),Ee=(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 Pe(n,s).div(Ae(n)).div(Ae(s))},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 Re(n,s).div(Ae(n)).div(Ae(s))},ze=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},Fe=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},Ne=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:je.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:Ee(this.point,t.point,s.point),cosine:Ie(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:a,cosine:r}=e.get(s);return n.isGreaterThanOrEqualTo(0)&&a.isGreaterThanOrEqualTo(0)?o.isLessThan(r)?1:o.isGreaterThan(r)?-1:0:n.isLessThan(0)&&a.isLessThan(0)?o.isLessThan(r)?-1:o.isGreaterThan(r)?1:0:a.isLessThan(n)?-1:a.isGreaterThan(n)?1:0}}},Oe=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(a.isLessThan(r)&&a.isLessThan(c))return-1;if(a.isGreaterThan(r)&&a.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(a.isLessThan(r))return-1;if(a.isGreaterThan(r))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(a),e=n.minus(i),h=c.minus(r),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=++We,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,a;const r=Ne.comparePoints(e,i);if(r<0)n=e,o=i,a=1;else{if(!(r>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);n=i,o=e,a=-1}const l=new Ne(n,!0),c=new Ne(o,!1);return new t(l,c,[s],[a])}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 Ce.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=Te(e,i);if(null===s)return null;const n=this.leftSE.point,o=this.rightSE.point,a=t.leftSE.point,r=t.rightSE.point,l=De(e,a)&&0===this.comparePoint(a),c=De(i,n)&&0===t.comparePoint(n),h=De(e,r)&&0===this.comparePoint(r),d=De(i,o)&&0===t.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?r:null;if(c)return h&&n.x.eq(r.x)&&n.y.eq(r.y)?null:n;if(l)return d&&o.x.eq(a.x)&&o.y.eq(a.y)?null:a;if(d&&h)return null;if(d)return o;if(h)return r;const u=((t,e,i,s)=>{if(e.x.isZero())return Fe(i,s,t.x);if(s.x.isZero())return Fe(t,e,i.x);if(e.y.isZero())return ze(i,s,t.y);if(s.y.isZero())return ze(t,e,i.y);const n=Pe(e,s);if(n.isZero())return null;const o={x:i.x.minus(t.x),y:i.y.minus(t.y)},a=Pe(o,e).div(n),r=Pe(o,s).div(n),l=t.x.plus(r.times(e.x)),c=i.x.plus(a.times(s.x)),h=t.y.plus(r.times(e.y)),d=i.y.plus(a.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(n,this.vector(),a,t.vector());return null===u?null:De(s,u)?Ce.snap(u):null}split(e){const i=[],s=void 0!==e.events,n=new Ne(e,!0),o=new Ne(e,!1),a=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(n);const r=new t(n,a,this.rings.slice(),this.windings.slice());return Ne.comparePoints(r.leftSE.point,r.rightSE.point)>0&&r.swapEvents(),Ne.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=Ce.snap({x:new pe(t[0][0]),y:new pe(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;eUe.run("union",t,e),Je=(t,...e)=>Ue.run("intersection",t,e),Ze=(t,...e)=>Ue.run("difference",t,e);function Xe(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function Qe(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 ti(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 ei(t,e,i=1){const s=t[0].toFixed(i),n=t[1].toFixed(i),o=e[0].toFixed(i),a=e[1].toFixed(i),r=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 si(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 ni(t){const e=[],i=new Set;for(const s of t||[]){const t=ii(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 r=Math.round(u*i/s.step)*s.step;Math.abs(u*i-i/2)<=s.step/2&&(r=i/2),r=Math.max(o,Math.min(i-o,r));const l=r/i;n={x:e+l*c,y:a+l*h,angle:t}}else n={x:p[0],y:p[1],angle:t}}}return n}function ai(t,e,i,s,n,o=1){const a=e*Math.PI/180,r=[Math.cos(a),Math.sin(a)];let l=null,c=o;for(const e of ni(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(r[0]*(e[1]-t[1])-r[1]*(e[0]-t[0]));if(s(i[0])>o||s(i[1])>o)continue;const n=(i[0][0]-t[0])*r[0]+(i[0][1]-t[1])*r[1],a=(i[1][0]-t[0])*r[0]+(i[1][1]-t[1])*r[1],h=Math.min(n,a),d=Math.max(n,a),u=h>0?h:d<0?-d:0;u[t[0]+r[0]*e,t[1]+r[1]*e],g=(h+d)/2;return{wallA:m(h),wallB:m(d),sideA:p,sideB:_,midA:m((h-u)/2),midB:m((u+d)/2),wallCenter:m(g),centered:Math.abs(g)<=n}}function ri(t,e,i=!1){if(null==e||"unavailable"===e||"unknown"===e)return"window"===t?0:1;const s=function(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}(e)!==!!i;return s?1:0}function li(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(r-o)*(t[1]-a)/(l-a)+o&&(i=!i)}return i}function hi(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;let a=o?((t[0]-e[0])*s+(t[1]-e[1])*n)/o:0;return a=Math.max(0,Math.min(1,a)),Math.hypot(t[0]-(e[0]+a*s),t[1]-(e[1]+a*n))}function di(t,e,i=1e-6){if(!e||e.length<2)return!1;for(let s=0;sn&&a<-n||o<-n&&a>n)&&(r>n&&l<-n||r<-n&&l>n)}function mi(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),n=Math.min(...i),o=Math.max(...i),a=Math.min(...s),r=Math.max(...s),l=Math.max(o-n,r-a)||1;let c=0,h=0,d=0;for(let e=0;e1e-9?[h/(3*c),d/(3*c)]:[(n+o)/2,(a+r)/2],p=(e,i)=>{const s=((e,i)=>{if(!ci([e,i],t))return-1/0;let s=1/0;for(let n=0;nm&&(m=c,_=[s,l])}if(_){const[t,i]=_,s=(o-n)/e,l=(r-a)/e;for(let e=-4;e<=4;e++)for(let n=-4;n<=4;n++){const o=t+s*e/4,a=i+l*n/4,r=p(o,a);r>m&&(m=r,_=[o,a])}}return _||gi(t)||t[0]}function gi(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(ui(s,t,e))return s;for(let s=0;svi(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&vi(e,t,i)))}function wi(t){if(!t||t.length<3)return 0;let e=0;for(let i=0;i[t[0],t[1]]),[t[0][0],t[0][1]]]]}function $i(t,e,i){for(let s=0;s1&&li(i[0],i[i.length-1],e)&&i.pop(),i}function Si(t,e,i){const[s,n]=t.split(":");return"device"===s?n:"entity"===s?"lg_"+n:e&&e.startsWith("v_")?e:i()}function Mi(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function Ci(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 Di(t,e,i,s){if(t.length<2)return;const n=e.x+s,o=e.x+e.w-s,a=e.y+s,r=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;s=0)i=e.slice(0,n).trim(),s=e.slice(n+1).trim();else{const t=e.split(".");t.length>2&&(i=t.slice(0,2).join("."),s=t.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?n>=0&&!s||s&&!/^[a-zA-Z0-9_.-]+$/.test(s)?null:s?{entity:i,attr:s}:{entity:i}:null}function Hi(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),n=Li(s?`${i}:${s}`:i);return n?`{${n.entity}${n.attr?`:${n.attr}`:""}}`:""}function qi(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(),a=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:a(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const r=n.state;if(null==r||""===r)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,n);if("string"==typeof e&&""!==e)return{text:a(e),formatted:!0}}catch{}return{text:a(String(r)),formatted:!1}}function Bi(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 Ui(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 Ni;const o=(e?.attr||"").trim(),a=qi(t,i,o||null);if(null===a)return Ni;return Bi(a,o?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}function Wi(t,e,i,s=()=>!0){const n=t??"";let o=!1;const a=n.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const n=Li(e);return n?(o=!0,s(n.entity||"")?Ui(i,n):Ni):t});if(o)return a;const r=(e?.entity||"").trim();if(!r)return n;if(!s(r)){const t=n.indexOf(Oi);return t>=0?n.slice(0,t)+Ni+n.slice(t+2):n?`${n} ${Ni}`:Ni}const l=Ui(i,e),c=n.indexOf(Oi);return c>=0?n.slice(0,c)+l+n.slice(c+2):n?`${n} ${l}`:l}const ji=20;function Gi(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 Vi(t,e){if(!e)return t;let i=t;for(const[t,s]of Object.entries(e))i=i.split("{"+t+"}").join(String(s));return i}const Ki="#55606c",Yi=.55,Ji={c:"#607d8b",a:.18};function Zi(t,e=Ji){const i=t&&"object"==typeof t?t:null,s=i?.a;return{c:Dt(i?.c,e.c),a:"number"==typeof s&&Number.isFinite(s)?Math.min(1,Math.max(0,s)):e.a}}function Xi(t,e){const i=Zi(t),s=e?.settings?.custom_fill;return s&&"object"==typeof s?Zi(s,i):i}function Qi(t){const e=t?.settings||{},i=!t?.plan_url,s="glow"===e.fill_mode;return{showBorders:e.show_borders??i,showNames:e.show_names??i,color:Dt(e.room_color,Ki),opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):Yi,fill:["lqi","light","temp","custom"].includes(e.fill_mode)?e.fill_mode:"none",customFill:Zi(e.custom_fill),glow:"boolean"==typeof e.glow_enabled?e.glow_enabled:s,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:Dt(e.bg_color,null),hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function ts(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return Dt(i,"")}const es={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}};function is(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(es)){const s=es[t],n=i[t];e[t]={c:Dt(n?.c,s.c),a:n&&"number"==typeof n.a?Math.min(1,Math.max(0,n.a)):s.a}}return e}function ss(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)),a=n.map((t,e)=>Math.round(t+(o[e]-t)*s));return"#"+a.map(t=>t.toString(16).padStart(2,"0")).join("")}function ns(t,e,i,s,n,o,a,r=Ji){const l=function(t,e,i,s,n,o,a,r=Ji){if("custom"===t)return Zi(r);if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:ss(a.lqi_low.c,a.lqi_high.c,t),a:a.lqi_low.a+(a.lqi_high.a-a.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?a.light_none.a>0?a.light_none:null:"on"===i?a.light_on:a.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(n,o),e=Math.max(n,o);return se?a.temp_hot:a.temp_ok}return null}(t,e,i,s,n,o,a,r);return l?{color:l.c,opacity:l.a,mode:t}:null}const os={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"]},as=[["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 rs(t){for(const e of[...Object.values(os),...as])if(t===e[0]||t===e[1])return e;return null}function ls(t,e,i,s,n){if(!s||"unavailable"===s||"unknown"===s)return t;if(n){const i="cover"===e?rs(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=os[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const n=rs(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 cs(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)]}const hs=1/2.2;function ds(t){const e=/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/.exec(t);return e?"#"+e.slice(1).map(t=>Math.min(255,Number(t)).toString(16).padStart(2,"0")).join(""):null}function us(t){if(!t||"object"!=typeof t||Array.isArray(t))return null;const e=t;if(Object.keys(e).some(t=>"c"!==t&&"bri"!==t))return null;const i=Dt(e.c,null);return i?void 0===e.bri||null===e.bri?{c:i}:"number"!=typeof e.bri||!Number.isFinite(e.bri)||e.bri<.01||e.bri>1?null:{c:i,bri:e.bri}:null}function ps(t,e,i){const s=us(e),n=t?.attributes||{},o=s?.bri??function(t){const e=t?.attributes?.brightness,i="number"==typeof e?e:"string"==typeof e&&""!==e.trim()?Number(e):Number.NaN;return Number.isFinite(i)?Math.max(0,Math.min(1,i/255)):1}(t);if(s)return{c:s.c,bri:o};const a=Rt(n.rgb_color);if(a)return{c:ds(a)||Dt(i,"#ffd9a0"),bri:o};const r=Number(n.color_temp_kelvin)||(Number(n.color_temp)>0?1e6/Number(n.color_temp):NaN);return Number.isFinite(r)&&r>0?{c:(l=cs(r),"#"+l.slice(0,3).map(t=>Math.min(255,Math.max(0,Math.round(Number(t)||0))).toString(16).padStart(2,"0")).join("")),bri:o}:{c:Dt(i,"#ffd9a0"),bri:o};var l}function _s(t,e,i){return"on"===t?.state?ps(t,e,i):null}function ms(t,e=1){const i=Math.max(0,Math.min(1,Number.isFinite(t)?t:1)),s=.7*Math.max(0,Math.min(1,Number.isFinite(e)?e:1))*(.4+.6*Math.pow(i,hs));return Math.max(0,Math.min(1,s))}function gs(t){return t.startsWith("light.")||t.startsWith("switch.")}function fs(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let n=0;nu||l>u)continue;const p=(n[0]-o[0])*h+(n[1]-o[1])*d,_=(a[0]-o[0])*h+(a[1]-o[1])*d,m=Math.max(0,Math.min(p,_)),g=Math.min(c,Math.max(p,_));g-m>i&&s.push([o[0]+h*m,o[1]+d*m,o[0]+h*g,o[1]+d*g])}}return s}function vs(t,e,i=1e-6){const s=[];for(const n of t){const t=[n[0],n[1]],o=[n[2],n[3]],a=o[0]-t[0],r=o[1]-t[1],l=Math.hypot(a,r);if(lo||n>o)continue;const a=(s[0]-t[0])*c+(s[1]-t[1])*h,r=(s[2]-t[0])*c+(s[3]-t[1])*h,u=Math.max(0,Math.min(a,r)),p=Math.min(l,Math.max(a,r));p-u>i&&d.push([u,p])}if(!d.length){s.push([t[0],t[1],o[0],o[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let u=0;for(const[e,n]of d)e-u>i&&s.push([t[0]+c*u,t[1]+h*u,t[0]+c*e,t[1]+h*e]),u=Math.max(u,n);l-u>i&&s.push([t[0]+c*u,t[1]+h*u,o[0],o[1]])}return s}function ys(t,e,i=1e-6){const s=[];for(let e=0;e{if("string"!=typeof t||!t)return;const i=$s(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 $s(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 xs(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i||"custom"===i?i:t}function Ss(t,e){const i=e?.settings;return"boolean"==typeof i?.glow?i.glow:"glow"===i?.fill_mode||t}function Ms(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Cs(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Ds(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Ts(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 Ps=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);function Rs(t,e){return"alarm_control_panel"===t||"siren"===t||"binary_sensor"===t&&!!e&&Ps.has(e)}function As(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}}const Es={availability:"available",status:"alarm",activity:"none"},Is=new Set(["motion","vibration","sound"]),zs=new Set(["occupancy","presence"]),Fs=new Set(["door","window","garage_door","opening"]),Ns=new Set(["running","power"]),Os=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Ls=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),Hs=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping"]),qs=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),Bs=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,Us=t=>String(t??"").trim().toLowerCase();function Ws(t,e){if(!e.startsWith("switch."))return!1;const i=t?.entities?.[e]||{},s=t?.states?.[e],n=e.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(n))return!0;if([i.translation_key,i.original_name,i.name].map(Us).some(t=>["power","main power","power switch","питание"].includes(t)))return!0;const o=Us(s?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(o)||/(?:^|[\s._-])питание$/.test(o)}function js(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=Us(t?.[e]);if(Ls.has(i)||Hs.has(i))return i}return""}function Gs(t,e){const i=t?.states?.[e],s=i?Us(i.state):"__missing__",n=String(e||"").split(".")[0],o=Us(i?.attributes?.device_class),a={eid:e,state:s,availability:Bs(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===a.availability)return a;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&Rs(t,e)}(n,o,s))return{...a,status:"alarm"};if("binary_sensor"===n)return Is.has(o)?{...a,edge:"rising"}:zs.has(o)?{...a,activity:"on"===s?"presence":"none"}:Fs.has(o)?{...a,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===o?{...a,activity:"on"===s?"transition":"none"}:Ns.has(o)&&"on"===s?{...a,status:"working",activity:"running"}:a;if("cover"===n)return{...a,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===n)return{...a,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===n)return{...a,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===n){const t=js(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(Us):[],n=!Hs.has(s)&&(qs.has(s)||Ls.has(s)||e.includes(s));return(t?Ls.has(t):n)?{...a,status:"working",activity:"running"}:a}if(["light","switch","fan","humidifier"].includes(n))return"on"===s?{...a,status:"working",activity:"running"}:a;if("media_player"===n)return"off"===s?{...a,availability:"unavailable"}:a;if("vacuum"===n)return"cleaning"===s?{...a,status:"working",activity:"running"}:"returning"===s?{...a,status:"working",activity:"transition"}:a;if("script"===n)return"on"===s?{...a,status:"working",activity:"running"}:a;if("automation"===n)return a;if("button"===n||"event"===n)return{...a,edge:"change"};const r=js(i.attributes);return Ls.has(r)||Ls.has(s)&&!Hs.has(s)?{...a,status:"working",activity:"running"}:a}function Vs(t,e){if(!(t=>!!t&&!Bs(t))(t)||"unavailable"===e.availability||t===e.state)return null;if("rising"===e.edge)return"off"===t&&"on"===e.state?"event":null;if("change"===e.edge)return"event";if("terminal_transition"===e.edge){const i=new Set([t,e.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}const Ks=new WeakMap,Ys=new WeakMap;let Js=1;const Zs="houseplan.ha-binding-status.v1";let Xs=null;function Qs(t){const e=function(t){const e=t?.connection||t;return!e||"object"!=typeof e&&"function"!=typeof e?null:e}(t);if(!e)return null;let i=Ks.get(e);return i||(i={revision:0,authoritative:!1,access:"pending",devices:{},entities:{},lastSuccess:0,listeners:new Set,refs:0},Ks.set(e,i)),i}function tn(t,e){const i=Array.isArray(t)?t:t&&"object"==typeof t&&Array.isArray(t.entries)?t.entries:null;if(!i)return null;const s={};for(const t of i){const i=t?.[e];"string"==typeof i&&i&&(s[i]=t)}return s}async function en(t,e){return e.loading||!t?.callWS||(e.loading=(async()=>{try{const[i,s]=await Promise.all([t.callWS({type:"config/device_registry/list"}),t.callWS({type:"config/entity_registry/list"})]),n=tn(i,"id"),o=tn(s,"entity_id");if(!n||!o)throw new Error("invalid_registry_response");e.devices=n,e.entities=o,e.authoritative=!0,e.access="full",e.lastSuccess=Date.now(),e.error=void 0}catch(t){e.authoritative=!1,e.access="limited",e.error=function(t){if(t&&"object"==typeof t){const e=t;return String(e.message||e.code||e.error||"registry_unavailable")}return String(t||"registry_unavailable")}(t)}finally{e.revision++,e.loading=void 0,function(t){for(const e of[...t.listeners])try{e()}catch{}}(e)}})()),e.loading}function sn(t,e){void 0===e.reloadTimer&&(e.reloadTimer=globalThis.setTimeout(()=>{e.reloadTimer=void 0,en(t,e)},80))}async function nn(t,e){if(e.subscribing||e.unsubDevice&&e.unsubEntity)return e.subscribing;const i=t?.connection?.subscribeEvents;return"function"==typeof i?(e.subscribing=(async()=>{try{e.unsubDevice||(e.unsubDevice=await i.call(t.connection,()=>sn(t,e),"device_registry_updated")),e.unsubEntity||(e.unsubEntity=await i.call(t.connection,()=>sn(t,e),"entity_registry_updated"))}catch{}finally{0===e.refs&&(e.unsubDevice?.(),e.unsubEntity?.(),e.unsubDevice=void 0,e.unsubEntity=void 0),e.subscribing=void 0}})(),e.subscribing):void 0}function on(t,e){const i=Qs(t);if(!i)return()=>{};const s=0===i.refs;i.refs++,i.listeners.add(e),("pending"===i.access||s)&&en(t,i),nn(t,i);let n=!1;return()=>{n||(n=!0,i.listeners.delete(e),i.refs=Math.max(0,i.refs-1),i.refs>0||(i.unsubDevice?.(),i.unsubEntity?.(),i.unsubDevice=void 0,i.unsubEntity=void 0,void 0!==i.reloadTimer&&globalThis.clearTimeout(i.reloadTimer),i.reloadTimer=void 0))}}function an(t){const e=Qs(t);if(!e)return{revision:0,authoritative:!1,access:"limited",devices:t?.devices||{},entities:t?.entities||{},lastSuccess:0,error:"registry_unavailable"};let i=t?.devices||{},s=t?.entities||{};if(e.authoritative){const n=e.liveDevices!==i||e.liveEntities!==s,o=void 0!==e.liveDevices||void 0!==e.liveEntities;if(n&&(e.liveDevices=i,e.liveEntities=s,e.projectedRevision=void 0,o&&sn(t,e)),e.projectedRevision!==e.revision||!e.projectedDevices||!e.projectedEntities){const t={...e.devices},n={...e.entities};for(const[e,s]of Object.entries(i))Object.prototype.hasOwnProperty.call(t,e)||(t[e]=s);for(const[t,e]of Object.entries(s))Object.prototype.hasOwnProperty.call(n,t)||(n[t]=e);e.projectedDevices=t,e.projectedEntities=n,e.projectedRevision=e.revision}i=e.projectedDevices,s=e.projectedEntities}return{revision:e.revision,authoritative:e.authoritative,access:e.access,devices:i,entities:s,lastSuccess:e.lastSuccess,error:e.error}}function rn(t){if(!t||"object"!=typeof t&&"function"!=typeof t)return 0;let e=Ys.get(t);return e||(e=Js++,Ys.set(t,e)),e}function ln(t,e=an(t)){return[e.revision,e.access,rn(e.devices),rn(e.entities)].join(":")}function cn(t){return!!t&&null==t.disabled_by}function hn(t,e=an(t)){const i={},s={},n={};for(const[t,s]of Object.entries(e.devices||{}))cn(s)&&(i[t]=s);for(const[t,i]of Object.entries(e.entities||{})){if(!cn(i))continue;const n=i.device_id?e.devices?.[i.device_id]:null;e.authoritative&&i.device_id&&!n||(n&&!cn(n)||(s[t]=i))}for(const[i,s]of Object.entries(t?.states||{})){const t=e.entities?.[i];if(e.authoritative&&!t)continue;if(t&&!cn(t))continue;const o=t?.device_id?e.devices?.[t.device_id]:null;e.authoritative&&t?.device_id&&!o||(o&&!cn(o)||(n[i]=s))}return{...t,devices:i,entities:s,states:n}}function dn(t,e=an(t)){return{...t,devices:e.devices||{},entities:e.entities||{}}}function un(t,e){const i=[];for(const[s,n]of Object.entries(t||{}))n?.device_id===e&&i.push(s);return i}function pn(){if(Xs)return Xs;if("undefined"==typeof localStorage)return{};try{const t=JSON.parse(localStorage.getItem(Zs)||"{}");if(!t||"object"!=typeof t||Array.isArray(t))return{};const e=Date.now(),i={};for(const[s,n]of Object.entries(t))!n||"active"!==n.kind&&"ha_disabled"!==n.kind||!Number.isFinite(n.ts)||e-n.ts>7776e6||(i[s]=n);return Xs=i,Xs}catch{return{}}}function _n(t,e,i=an(t)){if(!e||"virtual"===e)return{kind:"active",enabledEntityIds:[],allEntityIds:[]};const s=e.indexOf(":");if(s<1)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const n=e.slice(0,s),o=e.slice(s+1);if("device"!==n&&"entity"!==n||!o)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const a=i.devices||{},r=i.entities||{};if(i.authoritative){if("device"===n){const t=a[o];if(!t)return{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[]};const e=un(r,o);if(!cn(t))return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:e};const i=e.filter(t=>cn(r[t]));return e.length&&!i.length?{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:e}:{kind:"active",enabledEntityIds:i,allEntityIds:e}}const e=r[o];return e&&!cn(e)?{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]}:e?.device_id&&!a[e.device_id]?{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[o]}:e?.device_id&&!cn(a[e.device_id])?{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]}:e||t?.states?.[o]?{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}:{kind:"orphaned",reason:"entity_missing",enabledEntityIds:[],allEntityIds:[]}}const l=function(t){return pn()[t]||null}(e);if("device"===n){const e=a[o],i=un(r,o);if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:i};const s=i.filter(t=>{const e=r[t];return null==e?.disabled_by&&(!e.device_id||null==a[e.device_id]?.disabled_by)});if(i.length&&!s.length&&i.every(t=>null!=r[t]?.disabled_by))return{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:i};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"device",enabledEntityIds:[],allEntityIds:i};if(e||s.some(e=>!!t?.states?.[e]))return{kind:"active",enabledEntityIds:s,allEntityIds:i}}else{const e=r[o];if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]};if(e?.device_id&&null!=a[e.device_id]?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"entity",enabledEntityIds:[],allEntityIds:[o]};if(e||t?.states?.[o])return{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}}return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]}}function mn(t){const e={};for(const[i,s]of Object.entries(t.entities)){if(!s?.device_id||!cn(s))continue;const n=t.devices?.[s.device_id];n&&!cn(n)||(e[s.device_id]=e[s.device_id]||[]).push(i)}return e}function gn(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 fn(t,e){if(/_device_temperature$/.test(e))return!1;if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_temperature$/.test(e);const s=i.attributes||{};return"temperature"===s.device_class||/°C|°F/.test(s.unit_of_measurement||"")||/_temperature$/.test(e)}const vn=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],yn=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function bn(t,e){const i=e.map(e=>({eid:e,reg:t?.entities?.[e]})).filter(t=>!!t.reg);if(!i.length)return[];const s=i.filter(t=>!t.reg.entity_category),n=s.length?s:i;for(const t of vn){const e=n.filter(e=>e.eid.startsWith(t+"."));if(e.length)return yn(e).map(t=>t.eid)}const o=n.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=Us(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return Is.has(i)||zs.has(i)||Fs.has(i)||Ns.has(i)||"moving"===i||Os.has(i)}(t,e.eid));if(o.length)return yn(o).map(t=>t.eid);const a=n.filter(t=>t.eid.startsWith("switch."));if(a.length){const e=yn(a);return[(e.find(e=>Ws(t,e.eid))||e[0]).eid]}const r=[];for(const t of Kt)r.push(...yn(n.filter(e=>e.eid.startsWith(t+"."))));return r.push(...yn(n.filter(t=>!Kt.includes(t.eid.split(".")[0])))),r.map(t=>t.eid)}function wn(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=>fn(t,e.eid));if(i)return i.eid}return bn(t,e)[0]}function kn(t,e,i=[]){return[...new Set($n(t,e,i))].filter(t=>gs(t))}function $n(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).filter(t=>"string"==typeof t&&!s.has(t))}function xn(t,e){if("string"==typeof e)return t.area===e;const i=t.marker?.room_id;return i?!!e.id&&i===e.id:!!e.area&&t.area===e.area}function Sn(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(gs),s=t.primary&&gs(t.primary)?t.primary:null;return[...new Set([e,s,...i].filter(t=>!!t&&gs(t)))]}function Mn(t){const e=Sn(t),i=t.marker?.light_entity;return i&&e.includes(i)?i:e[0]||null}function Cn(t,e){if(!1===e.marker?.is_light)return[];if(!0===e.marker?.is_light){return[{eid:Mn(e),via:"forced"}]}const i=e.primary||bn(t,e.entities)[0];return i&&!i.startsWith("light.")?[]:e.entities.filter(e=>e.startsWith("light.")&&!!t.states?.[e]).map(t=>({eid:t,via:"light"}))}const Dn=new WeakMap;function Tn(t,e){const i=kn(e.marker?.binding,e.controls??e.marker?.controls,e.entities),s=$n(e.marker?.binding,e.marker?.controls??e.controls,e.entities);return(null!=e.marker?.is_light||!i.length&&!s.some(t=>t.startsWith("marker:")))&&Cn(t,e).length>0}function Pn(t,e){const i=kn(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"})),s=Tn(t,e)?Cn(t,e):[];for(const t of s)t.eid&&i.some(e=>e.eid===t.eid)||i.push(t);return i}function Rn(t,e,i){const{visible:s,markerById:n,persistedByDevice:o}=function(t){const e=function(t){return t.map(t=>[t.id||"",!0===t.hidden?1:0,t.primary||"",[...t.entities].join(","),void 0===t.controls?"":[...t.controls].join(","),t.marker?.id||"",t.marker?.binding||"",t.marker?.is_light,t.marker?.light_entity||"",null==t.marker?.controls?"":[...t.marker.controls].join(",")].join("")).join("")}(t),i=Dn.get(t);if(i?.fingerprint===e)return i;const s=t.filter(t=>!t.hidden),n=new Map,o=new Map;for(const e of t){o.set(e,$n(e.marker?.binding,e.marker?.controls??e.controls,e.entities));const t=e.hidden||!0!==e.marker?.is_light?null:e.marker?.id||e.id;t&&n.set(String(t),e)}const a={fingerprint:e,visible:s,markerById:n,persistedByDevice:o};return Dn.set(t,a),a}(e),a=null==i?s:s.filter(t=>xn(t,i)),r=new Map,l=new Map;for(const e of a){const i=Pn(t,e).filter(t=>"controls"!==t.via);for(const s of i){const i=!0===e.marker?.is_light?String(e.marker?.id||e.id||""):"",n=i?`marker:${i}`:`entity:${s.eid}`,o=s.eid?[s.eid]:[],a={key:n,eid:s.eid||"",stateEids:o,serviceEids:o,device:e,via:s.via,castsGlow:!0,passive:!s.eid,on:!s.eid||"on"===t.states?.[s.eid]?.state};r.set(e,[...r.get(e)||[],a]),s.eid&&l.set(s.eid,a)}}const c=new Map,h=new Map;for(const t of e){const e=o.get(t)||[],i=new Set(t.hidden?[]:void 0===t.controls?e.filter(gs):t.controls);h.set(t,i);const s=t.hidden?null:Mn(t);for(const t of e)if(t.startsWith("marker:")){const e=t.slice(7),o=n.get(e);if(!o||!r.has(o))continue;const a=c.get(e)||{linked:!1,drivers:new Set};a.linked=!0;const l=i.size?i:new Set(s?[s]:[]);for(const t of l)a.drivers.add(t);c.set(e,a)}}for(const e of r.values())for(const i of e){if(!i.passive)continue;const e=i.key.slice(7),s=c.get(e);i.on=!s?.linked||[...s.drivers].some(e=>"on"===t.states?.[e]?.state)}const d=new Map,u=t=>{const e=d.get(t.key);(!e||t.castsGlow&&!e.castsGlow)&&d.set(t.key,t)};for(const e of s){const s=o.get(e)||[],a=null==i||xn(e,i);for(const i of s){if(i.startsWith("marker:")){const t=n.get(i.slice(7)),e=t?r.get(t):null;if(e)for(const t of e)u(t);continue}if(!gs(i)||!h.get(e)?.has(i))continue;const s=l.get(i);s?u(s):a&&u({key:`entity:${i}`,eid:i,stateEids:[i],serviceEids:[i],device:e,via:"controls",castsGlow:!1,passive:!1,on:"on"===t.states?.[i]?.state})}for(const t of r.get(e)||[])u(t)}return[...d.values()]}function An(t){const e=t.filter(t=>t.castsGlow);return e.find(t=>t.on)||e[0]||null}function En(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function In(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 Mi(i)}function zn(t,e){for(const i of e){if(!fn(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 Fn(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 Nn(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 On(t,e){for(const i of e){if(!Nn(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 Ln(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden||!cn(s))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(!cn(e))continue;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 Hn(t,e,i,s,n){const o=Vt(e,i,n);if(o!==Gt)return o;const a=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&a.push(i)}return function(t){for(const e of t){const t=jt[e];if(t)return t}return null}(a)??Gt}function qn(t){const e=new Set,i=new Set;for(const s of t||[]){if(!0!==s?.removed)continue;const t=String(s.binding||"").indexOf(":");if(t<1)continue;const n=s.binding.slice(0,t),o=s.binding.slice(t+1);o&&("device"===n?e.add(o):"entity"===n&&i.add(o))}return{devices:e,entities:i}}function Bn(t,e,i){if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function Un(t,e,i,s,n){const o=kn(e.binding,e.controls,t.entities).filter(t=>!Bn(i,t,s)).filter(t=>"active"===_n(i,"entity:"+t,n).kind);t.marker=e,t.controls=o,t.userHidden=!0===e.hidden,t.hidden=t.userHidden||"ha_disabled"===t.bindingStatus?.kind,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 Wn(t){const e=t.hass,i=t.registry||an(e),s=hn(e,i),n=dn(e,i),{areaToSpace:o,markers:a,settings:r,excluded:l,showAll:c,firstSpaceId:h,loc:d,iconRules:u}=t,p=!1!==r.group_lights,_=qn(a),m=Ln(s,p).filter(t=>!Bn(s,t.eid,_)),g=new Set(m.map(t=>t.area)),f=mn(s),v=function(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}(n),y=new Set;for(const t of a){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||y.add(t.binding)}const b=(t,e)=>a.find(i=>i.binding===t+":"+e),w={},k=[];for(const t of Object.values(s.devices)){const n=t.area_id;if(!n||!o[n])continue;if("service"===t.entry_type)continue;if(y.has("device:"+t.id))continue;const a=_n(e,"device:"+t.id,i);if("active"!==a.kind)continue;const h=b("device",t.id);if(h&&h.hidden&&!r.filter_seeded)continue;const _=f[t.id]||[],m=gn(s,t,_),v=!r.filter_seeded;if(v&&!c){if(l.has(m))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===m&&t.via_device_id)continue}const $=(t.name_by_user||t.name||d("device.unnamed")).trim(),x=$+"|"+n;let S=Hn(s,$,t.model,_,u);if(_.some(t=>t.startsWith("lock."))&&(S="mdi:lock"),v&&!c&&p&&"mdi:lightbulb"===S&&g.has(n))continue;w[x]=(w[x]||0)+1;const M=w[x]>1?$+" "+w[x]:$,C={id:t.id,name:M,model:t.model||"",area:n,space:o[n],icon:S,entities:_,allEntities:a.allEntityIds,bindingStatus:a,bindingKind:"device",bindingRef:t.id,pdfs:[]};C.primary=wn(s,_,S),"mdi:thermometer"!==S&&"mdi:air-filter"!==S||(C.temp=zn(s,_)),C.primary&&Nn(s,C.primary)&&(C.hum=On(s,_)),k.push(C)}for(const t of m)o[t.area]&&(y.has("entity:"+t.eid)||k.push({id:"lg_"+t.eid,name:t.name,model:d("device.light_group"),area:t.area,space:o[t.area],icon:"mdi:lightbulb-group",entities:[t.eid],allEntities:[t.eid],bindingStatus:{kind:"active",enabledEntityIds:[t.eid],allEntityIds:[t.eid]},primary:t.eid,bindingKind:"entity",bindingRef:t.eid,pdfs:[]}));for(const t of a){if(t.removed)continue;const[a,l]=t.binding.split(":"),c="device"===a||"entity"===a?_n(e,t.binding,i):null;if(!t.hidden||r.filter_seeded||"ha_disabled"===c?.kind)if("device"===a){const e=c;if("unverified"===e.kind)continue;const a=n.devices[l],r=t.area||a?.area_id||"",p=r&&o[r]||t.space||h,m="active"===e.kind?e.enabledEntityIds:[];let g=a?Hn(s,a.name_by_user||a.name||"",a.model,m,u):"mdi:help-circle";m.some(t=>t.startsWith("lock."))&&(g="mdi:lock");const f={id:t.id,name:a?.name_by_user||a?.name||d("device.fallback"),model:a?.model||"",area:r,space:p,icon:g,entities:m,allEntities:e.allEntityIds.length?e.allEntityIds:a&&v[a.id]||[],bindingStatus:e,bindingKind:"device",bindingRef:l};f.primary=wn(s,m,g),"mdi:thermometer"!==g&&"mdi:air-filter"!==g||(f.temp=zn(s,m)),f.primary&&Nn(s,f.primary)&&(f.hum=On(s,m)),Un(f,t,n,_,i),k.push(f)}else if("entity"===a){if(Bn(n,l,_))continue;const e=c;if("unverified"===e.kind)continue;const a=n.entities[l],r=t.area||a?.area_id||a?.device_id&&n.devices[a.device_id]?.area_id||"",d=r&&o[r]||t.space||h,p=s.states[l],m=a?.name||p?.attributes?.friendly_name||l;let g=Hn(s,m,"",[l],u);l.startsWith("lock.")&&(g="mdi:lock");const f={id:t.id,name:m,model:"",area:r,space:d,icon:g,entities:"active"===e.kind?[l]:[],allEntities:[l],bindingStatus:e,primary:"active"===e.kind?l:void 0,bindingKind:"entity",bindingRef:l};"mdi:thermometer"!==g&&"mdi:air-filter"!==g||!f.entities.length||(f.temp=zn(s,f.entities)),f.entities.length&&Nn(s,l)&&(f.hum=On(s,f.entities)),Un(f,t,n,_,i),k.push(f)}else{const e=t.area||"",s=t.space||e&&o[e]||h,a={id:t.id,name:t.name||d("device.virtual"),model:t.model||"",area:e,space:s,icon:t.icon||"mdi:map-marker",entities:[],allEntities:[],bindingStatus:{kind:"active",enabledEntityIds:[],allEntityIds:[]},bindingKind:"virtual",virtual:!0};Un(a,t,n,_,i),k.push(a)}}return k}function jn(t,e,i,s){if(!e)return null;if(function(t,e,i){if(!e)return!1;const s=e.indexOf(":");if(s<1)return!1;const n=e.slice(0,s),o=e.slice(s+1),a=qn(i);return"device"===n?a.devices.has(o):"entity"===n&&Bn(t,o,a)}(t,e,s))return null;const n=e.indexOf(":");if(n<0)return null;const o=e.slice(0,n),a=e.slice(n+1);if(!a)return null;if("entity"===o){const e=parseFloat(t.states[a]?.state);return Number.isFinite(e)?"temp"===i?Math.round(10*e)/10:Math.round(e):null}if("device"===o){const e=Object.entries(t.entities).filter(([,t])=>t.device_id===a).map(([t])=>t);return"temp"===i?zn(t,e):On(t,e)}return null}const Gn=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");function Vn(t,e,i){const s=[];for(const n of e){if(n.area!==i||n.virtual)continue;const e=In(t,n.entities);null!=e&&s.push(e)}return Mi(s)}function Kn(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=zn(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}const Yn=new Set(["light","switch","fan","humidifier","climate","media_player","input_boolean","automation","remote","siren","vacuum","water_heater","camera"]),Jn={vacuum:{turn_on:1,turn_off:2},media_player:{turn_on:128,turn_off:256}};function Zn(t){return t.slice(0,t.indexOf("."))}function Xn(t,e,i){if(!function(t){return!!t?.services&&"object"==typeof t.services&&Object.keys(t.services).length>0}(t))return!0;const s=t.services?.[e];return!!s&&Object.prototype.hasOwnProperty.call(s,i)}function Qn(t,e,i){return Xn(t,e,i)?{domain:e,service:i}:Xn(t,"homeassistant",i)?{domain:"homeassistant",service:i}:null}function to(t,e){return t?.entities?.[e]||null}function eo(t,e,i){const s=t?.states?.[i],n=to(e,i);return s?.attributes?.friendly_name||n?.name||n?.original_name||i}function io(t,e){const i=to(t,e);if(null!=i?.disabled_by)return!0;const s=i?.device_id?t?.devices?.[i.device_id]:null;return null!=s?.disabled_by}function so(t,e){const i=t?.attributes?.supported_features;if(null==i||""===i)return!0;const s=Number(i);return Number.isFinite(s)&&0!==(s&e)}function no(t,e,i){const s=Zn(i);if("lock"===s||"alarm_control_panel"===s)return!0;if("cover"!==s)return!1;const n=t?.states?.[i],o=to(e,i),a=String(n?.attributes?.device_class||o?.device_class||o?.original_device_class||"");return zi.has(a)}function oo(t,e,i,s,n){return{ref:i,entityId:s,name:s?eo(t,e,s):null,reason:n}}function ao(t,e,i,s,n,o=null){return{target:null,skipped:oo(t,e,i,s,n),semantics:o,nextEffect:null,command:null}}function ro(t,e,i,s,n=i){const o=Zn(i);if("cover"!==o&&"valve"!==o)return function(t,e,i,s,n=i){const o=Zn(i),a=t?.states?.[i];if(io(e,i))return ao(t,e,n,i,"ha-disabled","power");if(no(t,e,i))return ao(t,e,n,i,"secure");if(!a)return ao(t,e,n,i,"missing","power");if("unavailable"===a.state)return ao(t,e,n,i,"unavailable","power");if(!Yn.has(o))return ao(t,e,n,i,"unsupported");let r,l;"unknown"===a.state||""===a.state?(r="toggle",l="toggle"):"off"===a.state?(r="turn-on",l="turn_on"):(r="turn-off",l="turn_off");const c=Jn[o]?.[l];if(c&&!so(a,c))return ao(t,e,n,i,"unsupported","power");const h=Qn(t,o,l);return h?{target:{entityId:i,name:eo(t,e,i),state:String(a.state||""),via:s},skipped:null,semantics:"power",nextEffect:r,command:{...h,data:{entity_id:i}}}:ao(t,e,n,i,"unsupported","power")}(t,e,i,s,n);if(io(e,i))return ao(t,e,n,i,"ha-disabled",o);if(no(t,e,i))return ao(t,e,n,i,"secure",o);const a=t?.states?.[i];if(!a)return ao(t,e,n,i,"missing",o);if("unavailable"===a.state)return ao(t,e,n,i,"unavailable",o);const r=function(t,e,i){const s=String(i?.state||""),n="closed"===s?{service:`open_${e}`,effect:"open",feature:1}:"open"===s?{service:`close_${e}`,effect:"close",feature:2}:"opening"===s||"closing"===s?{service:`stop_${e}`,effect:"stop",feature:8}:null;return n&&so(i,n.feature)&&Xn(t,e,n.service)?n:Xn(t,e,"toggle")?{service:"toggle",effect:"toggle"}:null}(t,o,a);if(!r)return ao(t,e,n,i,"unsupported",o);return{target:{entityId:i,name:eo(t,e,i),state:String(a.state||""),via:s},skipped:null,semantics:o,nextEffect:r.effect,command:{domain:o,service:r.service,data:{entity_id:i}}}}function lo(t){const e=Zn(t);return"cover"===e||"valve"===e||"lock"===e||"alarm_control_panel"===e||Yn.has(e)}function co(t,e){if("entity"===t.bindingKind&&t.bindingRef)return{entityId:t.bindingRef,via:"binding"};const i=bn(e,t.entities.length?t.entities:t.allEntities||[]),s=i.find(lo)||i[0]||null;return s?{entityId:s,via:"device-role"}:null}function ho(t){const e=t.skipped?.reason;return"missing"===e?"unavailable":e||"unsupported"}function uo(t,e){return{origin:t,kind:"none",semantics:null,targets:[],skippedTargets:[],noneReason:e,nextEffect:null,command:null}}function po(t,e){return"cover"===t||"toggle"===t?"toggle":"more-info"===t||"run"===t||"info"===t?t:"light"===e?"toggle":"info"}function _o(t,e){return{origin:t,kind:"single",semantics:e.semantics,targets:e.target?[e.target]:[],skippedTargets:e.skipped?[e.skipped]:[],noneReason:e.command?null:ho(e),nextEffect:e.nextEffect,command:e.command}}function mo(t){const{hass:e,device:i}=t,s=t.registryHass||e,n=function(t){return"toggle"===t.tapAction?"explicit-toggle":"cover"===t.tapAction?"legacy-cover":!t.tapAction&&t.primary?.startsWith("light.")?"default-light":null}(i);if(!n)return null;if("explicit-toggle"===n){if($n(i.marker?.binding,i.marker?.controls??i.controls,i.entities).length)return function(t){const{hass:e,device:i,devices:s}=t,n=t.registryHass||e,o=$n(i.marker?.binding,i.marker?.controls??i.controls,i.entities),a=t.lightSources||Rn(e,s),r=new Map;for(const t of a){if(!t.key.startsWith("marker:"))continue;const e=r.get(t.key)||[];e.push(t),r.set(t.key,e)}const l=co(i,n),c=new Map;for(const t of s){const e=String(t.marker?.id||t.id||"");e&&c.set(e,t)}const h=new Map,d=[],u=new Set,p=t=>{const e=`${t.ref}\n${t.entityId||""}\n${t.reason}`;u.has(e)||(u.add(e),d.push(t))},_=(t,i,s)=>{const o=ro(e,n,t,i,s);o.target?h.set(t,h.get(t)||o.target):o.skipped&&p(o.skipped)};for(const t of o){if(!t.startsWith("marker:")){gs(t)?_(t,"control-entity",t):p(oo(e,n,t,t.includes(".")?t:null,"unsupported"));continue}const i=r.get(t)||[];if(!i.length){const i=c.get(t.slice(7));if("ha_disabled"===i?.bindingStatus?.kind){const s=i.bindingStatus.allEntityIds[0]||null;p({ref:t,entityId:s,name:i.name||(s?eo(e,n,s):null),reason:"ha-disabled"})}else p(oo(e,n,t,null,"missing"));continue}const s=[...new Set(i.flatMap(t=>t.serviceEids))];if(s.length){for(const e of s)_(e,"control-entity",t);continue}const o=i.some(t=>t.passive);o&&l&&gs(l.entityId)?_(l.entityId,"control-marker-driver",t):p(oo(e,n,t,null,o?"missing":"unsupported"))}const m=[...h.values()];if(!m.length)return{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:m,skippedTargets:d,noneReason:d.length&&d.every(t=>"secure"===t.reason)?"secure":"configured-targets-missing",nextEffect:null,command:null};const g=m.some(t=>"on"===t.state)?"turn_off":"turn_on",f=Qn(e,"homeassistant",g);return f?{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:m,skippedTargets:d,noneReason:null,nextEffect:"turn_on"===g?"turn-on":"turn-off",command:{...f,data:{entity_id:m.map(t=>t.entityId)}}}:{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:[],skippedTargets:[...d,...m.map(t=>oo(e,n,t.entityId,t.entityId,"unsupported"))],noneReason:"unsupported",nextEffect:null,command:null}}(t)}if("legacy-cover"===n){const t=(i.entities.length?i.entities:i.allEntities||[]).find(t=>t.startsWith("cover."));return t?_o(n,ro(e,s,t,"device-role")):uo(n,"no-actionable-entity")}const o=co(i,s);return o?_o(n,ro(e,s,o.entityId,o.via)):uo(n,i.virtual||"virtual"===i.bindingKind?"no-actionable-entity":"no-binding")}function go(t){if(!t)return[];const e=Array.isArray(t.data.entity_id)?t.data.entity_id:[t.data.entity_id];return[...new Set(e)].sort()}const fo={climate:["current_temperature","temperature","current_humidity","humidity"],water_heater:["current_temperature","temperature"],cover:["current_position"],valve:["current_position"],fan:["percentage"],humidifier:["current_humidity","humidity"],light:["brightness"],media_player:["volume_level"],vacuum:["battery_level","fan_speed"],lawn_mower:["battery_level","fan_speed"]};function vo(t){return t?"entity_state"===t.kind?`state:${t.entity_id}`:"entity_attribute"===t.kind?`attr:${t.entity_id}:${t.attribute}`:"derived_marker_state"===t.kind?`marker:${t.ref}`:"derived:lqi":""}function yo(t){if("derived:lqi"===t)return{kind:"derived_lqi"};if(t.startsWith("state:"))return{kind:"entity_state",entity_id:t.slice(6)};if(t.startsWith("attr:")){const e=t.slice(5),i=e.lastIndexOf(":");if(i>0)return{kind:"entity_attribute",entity_id:e.slice(0,i),attribute:e.slice(i+1)}}return t.startsWith("marker:marker:")?{kind:"derived_marker_state",ref:t.slice(7)}:null}function bo(t,e){const i=t?.entities?.[e],s=t?.states?.[e];return String(i?.name||i?.original_name||s?.attributes?.friendly_name||e)}function wo(t){return t.replaceAll("_"," ")}function ko(t,e=1){const i=Number(t);return Number.isFinite(i)?`${Math.round(i*e)} %`:null}function $o(t){if("derived_lqi"===t.kind)return"lqi";if("entity_attribute"===t.kind){if(t.attribute.includes("temperature"))return"temperature";if(t.attribute.includes("humidity"))return"humidity"}return"default"}function xo(t,e,i,s=[]){let n=null,o="",a="available";if("entity_state"===i.kind){o=bo(t,i.entity_id);const e=t?.states?.[i.entity_id];if(e)if(Mo(t,i.entity_id))if(["unknown","unavailable"].includes(String(e.state).toLowerCase()))a="unavailable";else{const s=qi(t,i.entity_id);n=s?Bi(s,String(e.attributes?.unit_of_measurement||"")):null,n||(a="unavailable")}else a="unavailable";else a="missing"}else if("entity_attribute"===i.kind){o=`${wo(i.attribute)} · ${bo(t,i.entity_id)}`;const e=t?.states?.[i.entity_id];e?Mo(t,i.entity_id)?(n=function(t,e,i){const s=t?.states?.[e];if(!s||!(i in(s.attributes||{})))return null;const n=qi(t,e,i);if(n?.formatted)return n.text;const o=s.attributes?.[i];if("brightness"===i)return ko(o,100/255);if("volume_level"===i)return ko(o,100);if(["current_position","percentage","current_humidity","humidity","battery_level"].includes(i))return ko(o);if("current_temperature"===i||"temperature"===i){const e=Number(o);if(!Number.isFinite(e))return null;const i=String(t?.config?.unit_system?.temperature||"°C");return`${Math.round(10*e)/10} ${i}`}return n?.text||(["string","number","boolean"].includes(typeof o)?String(o):null)}(t,i.entity_id,i.attribute),null==n&&(a="unavailable")):a="unavailable":a="missing"}else if("derived_lqi"===i.kind){o="LQI";const i=In(t,e.entities);null==i?a="unavailable":n=String(i)}else{o="Light state";const e=s.find(t=>t.ref===i.ref);if(e){const i=e.on?"on":"off",s=t?.localize?.(`state.default.${i}`);n="string"==typeof s&&s?s:i,o=e.name||o}else a="missing"}const r=t?.localize?.("state.default.unavailable")||"Unavailable";return{source:i,sourceLabel:o,text:n??"—",fullText:n??r,availability:a,isLqi:"derived_lqi"===i.kind,tone:$o(i)}}function So(t,e){if(!0===e.marker?.use_climate_temp){const i=e.entities.find(e=>e.startsWith("climate.")&&Number.isFinite(Number(t?.states?.[e]?.attributes?.current_temperature)));if(i)return{kind:"entity_attribute",entity_id:i,attribute:"current_temperature"}}if("mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon){const i=e.entities.find(e=>fn(t,e)&&Number.isFinite(Number(t?.states?.[e]?.state)));if(i)return{kind:"entity_state",entity_id:i}}return e.primary&&Nn(t,e.primary)&&Number.isFinite(Number(t?.states?.[e.primary]?.state))?{kind:"entity_state",entity_id:e.primary}:null}function Mo(t,e){const i=t?.entities?.[e];if(i&&!cn(i))return!1;const s=i?.device_id?t?.devices?.[i.device_id]:null;return!s||cn(s)}function Co(t,e){return Mo(t,e)&&!!t?.states?.[e]}function Do(t,e,i=[e]){const s=new Set(e.entities);for(const t of e.controls||[])t.startsWith("marker:")||s.add(t);const n=[],o=Rn(t,i),a=o.filter(t=>t.key.startsWith("marker:")).map(t=>({ref:t.key,on:t.on,name:t.device?.name||t.key})),r=i=>{const s=vo(i);if(n.some(t=>t.key===s))return;const o=xo(t,e,i,a),r="entity_attribute"===i.kind?`${i.entity_id} · ${i.attribute}`:"entity_state"===i.kind?i.entity_id:"derived_marker_state"===i.kind?i.ref:"LQI";n.push({key:s,source:i,label:o.sourceLabel,technical:r,value:o.text,available:"available"===o.availability})};for(const e of[...s].sort()){if(!Co(t,e))continue;const i=e.split(".")[0],s=t?.entities?.[e];if("button"===i||"event"===i||"config"===s?.entity_category)continue;r({kind:"entity_state",entity_id:e});const n=t?.states?.[e];for(const t of fo[i]||[])t in(n?.attributes||{})&&r({kind:"entity_attribute",entity_id:e,attribute:t})}const l=new Set((e.marker?.controls||e.controls||[]).filter(t=>t.startsWith("marker:")));!0===e.marker?.is_light&&e.marker.id&&l.add(`marker:${e.marker.id}`);const c=new Set(o.map(t=>t.key));for(const t of[...l].sort())c.has(t)&&r({kind:"derived_marker_state",ref:t});return e.virtual||null==In(t,e.entities)||r({kind:"derived_lqi"}),n}function To(t,e,i){const s=So(t,e);if(s&&i.some(t=>t.key===vo(s)))return s;const n=e.primary&&i.find(t=>t.key===`state:${e.primary}`);if(n)return n.source;const o=[t=>t.technical.includes("temperature"),t=>t.technical.includes("humidity"),t=>t.technical.includes("battery")];for(const t of o){const e=i.find(t);if(e)return e.source}return i.find(t=>"derived_lqi"!==t.source.kind)?.source||i[0]?.source||null}const Po={sourceKind:"none",visualSources:[],criticalSources:[],samples:[]};function Ro(t,e){const i=t?.entities?.[e],s=t?.states?.[e];return String(i?.name||i?.original_name||s?.attributes?.friendly_name||e)}function Ao(t,e){const i=t?.states?.[e];if(!i)return"";if("function"==typeof t?.formatEntityState)try{const e=t.formatEntityState(i);if("string"==typeof e&&e)return e}catch{}return String(i.state??"")}function Eo(t,e,i,s){return{eid:e,role:i,name:Ro(t,e),state:String(t?.states?.[e]?.state??""),stateText:Ao(t,e),integrationDomain:t?.entities?.[e]?.platform||null,sample:s||Gs(t,e)}}function Io(t,e,i=[e],s){const n=e.hidden&&e.userHidden?{...e,hidden:!1}:e;let o="none",a=[];const r=(l=mo({hass:t,devices:i,device:n,lightSources:s}),"cover"!==l?.semantics?null:l.targets[0]?.entityId||l.skippedTargets[0]?.entityId||null);var l;const c=Rn(t,[n]),h=$n(n.marker?.binding,n.marker?.controls??n.controls,n.entities),d=new Set(h.filter(t=>t.startsWith("marker:"))),u=!0===n.marker?.is_light||d.size>0?s||Rn(t,i):c,p=u.filter(t=>t.device.id===n.id&&"controls"!==t.via),_=c.filter(t=>"controls"!==t.via),m=[...c.filter(t=>"controls"===t.via),...p.length?p:_];if(d.size)for(const t of u){if(!d.has(t.key))continue;t.stateEids.length>0&&m.some(e=>e.stateEids.some(e=>t.stateEids.includes(e)))||m.some(e=>e.key===t.key)||m.push({...t,via:"controls",castsGlow:!1})}if(r)o="cover",a=[Eo(t,r,"cover")];else if(m.length)o=m.some(t=>"controls"===t.via)?"controls":"light",a=m.map(i=>{const s="controls"===i.via?"control":"forced"===i.via?"forced_light":"light";if(!i.passive)return Eo(t,i.eid,s);const n=i.on?"on":"off";return{eid:i.key,role:s,name:e.name,state:n,stateText:n,integrationDomain:null,sample:{eid:i.key,state:n,availability:"available",status:i.on?"working":"neutral",activity:"none",edge:"none"}}});else{const e=bn(t,n.entities),i=e.length?e:n.entities.filter(e=>!!t?.states?.[e]);if(i.length){o="device_role";const e=function(t,e,i){const s=e.map(e=>Gs(t,e)),n=i.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category);return 1===e.length&&n.length>1&&Ws(t,e[0])?s.map(t=>"unavailable"===t.availability?t:"off"===t.state?{...t,availability:"unavailable",status:"neutral",activity:"none",edge:"none"}:{...t,status:"neutral",activity:"none",edge:"none"}):s}(t,i,n.entities);a=i.map((i,s)=>Eo(t,i,"device_role",e[s]))}else n.primary&&(o="primary",a=[Eo(t,n.primary,"primary")])}const g=[];for(const e of n.entities||[]){const i=Gs(t,e);"alarm"!==i.status||a.some(t=>t.eid===e)||g.push(Eo(t,e,"critical",i))}return{sourceKind:o,visualSources:a,criticalSources:g,samples:[...a,...g].map(t=>t.sample)}}function zo(t,e,i,s){if(e.virtual)return{source:null,text:null,fallback:"value_virtual"};if(s){const i=function(t,e){if(!0!==e.marker?.use_climate_temp)return null;for(const i of e.entities){if(!i.startsWith("climate."))continue;const e=Number(t?.states?.[i]?.attributes?.current_temperature);if(Number.isFinite(e))return{eid:i,text:Math.round(10*e)/10+"°"}}return null}(t,e);if(i)return{source:{kind:"temperature",eid:i.eid,attribute:"current_temperature",text:i.text},text:i.text,fallback:null};const s=function(t,e){if("mdi:thermometer"!==e.icon&&"mdi:air-filter"!==e.icon)return null;for(const i of e.entities){if(!fn(t,i))continue;const e=Number(t?.states?.[i]?.state);if(Number.isFinite(e))return{eid:i,text:Math.round(10*e)/10+"°"}}return null}(t,e);if(s)return{source:{kind:"temperature",eid:s.eid,text:s.text},text:s.text,fallback:null};const n=function(t,e){if(!e.primary||!Nn(t,e.primary))return null;const i=Number(t?.states?.[e.primary]?.state);return Number.isFinite(i)?{eid:e.primary,text:`${Math.round(i)}%`}:null}(t,e);if(n)return{source:{kind:"humidity",eid:n.eid,text:n.text},text:n.text,fallback:null}}if(1!==i.visualSources.length)return{source:null,text:null,fallback:i.visualSources.length?"value_ambiguous_sources":"value_no_state"};const n=i.visualSources[0].eid,o=function(t,e){const i=t?.states?.[e];if(!i||null==i.state||""===String(i.state).trim())return{text:"",fallback:"value_no_state"};const s=i.state;if(!["string","number","boolean"].includes(typeof s))return{text:"",fallback:"value_non_scalar"};const n=String(s).trim().toLowerCase();if("unknown"===n||"unavailable"===n)return{text:"",fallback:"value_no_state"};const o=qi(t,e);return o?{text:Bi(o,String(i.attributes?.unit_of_measurement||"")),fallback:null}:{text:"",fallback:"value_no_state"}}(t,n);return o.fallback?{source:null,text:null,fallback:o.fallback}:{source:{kind:"entity",eid:n,text:o.text},text:o.text,fallback:null}}function Fo(t,e,i){const s=t.marker?.binding||(t.bindingKind&&t.bindingRef?`${t.bindingKind}:${t.bindingRef}`:t.virtual?"virtual":""),n=[...e.visualSources.map(t=>`${t.role}:${t.eid}`),...e.criticalSources.map(t=>`critical:${t.eid}`)].sort(),o=i?`${i.kind}:${i.eid}:${i.attribute||""}`:"none";return[s,e.sourceKind,...n,`value:${o}`].join("\n")}function No(t,e,i=!0,s){const n=s||Io(t,e);return Fo(e,n,zo(t,e,n,i).source)}function Oo(t){if(t.effectiveHidden)return[];if("static_icon"===t.display)return["static-icon"];const e=[],{visual:i}=t;return"alarm"===i.status?e.push("alarm"):"unavailable"===i.availability?e.push("unavail"):"working"===i.status?e.push("on"):"open"===i.status&&e.push("open"),"icon_ripple"===t.display&&"alarm"!==i.status&&"none"!==t.activity&&(e.push("activity-"+t.activity),"event"===t.activity&&t.activityGeneration%2==0&&e.push("activity-gen2")),e}function Lo(t,e,i){const s=Ri(e.marker?.display),n="static_icon"===s,o=n&&!1===i.sourceDetails?Po:Io(t,e,i.lightDevices||[e],i.lightSources),a=e.bindingStatus,r="ha_disabled"===a?.kind,l="orphaned"===a?.kind,c=!0===e.userHidden||!0===e.marker?.hidden,h=r||c&&!i.designPreview,d=function(t){if(!t.length)return{availability:"available",status:"neutral",activity:"none"};const e=t.filter(t=>"available"===t.availability);if(!e.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(e.some(t=>"alarm"===t.status))return Es;const i=e.some(t=>"working"===t.status)?"working":e.some(t=>"open"===t.status)?"open":"neutral",s=e.some(t=>"transition"===t.activity)?"transition":e.some(t=>"presence"===t.activity)?"presence":e.some(t=>"running"===t.activity)?"running":"none";return{availability:"available",status:i,activity:s}}(o.samples);let u=d;h||n?u={availability:"available",status:"neutral",activity:"none"}:"alarm"===d.status||i.liveStates||(u={availability:"available",status:"neutral",activity:"none"});const p=n&&!1===i.sourceDetails?{source:null,text:null,fallback:null}:zo(t,e,o,i.showTemperature),_=Fo(e,o,p.source),m=i.activityRuntime,g=i.now??Date.now();!n&&!h&&i.liveStates&&"alarm"!==d.status&&m?.sources===_&&m.flashTs&&m.flashKind&&g-m.flashTs<3300&&(u={...u,activity:m.flashKind});const f="icon_ripple"===s&&!h&&i.liveStates&&"alarm"!==u.status?u.activity:"none",v=n||h||!i.showTemperature?null:!0===e.marker?.use_climate_temp?Fn(t,e.entities):"mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon?zn(t,e.entities):null,y=!n&&!h&&i.showTemperature&&e.primary&&Nn(t,e.primary)?On(t,e.entities):null,b=n||h||!i.showSignal||e.virtual?null:In(t,e.entities),w=function(t,e,i){const s=null!=e.marker?.value_badge;if(i.effectiveHidden||"static_icon"===i.display)return null;const n=e.marker?.value_badge;if(s)return n?.enabled&&n.source?{configured:!0,enabled:!0,position:n.position||"right",...xo(t,e,n.source,i.markerStates)}:null;if(!i.showTemperature||"value"===i.display)return null;const o=So(t,e);if(!o)return null;let a=null,r="default";if("entity_attribute"===o.kind&&"current_temperature"===o.attribute){const i=Fn(t,e.entities);null!=i&&(a=`${i}°`),r="temperature"}else if("mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon){const i=zn(t,e.entities);null!=i&&(a=`${i}°`),r="temperature"}else{const i=On(t,e.entities);null!=i&&(a=`${i}%`),r="humidity"}return null==a?null:{configured:!1,enabled:!0,source:o,position:"right",sourceLabel:"entity_attribute"===o.kind?`${wo(o.attribute)} · ${bo(t,o.entity_id)}`:"entity_state"===o.kind?bo(t,o.entity_id):"",text:a,fullText:a,availability:"available",isLqi:!1,tone:r}}(t,e,{showTemperature:i.showTemperature,showSignal:i.showSignal,display:s,effectiveHidden:h,markerStates:o.visualSources.filter(t=>t.eid.startsWith("marker:")).map(t=>({ref:t.eid,on:"on"===t.state,name:t.name}))}),k="cover"===o.sourceKind?o.visualSources[0]?.eid:e.primary||o.visualSources[0]?.eid,$=k?t?.states?.[k]:null,x=!i.liveStates||n||h?e.icon:ls(e.icon,k?.split(".")[0],$?.attributes?.device_class,$?.state,!!e.marker?.icon),S=!i.liveStates||n||h?null:Rn(t,[{...e,hidden:!1}]).filter(t=>!!t.eid).map(e=>function(t){return t&&"on"===t.state?Rt(t.attributes?.rgb_color):null}(t?.states?.[e.eid])).find(t=>!!t)||null,M=Number(e.marker?.size)>0?Number(e.marker.size):1,C=Number(e.marker?.angle)||0,D=Number(e.marker?.ripple_size)>0?Number(e.marker.ripple_size):3,T=Dt(e.marker?.ripple_color,null),P=n?null:T||S||null,R="value"!==s||h?null:p.text,A="ha_disabled"===a?.kind?a.reason:null,E=function(t,e,i,s,n,o,a,r){return o?"ha_disabled":a?"orphaned":"static_icon"===r?"static_icon":"alarm"===e.status?"alarm":n?"unavailable"===e.availability?"unavailable":"cover"===s?"cover_icon_state":"presence"===i?"presence":"event"===i?"event":"transition"===i?"transition":"working"===e.status?"icon_ripple"===r&&"none"!==i?"working_activity":"working":"open"===e.status?"open":(t.primary||"").startsWith("media_player.")?"media_neutral":"neutral":"live_states_disabled"}(e,u,f,o.sourceKind,i.liveStates,r,l,s),I=[];i.designPreview&&c&&I.push("hidden_design_preview"),n||!0!==e.marker?.vacuum?.live||I.push("vacuum_live_plan_only");const z=1===o.visualSources.length&&Ws(t,o.visualSources[0].eid),F=e.entities.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category).length;z&&F>1&&I.push("composite_power_source"),"icon_ripple"!==s&&"static_icon"!==s&&"alarm"!==d.status&&"none"!==d.activity&&I.push("activity_display_disabled");const N={binding:e.marker?.binding||(e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:e.virtual?"virtual":""),sourceKind:o.sourceKind,visualSources:o.visualSources,criticalSources:o.criticalSources,valueSource:p.source,sourceSignature:_,visual:u,display:s,icon:x,valueText:R,valueFullText:R,fallbackReason:"value"!==s||R?null:p.fallback,activity:f,activityGeneration:m?.gen||1,classes:[],tempText:null==v?null:String(v),humText:null==y?null:String(y),valueBadge:w,lqiText:null==b||w?.isLqi?null:String(b),lqiColor:null==b?null:Xe(b),lightColor:S,scale:M,angle:C,rippleScale:D,rippleColor:P,userHidden:c,effectiveHidden:h,haDisabled:r,disabledReason:A,orphaned:l,vacuumLive:!n&&!0===e.marker?.vacuum?.live,explanation:{reason:E,notices:I}};return{...N,classes:Oo(N)}}const Ho=new WeakMap;function qo(t){const e=function(t){const e=t?.connection||t;return!e||"object"!=typeof e&&"function"!=typeof e?null:e}(t);if(!e)return null;let i=Ho.get(e);return i||(i={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},Ho.set(e,i)),i}function Bo(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function Uo(t,e){const i=qo(t);if(!i)return()=>{};i.refs++,i.listeners.add(e),async function(t,e){e.loading||"function"!=typeof t?.callWS?e.loading:e.loaded&&e.loadedAt&&Date.now()-e.loadedAt<3e5||(e.loading=(async()=>{const[i,s]=await Promise.allSettled([t.callWS({type:"config_entries/get"}),t.callWS({type:"manifest/list"})]);"fulfilled"===i.status&&Array.isArray(i.value)&&(e.configEntries=Object.fromEntries(i.value.filter(t=>"string"==typeof t?.entry_id).map(t=>[t.entry_id,t]))),"fulfilled"===s.status&&Array.isArray(s.value)&&(e.manifests=Object.fromEntries(s.value.filter(t=>"string"==typeof t?.domain).map(t=>[t.domain,t]))),e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Bo(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Bo(e)}),e.loading)}(t,i),async function(t,e){if(e.unsubscribe||e.subscribing)return e.subscribing;const i=t?.connection?.subscribeMessage;"function"==typeof i&&(e.subscribing=(async()=>{try{e.unsubscribe=await i.call(t.connection,t=>{const i=Array.isArray(t)?t:Array.isArray(t?.entries)?t.entries:t?[t]:[];let s=!1;const n={...e.configEntries};for(const t of i){const e=t?.entry||t?.data?.entry||("string"==typeof t?.entry_id?t:null),i=e?.entry_id;if("string"!=typeof i)continue;const o=t?.type||t?.action||t?.data?.action;"removed"===o||"remove"===o?delete n[i]:n[i]=e,s=!0}s&&(e.configEntries=n,Bo(e))},{type:"config_entries/subscribe"})}catch{}finally{0===e.refs&&(e.unsubscribe?.(),e.unsubscribe=void 0),e.subscribing=void 0}})(),e.subscribing)}(t,i);let s=!1;return()=>{s||(s=!0,i.listeners.delete(e),i.refs=Math.max(0,i.refs-1),i.refs||(i.unsubscribe?.(),i.unsubscribe=void 0))}}function Wo(t){const e=qo(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function jo(t,e,i=Wo(t),s){const n=i.manifests[e];return function(t,e){if("function"!=typeof t?.localize)return"";try{const i=`component.${e}.title`,s=t.localize(i);return"string"==typeof s&&s!==i?s:""}catch{return""}}(t,e)||String(n?.name||"")||String(s||"")||e.replace(/_/g," ")}function Go(t,e,i,s,n){const o=String(e||"").trim();if(!o)return null;const a=n?s.configEntries[n]:null;return{domain:o,label:jo(t,o,s,a?.title),configEntryId:n,configEntryTitle:a?.title,confidence:i}}function Vo(t){const e={"registry-owner":0,"entity-platform":1,"identifier-fallback":2},i=new Set;return t.filter(t=>!!t).sort((t,i)=>e[t.confidence]-e[i.confidence]||t.label.localeCompare(i.label)||t.domain.localeCompare(i.domain)).filter(t=>{const e=`${t.domain}\n${t.configEntryId||""}`;return!i.has(e)&&(i.add(e),!0)})}function Ko(t,e,i,s=Wo(t)){const n=i.entities?.[e]||t?.entities?.[e];if(!n)return null;const o=n.config_entry_id||void 0,a=o?s.configEntries[o]?.domain:"";return Go(t,n.platform||a,"entity-platform",s,o)}var Yo={"editor.context_actions":"Actions: {object}","editor.tool_options":"Tool options: {tool}","editor.palette":"Palette: {tool}","editor.open_group":"Tool group: {group}","editor.group_active":"{group} — active: {item}","editor.disabled_action":"{action} is unavailable: {reason}","btn.properties":"Properties","btn.keep_as_walls":"Keep as closed walls","title.markup_partition":"Partition: draw one independent physical wall","title.markup_select":"Select and edit walls, columns and saved outlines","title.markup_column":"Column: click a grid point to place a square column","markup.partition":"Partition","markup.column":"Column","markup.hint_partition":"two clicks draw one partition · Shift locks to 45°","markup.hint_column":"click a grid point to place a square column","history.draft_segment":"Add room-draft segment","history.draft_merge":"Join unfinished room outlines","history.draft_segment_delete":"Remove room-draft segment","history.partition_add":"Add partition","history.column_add":"Add column","history.physical_edit":"Change physical object","history.physical_delete":"Delete physical object","history.physical_move":"Move physical object","history.contour_to_partitions":"Convert contour to closed walls","toast.column_duplicate":"A column with the same centre and outer size already exists","confirm.delete_draft":"Delete the whole unfinished room contour?","confirm.delete_draft_segment":"Delete this draft segment? The remaining contour may split in two.","physical.partition_properties":"Partition properties","physical.column_properties":"Column properties","physical.draft_properties":"Draft segment properties","physical.shape":"Shape","physical.square":"Square","physical.circle":"Circle","physical.diameter":"Diameter","physical.side":"Side","physical.rotation":"Rotation angle","physical.length":"Length","physical.allowed_range":"Allowed: 1–{max} {unit}","physical.delete_segment":"Delete segment","physical.delete_draft":"Delete entire outline","physical.partition_size_title":"Thickness of the partition created by the second click (1–100 cm).","physical.column_size_title":"Outer side of the square column placed by the click (1–150 cm).","card.title":"House plan","count.devices":"{n} dev.","empty.no_spaces":"No spaces yet.","empty.add_first":"Add the first space and upload a floor plan.","empty.install":'Install the House Plan integration and add it in "Devices & services".',"btn.add_space":"Add space","btn.cancel":"Cancel","btn.save":"Save","btn.close":"Close","btn.delete":"Delete","btn.edit":"Edit","btn.open_in_ha":"Open in HA","btn.reset":"Reset","btn.attach":"Attach…","btn.upload":"Upload…","btn.replace":"Replace…","title.zoom_in":"Zoom in","title.zoom_out":"Zoom out","title.zoom_fit":"Fit all","title.add_device":"Add a device to the plan","title.show_all":"Show hidden and disabled devices as service markers (this tab only)","title.markup":"Room markup: grid, lines, outlines","title.configure_space":"Configure space","title.add_space":"Add space","title.markup_add":"Room outline: connect grid dots until the room closes; Shift locks the direction in 45° steps","title.markup_merge":"Merge: click one room, then the neighbour it shares a wall with","title.markup_split":"Split a room: click the room, then two points on its walls","title.markup_delroom":"Delete room: click inside a room and confirm","title.markup_boundary":"Boundary: click two points on a solid shared wall to make that stretch virtual; click a dashed stretch to restore it","title.need_plan":"Upload a floor-plan image","markup.add":"Room outline","markup.merge":"Merge","markup.split":"Split","markup.resize":"Resize","title.markup_resize":"Resize rooms: drag a wall handle; click a room for a corner scale frame","markup.hint_resize":"drag a wall handle · click a room — corner frame · Esc cancels a drag · Ctrl+Z — undo a step","markup.opening":"Opening","markup.boundary":"Boundary","markup.delete_room":"Delete room","history.undo":"Undo","history.redo":"Redo","history.undo_named":"Undo: {name} (Ctrl+Z)","history.redo_named":"Redo: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Nothing to undo","history.redo_empty":"Nothing to redo","history.undone":"Undone: {name}","history.redone":"Redone: {name}","history.add_room":"Create room","history.split_room":"Split room","history.merge_rooms":"Merge rooms","history.resize_room":"Resize room","history.open_boundary":"Open boundary","history.close_boundary":"Restore wall","history.wall_thickness":"Change wall thickness","history.add_opening":"Add door or window opening","history.edit_opening":"Edit opening","history.move_opening":"Move opening","history.delete_opening":"Delete opening","history.delete_room":"Delete room","history.decor_add":"Add decor object","history.decor_edit":"Edit decor object","history.decor_move":"Move decor object","history.decor_transform":"Transform decor object","history.decor_delete":"Delete decor object","history.backdrop_transform":"Transform plan backdrop","title.markup_opening":"Openings: click a wall to place, click an opening to edit","opening.new":"New opening","opening.edit":"Opening","opening.door":"Door","opening.window":"Window","opening.gate":"Gate","opening.type_label":"Type","opening.length_label":"Length, cm","opening.contact_label":"Open/close sensor","opening.lock_label":"Lock","opening.none":"— none —","opening.invert":"Invert open/closed","opening.flip_h":"Hinge on the other jamb","opening.flip_v":"Opens to the other side","opening.open":"Open","opening.closed":"Closed","opening.locked":"Locked","opening.unlocked":"Unlocked","opening.state_unknown":"unavailable","opening.no_entities":"No sensors bound — a static symbol on the plan.","toast.opening_no_wall":"Click next to a room wall — openings sit on walls","markup.delete":"Delete","markup.hint_points":"points: {n} · Shift — 45° steps · Esc/Ctrl+Z — undo a dot · first point or Ctrl/Cmd+click — close","markup.hint_start":"click a grid dot to start the outline","tip.lqi":"average zigbee signal:","tip.area":"area: {value}","info.device_header":"Device on the plan","info.model":"Model","info.state":"State","info.link":"Link","info.manuals":"Manuals","info.none":"No additional information","marker.new_device":"New device","marker.name_label":"Name (shown on the plan)","marker.name_ph":"Name","marker.binding_label":"Bind to an HA device","marker.binding_disabled":"disabled in Home Assistant","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 + dynamic plate","display.icon_ripple":"Icon + activity","display.static_icon":"Always static icon","marker.display_hint":"The dynamic plate shows work, open, alarm and unavailable states. Icon + activity also shows events and motion. Value instead of an icon shows one unambiguous HA state. A static icon does not react to device states.","marker.static_alarm_warning":"This device can report alarms. Static display hides the marker's visual alarm indication.","marker.preview.title":"Display preview","marker.preview.actual":"Now","marker.preview.example":"Example","marker.preview.integration":"Provided by","marker.preview.source":"Display source","marker.preview.current_state":"Current state","marker.preview.result":"On the plan","marker.preview.details":"Source details","marker.preview.select_source":"Choose a Home Assistant device or entity to see its actual display.","marker.preview.unknown_provider":"Unknown integration","marker.preview.virtual_provider":"House Plan · virtual device","marker.preview.no_source":"No active source","marker.preview.no_state":"No current state","marker.preview.mixed_states":"Several different states","marker.preview.multiple_sources":"{n} sources","marker.preview.more_sources":"+{n} more","marker.preview.scaled":"The preview is scaled to {n}% to fit. The saved size is unchanged.","marker.preview.demo_activity":"Show activity example","marker.preview.demo_notice":"Temporary activity example; the actual device state is unchanged.","marker.preview.demo_already_visible":"The current state already shows a real activity or alarm effect.","marker.preview.reduced_motion":"System reduced-motion is enabled, so the effect is shown as a static ring.","marker.preview.reason.neutral":"Neutral dark plate","marker.preview.reason.working":"Yellow plate: the device is working now","marker.preview.reason.working_activity":"Yellow plate and activity effect: the device is working now","marker.preview.reason.open":"Orange plate: physically open or unlocked","marker.preview.reason.cover_icon_state":"Neutral plate; the cover entity controls the displayed state and icon","marker.preview.reason.presence":"Activity effect while presence is detected","marker.preview.reason.event":"Short activity effect after a detected event","marker.preview.reason.transition":"Activity effect while the device is moving or changing state","marker.preview.reason.media_neutral":"Media devices stay on a neutral dark plate while available","marker.preview.reason.unavailable":"Subdued neutral plate: unavailable or off","marker.preview.reason.alarm":"Red alarm plate; alarm indication is always shown","marker.preview.reason.live_states_disabled":"Live-state styling is disabled for this card","marker.preview.reason.value_no_state":"No usable state value; the icon is shown instead","marker.preview.reason.value_ambiguous_sources":"Several possible value sources; the icon is shown instead","marker.preview.reason.value_non_scalar":"The source did not return a simple value; the icon is shown instead","marker.preview.reason.value_virtual":"A virtual device has no Home Assistant value; the icon is shown instead","marker.preview.reason.vacuum_live_plan_only":"Live vacuum position and trail are available only on the full plan","marker.preview.reason.hidden_design_preview":"The device is hidden on the plan; this design preview remains visible","marker.preview.reason.composite_power_source":"State comes from the device Power entity; auxiliary switches are ignored","marker.preview.reason.activity_display_disabled":"The selected display mode does not show ordinary activity effects","marker.preview.reason.ha_disabled":"The Home Assistant binding is disabled and will be hidden on the plan","marker.preview.reason.orphaned":"The saved Home Assistant binding can no longer be found","marker.preview.reason.static_icon":"Static mode: device state does not change the icon","marker.activity_color":"Effect color","marker.ripple_size":"Effect size","marker.size_label":"Icon size / rotation","marker.angle_label":"Rotate","marker.model_label":"Model","marker.model_ph":"e.g. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Description","marker.desc_ph":"Notes, specs…","marker.manuals_label":"Manuals (PDF etc.)","marker.sub_device":"device","marker.sub_z2m_group":" · Z2M group","marker.sub_group":"group","marker.sub_helper":"helper","space.new":"New space","space.header":"Space","space.title_label":"Title","space.title_ph":"e.g. Garage","space.plan_label":"Floor plan (background)","space.no_plan":"no plan image","space.plan_alt":"plan","room.new":"New room","room.name_label":"Display name","room.name_ph":"e.g. Terrace","room.area_label":"Home Assistant area (unassigned)","room.no_area_option":"— no area —","room.default_name":"Room","device.unnamed":"unnamed","device.light_group":"light group","device.fallback":"device","device.virtual":"virtual device","confirm.delete_room":'Delete room "{name}"?',"confirm.merge_rooms":'Merge rooms "{a}" and "{b}"?',"confirm.remove_marker":'Delete "{name}" from the plan? The device will disappear completely and stop contributing to plan data. You can add it again later.',"confirm.erase_decor":"Erase the {kind} object? You can undo this action from the editor history.","confirm.delete_space":'Delete space "{title}" with all its rooms and markup?',"toast.pos_save_failed":"Failed to save position: {err}","toast.no_entity":"The device has no suitable entity","toast.ha_disabled_action":"A Home Assistant object that is disabled cannot be used on the plan.","toast.ha_disabled_show_device":"A device disabled in Home Assistant cannot be shown on the plan. Enable it in Home Assistant first.","toast.ha_disabled_show_entity":"An entity disabled in Home Assistant cannot be shown on the plan. Enable it in Home Assistant first.","toast.ha_disabled_add":"A disabled Home Assistant object cannot be added to the plan. Enable it in Home Assistant first.","toast.ha_binding_unverified":"The object status could not be verified through the Home Assistant registry. Display and actions are temporarily unavailable.","toast.markup_needs_server":"Markup is available after the config is moved to the server","toast.conflict":"Config was changed in another window — data refreshed, repeat your last action","toast.cfg_save_failed":"Failed to save config: {err}","toast.room_overlap":"The outline overlaps room “{name}” — rooms must not overlap","toast.contour_min_edges":"Draw at least two edges before closing the outline","toast.contour_cannot_close":"The outline cannot close because it is degenerate or intersects itself","toast.merge_not_adjacent":"Only rooms that share a wall can be merged","toast.rooms_merged":"Rooms merged into “{name}”","toast.split_pick_wall":"Start the cut on the room’s wall","toast.split_bad_cut":"The cut must run wall to wall inside the room, without crossing walls or itself","merge.header":"Merge rooms","merge.hint":"The merged room keeps one name and one area. The other area is released — its devices leave the plan until another room claims it.","merge.keep":"Keep","merge.no_area":"no area","room.split_header":"New room from the split","toast.room_saved":"Room saved ({n}). Devices added: {added}. Outline the next one or exit markup.","toast.room_saved_no_area":"Room saved ({n}, no area). Outline the next one or exit markup.","toast.marker_needs_server":"Device editing is available after the config is moved to the server","toast.virtual_name_required":"Enter a name for the virtual device","toast.marker_saved":"Device saved","toast.marker_removed":"Device deleted from the plan","toast.integration_missing":"The House Plan integration is not installed — management unavailable","toast.plan_formats":"Supported formats: SVG, PNG, JPG, WebP","toast.plan_required":"Upload a floor plan — it is required","toast.space_added_onboard":"Space added. Outline the rooms: click grid dots and close the contour.","toast.space_added":"Space added","toast.space_saved":"Space saved","toast.space_deleted":"Space deleted","toast.delete_failed":"Delete failed: {err}","toast.error":"Error: {err}","toast.file_failed":'File "{name}" was not uploaded: {err}',"toast.files_attached":"Files attached: {n}","err.unknown":"unknown error","err.code":"code {code}","err.too_large":"file larger than {mb} MB","err.bad_ext":"unsupported type (PDF/image expected)","err.unauthorized":"administrator rights required","editor.title":"Title","editor.default_floor":"Default space","editor.icon_size":"Icon size, % of plan width","editor.show_temperature":"Show temperature","editor.live_states":"Live states (on/off, open…)","editor.show_signal":"Show zigbee signal (LQI)","editor.language":"Interface language","editor.lang_auto":"Auto (HA profile)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Icon rules: which MDI icon devices get by name","rules.title":"Icon rules","rules.hint":"Rules are checked top-down against “device name + model” (case-insensitive regex); the first match wins. When nothing matches, the entity device class decides, then the generic chip icon.","rules.pattern_ph":"regex, e.g. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Add rule","rules.reset":"Reset to defaults","rules.test_ph":"Try a device name…","rules.invalid":"invalid regex","rules.saved":"Icon rules saved","btn.up":"Up","btn.down":"Down","tap.info":"Device card","tap.more_info":"HA more-info dialog","tap.toggle":"Toggle state","marker.tap_label":"Tap action for this device","tap.toggle_note":"The resolved state is shown below. Secure devices are never toggled from the plan.","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","fill.custom":"Custom color","space.custom_fill":"Fill color","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…","continuity.restore_plan":"Restoring floor plan…","continuity.restore_connection":"Restoring device connection…","continuity.retry":"Try again","editor.space":"Space","editor.show_button":"Show button","editor.button_label":"Button label","editor.button_target":"Target dashboard path","marker.sub_entity":"entity","title.general_settings":"General settings","gs.title":"General settings","gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.light_group":"Fill: lights","gs.light_on":"Lights on","gs.light_off":"All lights off","gs.temp_group":"Fill: temperature","gs.temp_cold":"Cold","gs.temp_ok":"Comfortable","gs.temp_hot":"Hot","gs.lqi_group":"Fill: zigbee signal","gs.lqi_low":"Weak signal","gs.lqi_high":"Strong signal","gs.reset":"Reset to defaults","gs.saved":"General settings saved","space.show_lqi":"Show zigbee signal (LQI) next to devices","space.hide_decor":"Hide the decorative layer","space.hide_decor_tip":"Lines, shapes, labels and furniture stay where they are — visible in the backdrop editor, not on the plan.","space.hide_openings":"Hide openings","space.hide_openings_tip":"Door, window and gate 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":"Hidden and disabled","devbar.rules":"Icon rules","space.roomcard_section":"Room card shows:","space.label_temp":"Temperature","space.label_hum":"Humidity","space.label_lqi":"Average Zigbee signal","space.label_light":"Lights on/off","roomcard.light_on":"On","roomcard.light_off":"Off","roomcard.light_partial":"{on} of {total}","toast.split_pick_inside":"Intermediate cut points must be inside the room","mode.decor":"Background editor","decor.select":"Select","decor.line":"Line","decor.rect":"Rectangle","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Erase","decor.erase_confirm_title":"Erase object?","decor.color":"Color","decor.width":"Line width","decor.line_style":"Line style","decor.line_style_solid":"Solid","decor.line_style_dashed":"Dashed","decor.fill":"Fill","decor.fill_color":"Fill color","decor.length":"Length","decor.size":"Size","decor.angle":"Rotation angle, °","decor.text_size":"Text size","decor.backdrop_properties":"Plan backdrop properties","decor.text_title":"Text label","decor.object_title":"Edit {kind}","decor.text_label":"Text","decor.live_group":"Insert HA variable","decor.live_entity":"Entity","decor.live_entity_ph":"choose an entity","decor.live_attr":"Value","decor.live_attr_ph":"choose state or attribute","decor.live_state":"State","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag to move; pull a corner to resize; use the top handle to rotate. Shift changes proportions or frees the angle.","decor.backdrop_reset":"Reset the picture","decor.backdrop_reset_done":"The picture is back at its original place and size","marker.icon_auto":"Auto: {icon} (by icon rules; pick one to override)","marker.icon_pin_auto":"Pin","mode.plan_tip":"Plan editor — the geometry of the home: draw and split/merge rooms, bind them to HA areas, place doors, windows and gates, 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","space.glow_enabled":"Light-source glow","gs.glow_group":"Light-source glow","gs.glow_base":"House darkness","gs.glow_light":"Default light color / intensity","gs.wall_group":"Walls","gs.wall_fill":"Wall fill","gs.glow_radius":"Glow radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Controls other light sources","marker.controls_hint":"With tap action “Toggle”, a click flips every source added here. The marker’s own bound entity is controlled directly and cannot be added; use the switch below to classify it as a light.","marker.controls_filter":"Search lights and switches…","info.controls":"Controls","marker.glow_radius_label":"Glow radius","marker.glow_radius.help":"Sets the glow radius in metres or feet; an empty value uses the radius from general settings.","marker.glow_radius.help.aria":"Help: glow radius","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set how thick it is. Empty or zero removes the thickness.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new room-wall segment (1–100 cm). Shared walls keep the neighbour's value.","toast.wallthick_pick":"Click a wall (not an open boundary)","toast.wallthick_open":"Open boundaries have no thickness","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from 1 to {max} {unit}","toast.physical_angle":"Enter a rotation angle from 0° up to, but not including, 90°","toast.physical_limit":"The space has reached the limit for this type of geometry","markup.boundary_hint":"Select a shared room boundary or a dashed stretch","markup.boundary_hint_open":"Click the start of the open stretch","markup.boundary_hint_second":"Choose the end on the selected boundary · Esc to cancel","markup.boundary_hint_retry":"Choose the end on the selected boundary","markup.boundary_hint_restore":"Click to restore the wall","markup.boundary_hint_outer":"Only a boundary shared by two rooms can be opened","markup.boundary_hint_blocked":"a partition, column or unfinished contour is above this boundary","markup.boundary_hint_ambiguous":"Choose a stretch farther from the junction","markup.boundary_cancel":"Cancel the unfinished Boundary action","toast.openwall_pick":"This tool works with room boundaries","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.boundary_opened":"Boundary stretch opened","toast.boundary_restored":"Wall restored on this stretch","toast.boundary_same_edge":"Choose the end point on the selected wall","toast.boundary_ambiguous":"Choose a stretch slightly farther from the junction","toast.boundary_blocked":"This boundary is covered by independent masonry. Move or delete it with Select first","toast.delete_room_pick":"Click inside the room you want to delete","toast.openwall_openings_removed":"Openings on the virtual stretch were removed","toast.openwall_opened":"Boundary “{a}” ↔ “{b}” is now open","toast.openwall_closed":"Boundary “{a}” ↔ “{b}” is closed again","toast.opening_on_virtual":"Openings cannot sit on a virtual wall","marker.from_ha_option":"Pick from the HA list","marker.show_entities":"Show entities","marker.show_entities_tip":"Adds not only devices to the list, but all their entities too","marker.pick_ph":"Choose a device…","room.open_area":"Open the HA area","kiosk.title":"This screen's sizes","kiosk.hint":"Stored on this device only — every wall tablet or TV can have its own comfortable sizes.","kiosk.icon_scale":"Device icon size","kiosk.font_scale":"Room card text size","editor.kiosk":"Wall device (kiosk) mode","editor.cycle":"Auto-switch spaces every N seconds (kiosk, 0 = off)","room.settings_title":"Room settings","room.settings_section":"Room settings (override the space)","room.fill_label":"Fill in THIS room","fill.inherit":"As the space","room.custom_fill_space":"Space color","room.custom_fill_own":"Room color","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":"Include the device temperature in the room","marker.use_climate_temp_tip":"Adds an air conditioner or thermostat current_temperature to the room average. Configure the external value badge separately below.","marker.value_badge_title":"Value badge","marker.value_badge.help":"Shows one selected value next to the icon. It does not affect room metrics, light, Glow or the tap action.","marker.value_badge.help.aria":"Help: device value badge","marker.value_badge_enabled":"Show a value badge","marker.value_badge_source":"Value","marker.value_badge_source.help":"Choose a specific state, supported attribute, or derived value of this device.","marker.value_badge_source.help.aria":"Help: value badge source","marker.value_badge_position":"Position","marker.value_badge_position.help":"The chosen side stays fixed and does not flip automatically at a plan edge.","marker.value_badge_position.help.aria":"Help: value badge position","marker.value_badge_right":"Right","marker.value_badge_bottom":"Below","marker.value_badge_left":"Left","marker.value_badge_top":"Above","marker.value_badge_empty":"This device has no available values","marker.value_badge_static":"A static icon does not show live values. The setting is preserved.","marker.value_badge_missing":"Source unavailable","marker.value_badge_missing_hint":"The saved source is currently unavailable. Keep it, disable the badge, or choose another value.","marker.value_badge_duplicate":"The same value is already shown inside the icon.","marker.value_badge_state":"State · {name}","marker.value_badge_marker_state":"Light-source state · {name}","marker.value_badge_lqi":"Average Zigbee signal quality","marker.value_badge_attr_current_temperature":"Current temperature · {name}","marker.value_badge_attr_temperature":"Target temperature · {name}","marker.value_badge_attr_current_humidity":"Current humidity · {name}","marker.value_badge_attr_humidity":"Humidity · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Speed · {name}","marker.value_badge_attr_brightness":"Brightness · {name}","marker.value_badge_attr_volume_level":"Volume · {name}","marker.value_badge_attr_battery_level":"Battery · {name}","marker.value_badge_attr_fan_speed":"Fan speed · {name}","marker.light_role_label":"Is this device a light source?","marker.light_role.help":"Controls whether this marker itself is a spatial light source. Linked lights above remain independent room-light controls.","marker.light_role.help.aria":"Help: whether this device is a light source","marker.light_role_auto_yes":"Auto (light source)","marker.light_role_auto_no":"Auto (not a light source)","marker.light_role_always":"Always a light source","marker.light_role_never":"Never a light source","marker.light_entity_label":"Leading light entity","marker.light_entity.help":"For a composite device, choose the entity whose state, colour and brightness represent this plan source. The automatic fallback remains compatible with older plans.","marker.light_entity.help.aria":"Help: leading light entity","marker.light_entity_auto":"Automatic ({entity})","marker.light_entity_none":"no controllable entity","marker.light_entity_missing":"The saved entity {entity} is unavailable. House Plan temporarily uses {fallback}; the saved choice will be restored if it returns.","marker.glow_color_label":"Glow colour and brightness","marker.glow_mode.help":"Use live source values, override only its colour, or fix both colour and brightness. The minimum is 1%; to disable the source, choose “Never”.","marker.glow_mode.help.aria":"Help: glow colour and brightness","marker.glow_mode_auto":"From source","marker.glow_mode_color":"Set colour","marker.glow_mode_fixed":"Set colour and brightness","marker.glow_color":"Glow colour","marker.glow_brightness":"Brightness","marker.glow_disabled_never":"Glow settings are unavailable because this marker is explicitly not a light source.","marker.glow_disabled_auto":"Auto mode found no spatial light source for this marker.","marker.glow_disabled_no_entity":"No active controllable Home Assistant entity is available for this spatial source.","marker.glow_passive_hint":"This source has no own Home Assistant data. Set its colour and brightness manually; its radius remains available.","marker.control_broken":"Saved source is missing or no longer marked as a light","marker.control_passive":"passive source","toast.marker_control_cycle":"This link would create a circular chain of light controls.","toast.marker_binding_required":"Choose a Home Assistant device before linking another light source.","confirm.unlock":"Unlock “{name}”?","toast.files_migrate_failed":"Attachments could not be moved to the new binding, links keep pointing at the old files: {err}","space.pick_saved":"Already uploaded","space.pick_saved_hint":"Plans stored on the server, including ones you detached earlier","space.no_saved":"No plans stored on the server yet.","space.loading":"Loading…","space.used_by":"in use: {list}","space.in_use":"A space still uses this plan — detach it first","btn.use":"Use","confirm.delete_plan":'Delete the plan file "{name}" from the server? This cannot be undone.',"toast.plans_list_failed":"Could not list the stored plans: {err}","toast.plan_delete_failed":"Could not delete the plan: {err}","marker.hide":"Hide","marker.hide_tip":'The device will disappear from the plan after saving but will still count toward the room signal. Restore it through "Hidden and disabled" in the device editor.',"marker.show":"Show","marker.show_tip":"The device will appear on the plan again after saving.","marker.hidden_ghost":"Device hidden by the user","marker.ha_disabled_device":"The device is disabled in Home Assistant and hidden from the plan.","marker.ha_disabled_entity":"The entity is disabled in Home Assistant and hidden from the plan.","marker.ha_disabled_all_entities":"The device has no active Home Assistant entities, so it is hidden from the plan.","marker.ha_registry_limited":"The full Home Assistant registry is unavailable to this user. The unverified object cannot be shown or used for now.","marker.delete_tip":"Completely delete the device from the plan and every aggregate. You can add it again later.","tap.run":"Run automation/script/scene","tap.cover":"Open/close (curtains/blinds)","marker.run_target_label":"What to run","marker.run_search_ph":"Search: automation, script or scene…","marker.run_target_gone":"Target {id} not found — pick again","marker.tap_confirm":"Ask for confirmation","marker.tap_confirm_tip":"Show a confirmation dialog before acting — a guard against accidental taps.","marker.toggle_hint_single":"Target: {name} ({id}).","marker.toggle_hint_group":"Will toggle {count} source(s): {names}.","marker.toggle_hint_current":"Now: {state} → {effect}.","marker.toggle_hint_group_current":"Currently on: {on} of {count} → {effect}.","marker.toggle_hint_skipped":"Will be skipped ({count}): {targets}.","marker.toggle_effect_turn_on":"will turn on","marker.toggle_effect_turn_off":"will turn off","marker.toggle_effect_open":"will open","marker.toggle_effect_close":"will close","marker.toggle_effect_stop":"will stop","marker.toggle_effect_toggle":"state will toggle","marker.toggle_skip_missing":"not found","marker.toggle_skip_ha_disabled":"disabled in HA","marker.toggle_skip_unavailable":"unavailable","marker.toggle_skip_unsupported":"toggle is unsupported","marker.toggle_skip_secure":"blocked for security","marker.toggle_none_no_binding":"This device has no binding, so there is no state to toggle. Tapping will do nothing.","marker.toggle_none_no_actionable_entity":"This device has no state that can be toggled. Tapping will do nothing.","marker.toggle_none_configured_targets_missing":"The configured targets are unavailable. The device's own entity will not be substituted for them.","marker.toggle_none_ha_disabled":"The target is disabled in Home Assistant and cannot be used on the plan.","marker.toggle_none_unavailable":"The target is currently unavailable. Tapping will do nothing.","marker.toggle_none_unsupported":"Home Assistant exposes no safe toggle operation for this entity. Tapping will do nothing.","marker.toggle_none_secure":"Toggling locks, alarms and secure gates from the plan is blocked for security.","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","toast.tap_target_changed":"The action target changed. Try again.","toast.value_badge_source_required":"Choose a value for the badge","btn.run":"Run","vac.section":"Robot vacuum: live position","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_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.about_group":"About","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · docs & issues","gs.about_telegram":"Telegram chat","space.bg_color":"Background around the plan","space.bg_inherit":"Inherit general","space.bg_inherited":"inherits general settings","space.bg_mode":"Plan background","space.north":"North on the plan (override)","space.north_inherited":"inherited: {v}","space.sun_rays":"Sunlight through windows","space.sun_inherit":"Inherit general","space.sun_on":"On","space.sun_off":"Off","canvas.far_objects":"{n} object(s) far from the plan","canvas.show_far":"Show","canvas.home_tip":"The plan is over there — click to fit it","gs.grid_group":"Plan maintenance","gs.grid_hint":"Updates data models, aligns plan elements to the grid and merges redundant wall fragments. An exact report is shown before anything is stored.","gs.align_all":"Optimize plans","gs.align_title":"Optimize plans","gs.align_none":"All plans already use the current optimized data model.","gs.align_count":"{n} of {total} elements will move, by at most {cm} cm.","gs.align_where":"The largest shift is in “{s}”.","gs.align_turned":"Openings whose angle is corrected: {n}.","gs.align_removed_drafts":"Invalid outlines collapsed by the grid and removed: {n}.","gs.optimize_changes":"Model migrations: {m}; plans canonicalized: {c}; merged real-wall fragments: {w}; virtual fragments: {s}.","gs.optimize_glow_migration":"Legacy Glow: {spaces} spaces → no data fill + independent Glow; {rooms} rooms → inherited data fill + independent Glow.","gs.align_warn":"Elements deliberately placed between grid nodes will move. One undo is available after the operation, only until the next plan edit.","gs.align_run":"Optimize","gs.align_done":"Plans optimized: {n} elements moved, {m} records maintained","gs.optimize_undo":"Undo last optimization","gs.optimize_undone":"The last optimization was undone","decor.furniture":"Furniture","furn.title":"Furniture library","furn.symbol":"Symbol","furn.group_furniture":"Furniture","furn.group_appliance":"Appliances","furn.group_sanitary":"Plumbing","furn.group_other":"Other","furn.width":"Width","furn.depth":"Depth","furn.pick_hint":"Pick a symbol, then click on the plan.","furn.place_hint":"Click on the plan — the piece lands against the nearest wall. Shift places it free.","furn.sym_sofa":"Sofa","furn.sym_armchair":"Armchair","furn.sym_coffee_table":"Coffee table","furn.sym_table_dining":"Dining table","furn.sym_table_round":"Round table","furn.sym_chair":"Chair","furn.sym_desk":"Desk","furn.sym_bed_double":"Double bed","furn.sym_bed_single":"Single bed","furn.sym_nightstand":"Nightstand","furn.sym_wardrobe":"Wardrobe","furn.sym_bookshelf":"Bookshelf","furn.sym_fridge":"Fridge","furn.sym_stove":"Cooker","furn.sym_dishwasher":"Dishwasher","furn.sym_washer":"Washing machine","furn.sym_dryer":"Tumble dryer","furn.sym_tv":"TV","furn.sym_ac":"Air conditioner","furn.sym_water_heater":"Water heater","furn.sym_toilet":"Toilet","furn.sym_bathtub":"Bathtub","furn.sym_shower":"Shower","furn.sym_sink":"Washbasin","furn.sym_kitchen_sink":"Kitchen sink","furn.sym_bidet":"Bidet","furn.sym_stairs":"Stairs","furn.sym_fireplace":"Fireplace","furn.sym_plant":"Plant","furn.sym_rug":"Rug","common.yes":"Yes","common.no":"No","vac.diag_source":"Source","vac.diag_platform":"Integration","vac.diag_status":"Status","vac.diag_position":"Position","vac.diag_rooms":"Rooms","vac.diag_rooms_value":"{total} · {matched} names match · {readiness}","vac.autocal_ready":"auto-calibration available","vac.autocal_not_ready":"need 3 matching names","vac.diag_path":"Integration path","vac.diag_map":"Map ID","vac.source_none":"not selected","vac.source_status_ok":"Ready","vac.source_status_missing":"Missing","vac.source_status_disabled":"Disabled in Home Assistant","vac.source_status_unavailable":"Unavailable","vac.source_status_unverified":"Cannot verify with current permissions","vac.source_status_unsupported":"No position data","vac.source_status_none":"No source","vac.source_banner_missing":"The saved source no longer exists. It was not replaced automatically; choose another source or restore it in Home Assistant.","vac.source_banner_disabled":"The saved source is disabled in Home Assistant. Enable it there or choose another source.","vac.source_banner_unverified":"Current Home Assistant permissions cannot verify this saved source. It remains pinned and will not be replaced automatically.","vac.choose_source":"Choose source","vac.source_auto":"Automatic","vac.source_auto_hint":"Use a compatible entity from this device","vac.all_cameras":"All cameras","vac.all_cameras_warn":"A camera may not provide robot data. Choose one only if it is your vacuum map.","vac.all_cameras_empty":"No other camera entities found.","vac.platform_unknown":"unknown integration","vac.cap_position":"position","vac.cap_rooms_short":"rooms","vac.cap_path":"path","vac.cap_map":"map ID","vac.cap_none":"no robot data detected","vac.xcme_hint":"Enable these Xiaomi Cloud Map Extractor attributes:","vac.documentation":"Documentation","vac.residual_title":"Check automatic calibration","vac.residual_message":"The matched rooms disagree by up to {error}. Apply this approximate calibration, refine it manually, or cancel without changing the saved setup.","vac.apply_proposal":"Apply","gs.backup_group":"Backup and transfer","gs.backup_hint":"Download a portable JSON backup or preview and import a backup made by House Plan.","backup.export_open":"Export","backup.import_open":"Import","backup.export_title":"Export House Plan","backup.import_title":"Import House Plan","backup.export_hint":"Choose whether the backup should contain the whole House Plan configuration or only the current space.","backup.full":"Full backup","backup.current_space":"Current space","backup.current_space_title":"Current space: {title}","backup.no_current_space":"No current space","backup.privacy_warning":"The archive keeps names, Home Assistant identifiers and exact coordinates. Internal plans and attachments are referenced, not embedded; runtime states and vacuum trails are not included.","backup.download":"Download JSON","backup.export_done":"Backup downloaded","backup.reading":"Checking the backup…","backup.revalidated":"The plan changed after this preview. The summary was refreshed; review it and confirm again.","backup.error.unauthorized":"You do not have permission to export or import this plan.","backup.error.not_ready":"House Plan is not ready yet. Try again in a moment.","backup.error.too_large":"The backup exceeds the 8 MiB limit.","backup.error.invalid_json":"The selected file is not valid JSON.","backup.error.invalid_format":"The selected file is not a House Plan backup.","backup.error.unsupported_export_version":"This backup format is not supported by the installed version.","backup.error.future_model":"The backup was created by a newer House Plan data model.","backup.error.invalid_config":"The backup contains an invalid House Plan configuration.","backup.error.invalid_layout":"The backup contains invalid object positions.","backup.error.invalid_content":"The backup contains invalid or inconsistent content references.","backup.error.space_not_found":"The selected space no longer exists.","backup.error.capacity_exceeded":"Adding this backup would exceed the plan limits.","backup.error.preview_expired":"The preview expired. Select the backup file again.","backup.error.preview_owner_mismatch":"This preview belongs to another Home Assistant user.","backup.error.conflict":"The plan changed after the preview. Review the refreshed summary.","backup.error.content_confirmation_required":"Confirm that unavailable local content may be detached.","backup.error.commit_failed":"The backup could not be applied safely. The previous plan was restored or is pending recovery.","backup.error.missing_plan":"A referenced plan file disappeared after the preview. Review the backup again.","backup.error.marker_control_missing":"A linked plan light is missing from the backup.","backup.error.marker_control_not_light":"A linked plan target is no longer marked as a light source.","backup.error.marker_control_self":"A light source cannot control itself.","backup.error.marker_control_cycle":"The backup contains a circular chain of light controls.","backup.error.duplicate_marker_control":"The backup contains a duplicate plan-light link.","backup.error.no_backup":"There is no import or optimization snapshot to restore.","backup.same_source":"Created on this Home Assistant instance","backup.foreign_source":"Created on another Home Assistant instance","backup.created":"Created: {value}","backup.versions":"Card {card}; integration {integration}; data model {model}","backup.count_spaces":"Spaces: {n}","backup.count_rooms":"Rooms: {n}","backup.count_walls":"Walls: {n}","backup.count_openings":"Openings: {n}","backup.count_decor":"Decor objects: {n}","backup.count_markers":"Devices: {n}","backup.count_layout":"Positions: {n}","backup.bindings":"Bindings — devices: {device}, entities: {entity}, virtual: {virtual}; positions without a space: {legacy}","backup.binding_status":"Target status — active: {active}, disabled: {disabled}, missing: {missing}","backup.missing_areas":"Areas missing on the target: {areas}","backup.dropped_marker_links":"Plan-light links outside this transferred space were omitted: {n}.","backup.replace_warning":"This replaces the current configuration and layout. Uploaded files are never deleted. One undo remains available until the next plan edit.","backup.foreign_bookkeeping":"Instance-specific known/new-device bookkeeping will not be imported.","backup.final_name":"New space name","backup.target_settings":"The target instance's global settings remain unchanged.","backup.duplicates":"Bindings already present: {n}","backup.skip":"Skip duplicate devices","backup.virtual_copy":"Add safe static virtual copies","backup.content":"Referenced content","backup.content_available":"available locally","backup.content_external":"external link","backup.content_detach_required":"will be detached","backup.confirm_detach":"I understand that unavailable internal plans and attachments will be detached from the imported configuration.","backup.replace":"Replace","backup.add":"Add space","backup.space_done":"Space imported: {rooms} rooms, {markers} devices","backup.full_done":"Backup restored: {spaces} spaces, {rooms} rooms, {markers} devices","backup.undo_import":"Undo last full import","backup.import_undone":"Full import undone"};const Jo={en:Yo,ru:{"editor.context_actions":"Действия: {object}","editor.tool_options":"Параметры инструмента: {tool}","editor.palette":"Палитра: {tool}","editor.open_group":"Группа инструментов: {group}","editor.group_active":"{group} — активно: {item}","editor.disabled_action":"{action} недоступно: {reason}","btn.properties":"Свойства","btn.keep_as_walls":"Оставить замкнутыми стенами","title.markup_partition":"Перегородка: нарисуйте одну независимую физическую стену","title.markup_select":"Выбор и редактирование стен, колонн и сохранённых контуров","title.markup_column":"Колонна: кликните точку сетки для квадратной колонны","markup.partition":"Перегородка","markup.column":"Колонна","markup.hint_partition":"два клика рисуют одну перегородку · Shift фиксирует шаг 45°","markup.hint_column":"кликните точку сетки, чтобы поставить квадратную колонну","history.draft_segment":"Добавление сегмента черновика комнаты","history.draft_merge":"Соединение незавершённых контуров комнаты","history.draft_segment_delete":"Удаление сегмента черновика комнаты","history.partition_add":"Добавление перегородки","history.column_add":"Добавление колонны","history.physical_edit":"Изменение физического объекта","history.physical_delete":"Удаление физического объекта","history.physical_move":"Перемещение физического объекта","history.contour_to_partitions":"Преобразование контура в замкнутые стены","toast.column_duplicate":"Колонна с тем же центром и внешним размером уже существует","confirm.delete_draft":"Удалить весь незавершённый контур комнаты?","confirm.delete_draft_segment":"Удалить этот сегмент черновика? Оставшийся контур может разделиться на два.","physical.partition_properties":"Свойства перегородки","physical.column_properties":"Свойства колонны","physical.draft_properties":"Свойства сегмента черновика","physical.shape":"Форма","physical.square":"Квадрат","physical.circle":"Круг","physical.diameter":"Диаметр","physical.side":"Сторона","physical.rotation":"Угол поворота","physical.length":"Длина","physical.allowed_range":"Допустимо: 1–{max} {unit}","physical.delete_segment":"Удалить сегмент","physical.delete_draft":"Удалить весь контур","physical.partition_size_title":"Толщина перегородки, создаваемой вторым кликом (1–100 см).","physical.column_size_title":"Внешняя сторона квадратной колонны, создаваемой кликом (1–150 см).","card.title":"План дома","count.devices":"{n} устр.","empty.no_spaces":"Пространств пока нет.","empty.add_first":"Добавьте первое пространство и загрузите план этажа.","empty.install":"Установите интеграцию House Plan и добавьте запись в «Устройства и службы».","btn.add_space":"Добавить пространство","btn.cancel":"Отмена","btn.save":"Сохранить","btn.close":"Закрыть","btn.delete":"Удалить","btn.edit":"Редактировать","btn.open_in_ha":"Открыть в HA","btn.reset":"Сброс","btn.attach":"Прикрепить…","btn.upload":"Загрузить…","btn.replace":"Заменить…","title.zoom_in":"Приблизить","title.zoom_out":"Отдалить","title.zoom_fit":"Вписать всё","title.add_device":"Добавить устройство на план","title.show_all":"Показать скрытые и деактивированные устройства служебными маркерами (только в этой вкладке)","title.markup":"Разметка комнат: сетка, линии, контуры","title.configure_space":"Настроить пространство","title.add_space":"Добавить пространство","title.markup_add":"Контур комнаты: соединяйте точки сетки линиями до замыкания комнаты; Shift фиксирует направление с шагом 45°","title.markup_merge":"Объединить: клик по одной комнате, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить комнату: кликните внутри комнаты и подтвердите удаление","title.markup_boundary":"Граница: две точки на сплошной общей стене делают участок виртуальным; клик по пунктиру восстанавливает стену","title.need_plan":"Загрузите подложку (план этажа)","markup.add":"Контур комнаты","markup.merge":"Объединить","markup.split":"Разделить","markup.resize":"Размер","title.markup_resize":"Изменение размера комнат: тяните ручку стены; клик по комнате — угловая рамка масштаба","markup.hint_resize":"тяните ручку стены · клик по комнате — угловая рамка · Esc отменяет перетаскивание · Ctrl+Z — отмена шага","markup.opening":"Проём","markup.boundary":"Граница","markup.delete_room":"Удалить комнату","history.undo":"Отменить","history.redo":"Повторить","history.undo_named":"Отменить: {name} (Ctrl+Z)","history.redo_named":"Повторить: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Нет операций для отмены","history.redo_empty":"Нет операций для повтора","history.undone":"Отменено: {name}","history.redone":"Повторено: {name}","history.add_room":"Создание комнаты","history.split_room":"Разделение комнаты","history.merge_rooms":"Объединение комнат","history.resize_room":"Изменение размера комнаты","history.open_boundary":"Открытие границы","history.close_boundary":"Восстановление стены","history.wall_thickness":"Изменение толщины стены","history.add_opening":"Добавление дверного или оконного проёма","history.edit_opening":"Изменение проёма","history.move_opening":"Перемещение проёма","history.delete_opening":"Удаление проёма","history.delete_room":"Удаление комнаты","history.decor_add":"Добавление объекта декора","history.decor_edit":"Изменение объекта декора","history.decor_move":"Перемещение объекта декора","history.decor_transform":"Трансформация объекта декора","history.decor_delete":"Удаление объекта декора","history.backdrop_transform":"Трансформация подложки плана","title.markup_opening":"Проёмы: клик по стене — добавить, клик по проёму — редактировать","opening.new":"Новый проём","opening.edit":"Проём","opening.door":"Дверь","opening.window":"Окно","opening.gate":"Ворота","opening.type_label":"Тип","opening.length_label":"Длина, см","opening.contact_label":"Датчик открытия","opening.lock_label":"Замок","opening.none":"— нет —","opening.invert":"Инвертировать открыто/закрыто","opening.flip_h":"Петли с другой стороны","opening.flip_v":"Открывается в другую сторону","opening.open":"Открыто","opening.closed":"Закрыто","opening.locked":"Заперто","opening.unlocked":"Не заперто","opening.state_unknown":"недоступно","opening.no_entities":"Датчики не привязаны — статичный символ на плане.","toast.opening_no_wall":"Кликните рядом со стеной комнаты — проёмы ставятся на стены","markup.delete":"Удалить","markup.hint_points":"точек: {n} · Shift — шаг 45° · Esc/Ctrl+Z — убрать точку · клик по первой или Ctrl/Cmd+клик — замкнуть","markup.hint_start":"кликните точку сетки, чтобы начать контур","tip.lqi":"средний сигнал zigbee:","tip.area":"площадь: {value}","info.device_header":"Устройство на плане","info.model":"Модель","info.state":"Состояние","info.link":"Ссылка","info.manuals":"Инструкции","info.none":"Нет дополнительной информации","marker.new_device":"Новое устройство","marker.name_label":"Имя (отображается на плане)","marker.name_ph":"Название","marker.binding_label":"Привязка к устройству HA","marker.binding_disabled":"деактивировано в Home Assistant","marker.virtual_option":"Виртуальное устройство (без привязки)","marker.search_ph":"Поиск устройства / группы…","marker.nothing_found":"ничего не найдено","marker.room_label":"Комната","marker.room_override":" (переопределить размещение)","marker.room_choose":"— выберите комнату —","marker.room_auto":"— по зоне устройства (авто) —","marker.icon_label":"Иконка","marker.icon_ph":"mdi:… (пусто = авто)","marker.display_label":"Отображение","display.badge":"Значок + динамическая подложка","display.icon_ripple":"Значок + активность","display.static_icon":"Всегда статичный значок","marker.display_hint":"Динамическая подложка показывает работу, открытие, тревогу и недоступность. Значок + активность также показывает события и движение. Значение вместо иконки выводит одно однозначное состояние HA. Статичный значок не реагирует на состояния устройства.","marker.static_alarm_warning":"Это устройство может сообщать о тревогах. Статичный режим скрывает визуальную тревогу маркера.","marker.preview.title":"Предпросмотр отображения","marker.preview.actual":"Сейчас","marker.preview.example":"Пример","marker.preview.integration":"Интеграция","marker.preview.source":"Источник отображения","marker.preview.current_state":"Текущее состояние","marker.preview.result":"На плане","marker.preview.details":"Подробнее об источниках","marker.preview.select_source":"Выберите устройство или сущность Home Assistant, чтобы увидеть фактическое отображение.","marker.preview.unknown_provider":"Интеграция неизвестна","marker.preview.virtual_provider":"House Plan · виртуальное устройство","marker.preview.no_source":"Нет активного источника","marker.preview.no_state":"Нет текущего состояния","marker.preview.mixed_states":"Несколько разных состояний","marker.preview.multiple_sources":"Источников: {n}","marker.preview.more_sources":"ещё {n}","marker.preview.scaled":"Предпросмотр уменьшен до {n}%, чтобы поместиться. Сохранённый размер не изменится.","marker.preview.demo_activity":"Показать пример активности","marker.preview.demo_notice":"Временный пример активности; фактическое состояние устройства не меняется.","marker.preview.demo_already_visible":"Текущее состояние уже показывает реальную активность или тревогу.","marker.preview.reduced_motion":"В системе включено уменьшение движения, поэтому эффект показан статичным кольцом.","marker.preview.reason.neutral":"Нейтральная тёмная подложка","marker.preview.reason.working":"Жёлтая подложка: устройство сейчас работает","marker.preview.reason.working_activity":"Жёлтая подложка и эффект активности: устройство сейчас работает","marker.preview.reason.open":"Оранжевая подложка: физически открыто или разблокировано","marker.preview.reason.cover_icon_state":"Нейтральная подложка; состояние и значок определяет сущность шторы или заслонки","marker.preview.reason.presence":"Эффект активности, пока обнаружено присутствие","marker.preview.reason.event":"Короткий эффект активности после события","marker.preview.reason.transition":"Эффект активности во время движения или смены состояния","marker.preview.reason.media_neutral":"Медиаустройство остаётся на нейтральной тёмной подложке, пока доступно","marker.preview.reason.unavailable":"Приглушённая нейтральная подложка: отключено или недоступно","marker.preview.reason.alarm":"Красная тревожная подложка; тревога отображается всегда","marker.preview.reason.live_states_disabled":"Оформление по живым состояниям отключено в настройках карточки","marker.preview.reason.value_no_state":"Нет подходящего значения состояния — вместо него показан значок","marker.preview.reason.value_ambiguous_sources":"Подходит несколько источников значения — вместо него показан значок","marker.preview.reason.value_non_scalar":"Источник вернул не простое значение — вместо него показан значок","marker.preview.reason.value_virtual":"У виртуального устройства нет значения HA — вместо него показан значок","marker.preview.reason.vacuum_live_plan_only":"Живая позиция и след пылесоса доступны только на полном плане","marker.preview.reason.hidden_design_preview":"Устройство скрыто на плане, но его оформление видно в предпросмотре","marker.preview.reason.composite_power_source":"Состояние берётся из сущности питания устройства; вспомогательные переключатели не учитываются","marker.preview.reason.activity_display_disabled":"Выбранный режим отображения не показывает обычные эффекты активности","marker.preview.reason.ha_disabled":"Привязка отключена в Home Assistant, поэтому устройство будет скрыто на плане","marker.preview.reason.orphaned":"Сохранённая привязка Home Assistant больше не найдена","marker.preview.reason.static_icon":"Статичный режим: состояние устройства не меняет значок","marker.activity_color":"Цвет эффекта","marker.ripple_size":"Размер эффекта","marker.size_label":"Размер / поворот значка","marker.angle_label":"Поворот","marker.model_label":"Модель","marker.model_ph":"напр. Aqara T&H","marker.link_label":"Ссылка","marker.desc_label":"Описание","marker.desc_ph":"Заметки, характеристики…","marker.manuals_label":"Инструкции (PDF и т.п.)","marker.sub_device":"устройство","marker.sub_z2m_group":" · Z2M-группа","marker.sub_group":"группа","marker.sub_helper":"хелпер","space.new":"Новое пространство","space.header":"Пространство","space.title_label":"Название","space.title_ph":"Например: Гараж","space.plan_label":"Подложка (план)","space.no_plan":"нет подложки","space.plan_alt":"план","room.new":"Новая комната","room.name_label":"Отображаемое имя","room.name_ph":"Например: Терраса","room.area_label":"Зона Home Assistant (свободные)","room.no_area_option":"— без зоны —","room.default_name":"Комната","device.unnamed":"без имени","device.light_group":"группа света","device.fallback":"устройство","device.virtual":"виртуальное устройство","confirm.delete_room":"Удалить комнату «{name}»?","confirm.merge_rooms":"Слить комнаты «{a}» и «{b}»?","confirm.remove_marker":"Удалить «{name}» с плана? Устройство исчезнет полностью и перестанет участвовать в данных плана. Позже его можно будет добавить заново.","confirm.erase_decor":"Стереть объект «{kind}»? Действие можно отменить из истории редактора.","confirm.delete_space":"Удалить пространство «{title}» со всеми комнатами и разметкой?","toast.pos_save_failed":"Не удалось сохранить позицию: {err}","toast.no_entity":"У устройства нет подходящей сущности","toast.ha_disabled_action":"Деактивированный объект Home Assistant нельзя использовать на плане.","toast.ha_disabled_show_device":"Деактивированное в Home Assistant устройство нельзя показать на плане. Сначала активируйте его в Home Assistant.","toast.ha_disabled_show_entity":"Деактивированную в Home Assistant сущность нельзя показать на плане. Сначала активируйте её в Home Assistant.","toast.ha_disabled_add":"Деактивированный объект Home Assistant нельзя добавить на план. Сначала активируйте его в Home Assistant.","toast.ha_binding_unverified":"Статус объекта не удалось подтвердить по реестру Home Assistant. Отображение и действия временно недоступны.","toast.markup_needs_server":"Разметка доступна после переноса конфига на сервер","toast.conflict":"Конфиг изменён в другом окне — данные обновлены, повторите последнее действие","toast.cfg_save_failed":"Не удалось сохранить конфиг: {err}","toast.room_overlap":"Контур накладывается на комнату «{name}» — комнаты не должны накладываться","toast.contour_min_edges":"Чтобы замкнуть контур, сначала нарисуйте минимум две грани","toast.contour_cannot_close":"Контур нельзя замкнуть: он вырожден или пересекает сам себя","toast.merge_not_adjacent":"Объединять можно только комнаты с общей стеной","toast.rooms_merged":"Комнаты объединены в «{name}»","toast.split_pick_wall":"Начните разрез на стене комнаты","toast.split_bad_cut":"Разрез — от стены до стены внутри комнаты, без пересечения стен и самого себя","merge.header":"Объединение комнат","merge.hint":"У объединённой комнаты одно имя и одна зона. Вторая зона освобождается — её устройства уйдут с плана, пока их не заберёт другая комната.","merge.keep":"Оставить","merge.no_area":"без зоны","room.split_header":"Новая комната после разделения","toast.room_saved":"Комната сохранена ({n}). Устройств добавлено: {added}. Обведите следующую или выйдите из разметки.","toast.room_saved_no_area":"Комната сохранена ({n}, без зоны). Обведите следующую или выйдите из разметки.","toast.marker_needs_server":"Редактирование устройств доступно после переноса конфига на сервер","toast.virtual_name_required":"Укажите имя виртуального устройства","toast.marker_saved":"Устройство сохранено","toast.marker_removed":"Устройство удалено с плана","toast.integration_missing":"Интеграция House Plan не установлена — управление недоступно","toast.plan_formats":"Поддерживаются SVG, PNG, JPG, WebP","toast.plan_required":"Загрузите подложку — план этажа обязателен","toast.space_added_onboard":"Пространство добавлено. Обведите комнаты: кликайте по точкам сетки и замкните контур.","toast.space_added":"Пространство добавлено","toast.space_saved":"Пространство сохранено","toast.space_deleted":"Пространство удалено","toast.delete_failed":"Ошибка удаления: {err}","toast.error":"Ошибка: {err}","toast.file_failed":"Файл «{name}» не загружен: {err}","toast.files_attached":"Прикреплено файлов: {n}","err.unknown":"неизвестная ошибка","err.code":"код {code}","err.too_large":"файл больше {mb} МБ","err.bad_ext":"недопустимый тип (нужен PDF/изображение)","err.unauthorized":"нужны права администратора","editor.title":"Заголовок","editor.default_floor":"Пространство по умолчанию","editor.icon_size":"Размер иконок, % ширины плана","editor.show_temperature":"Показывать температуру","editor.live_states":"Живые состояния (вкл/выкл, открыто…)","editor.show_signal":"Показывать сигнал zigbee (LQI)","editor.language":"Язык интерфейса","editor.lang_auto":"Авто (профиль HA)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Правила иконок: какая MDI-иконка достаётся устройству по имени","rules.title":"Правила иконок","rules.hint":"Правила проверяются сверху вниз по строке «имя устройства + модель» (regex без учёта регистра); срабатывает первое совпадение. Если ничего не подошло — решает device class сущности, затем — иконка-заглушка.","rules.pattern_ph":"regex, напр. розетк|plug","rules.icon_ph":"mdi:power-socket-de","rules.add":"Добавить правило","rules.reset":"Сбросить к умолчаниям","rules.test_ph":"Проверьте имя устройства…","rules.invalid":"некорректный regex","rules.saved":"Правила иконок сохранены","btn.up":"Вверх","btn.down":"Вниз","tap.info":"Карточка устройства","tap.more_info":"Диалог HA (more-info)","tap.toggle":"Переключить состояние","marker.tap_label":"Действие по нажатию для этого устройства","tap.toggle_note":"Под селектором показано, какое состояние изменится. Защищённые устройства не переключаются с плана.","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":"По освещению","fill.custom":"Свой цвет","space.custom_fill":"Цвет заливки","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":"Загрузка…","continuity.restore_plan":"Восстанавливаем план…","continuity.restore_connection":"Восстанавливаем подключение к устройствам…","continuity.retry":"Повторить","editor.space":"Пространство","editor.show_button":"Показывать кнопку","editor.button_label":"Текст кнопки","editor.button_target":"Путь дашборда (куда вести)","marker.sub_entity":"сущность","title.general_settings":"Общие настройки","gs.title":"Общие настройки","gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.light_group":"Заливка: освещение","gs.light_on":"Свет включён","gs.light_off":"Весь свет выключен","gs.temp_group":"Заливка: температура","gs.temp_cold":"Холодно","gs.temp_ok":"Комфорт","gs.temp_hot":"Жарко","gs.lqi_group":"Заливка: зигби-сигнал","gs.lqi_low":"Слабый сигнал","gs.lqi_high":"Сильный сигнал","gs.reset":"Сбросить к умолчаниям","gs.saved":"Общие настройки сохранены","space.show_lqi":"Показывать зигби-сигнал (LQI) у устройств","space.hide_decor":"Скрыть декоративный слой","space.hide_decor_tip":"Линии, фигуры, надписи и мебель остаются на месте — их видно в редакторе подложки, но не на плане.","space.hide_openings":"Скрыть проёмы","space.hide_openings_tip":"Двери, окна и ворота не рисуются, но продолжают работать: свет проходит, солнце светит в окна, датчики открытия срабатывают. В редакторе плана проёмы видно всегда.","gs.light_none":"Нет источников света","mode.plan":"Редактор плана","mode.devices":"Редактор устройств","display.value":"Значение вместо иконки","marker.subarea":"без зоны, вручную","device.new":"Новое устройство — откройте его редактор, чтобы снять отметку","opening.unlock_action":"Открыть замок","opening.lock_action":"Закрыть замок","opening.lock_pending":"Выполняется…","title.close_editor":"Закрыть редактор (вернуться к просмотру)","devbar.add":"Добавить","devbar.show_all":"Скрытые и деактивированные","devbar.rules":"Правила иконок","space.roomcard_section":"В карточке комнаты:","space.label_temp":"Температура","space.label_hum":"Влажность","space.label_lqi":"Средний Zigbee-сигнал","space.label_light":"Свет вкл/выкл","roomcard.light_on":"Вкл","roomcard.light_off":"Выкл","roomcard.light_partial":"{on} из {total}","toast.split_pick_inside":"Промежуточные точки разреза — внутри комнаты","mode.decor":"Редактор подложки","decor.select":"Выбрать","decor.line":"Линия","decor.rect":"Прямоугольник","decor.ellipse":"Овал","decor.text":"Надпись","decor.erase":"Стереть","decor.erase_confirm_title":"Стереть объект?","decor.color":"Цвет","decor.width":"Толщина линии","decor.line_style":"Стиль линии","decor.line_style_solid":"Сплошная","decor.line_style_dashed":"Пунктирная","decor.fill":"Заливка","decor.fill_color":"Цвет заливки","decor.length":"Длина","decor.size":"Размер","decor.angle":"Угол поворота, °","decor.text_size":"Размер текста","decor.backdrop_properties":"Свойства картинки-подложки","decor.text_title":"Надпись","decor.object_title":"Редактирование: {kind}","decor.text_label":"Текст","decor.live_group":"Вставить переменную HA","decor.live_entity":"Сущность","decor.live_entity_ph":"выберите сущность","decor.live_attr":"Значение","decor.live_attr_ph":"выберите состояние или атрибут","decor.live_state":"Состояние","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — перемещение; угол — размер; верхнюю ручку — поворот. Shift меняет пропорции или снимает шаг угла.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","marker.icon_pin_auto":"Закрепить","mode.plan_tip":"Редактор плана — геометрия дома: рисование и объединение/разделение комнат, привязка к зонам HA, двери, окна и ворота, карточки комнат, масштаб","mode.devices_tip":"Редактор устройств — всё про значки: перетаскивание, клик — настройка привязки/иконки/отображения, виртуальные устройства, правила иконок","mode.decor_tip":"Редактор подложки — чисто визуальный декор под планом: линии, прямоугольники, овалы и надписи, не реагирующие на клики","space.glow_enabled":"Свечение источников света","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.help":"Задаёт радиус свечения в метрах или футах; пустое значение использует радиус из общих настроек.","marker.glow_radius.help.aria":"Подсказка: радиус свечения","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены комнаты (1–100 см). Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от 1 до {max} {unit}","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","markup.boundary_hint":"Выберите общую границу комнат или пунктирный участок","markup.boundary_hint_open":"Кликните начало открытого участка","markup.boundary_hint_second":"Укажите конец на выбранной границе · Esc — отмена","markup.boundary_hint_retry":"Укажите конец на выбранной границе","markup.boundary_hint_restore":"Кликните, чтобы восстановить стену","markup.boundary_hint_outer":"Открыть можно только общую границу двух комнат","markup.boundary_hint_blocked":"над границей находится перегородка, колонна или незавершённый контур","markup.boundary_hint_ambiguous":"Выберите участок чуть дальше от стыка","markup.boundary_cancel":"Отменить незавершённое действие «Граница»","toast.openwall_pick":"Инструмент работает с границами комнат","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.boundary_opened":"Участок границы открыт","toast.boundary_restored":"Стена на участке восстановлена","toast.boundary_same_edge":"Выберите конечную точку на выбранной стене","toast.boundary_ambiguous":"Выберите участок чуть дальше от стыка","toast.boundary_blocked":"Граница перекрыта независимой стеной. Сначала переместите или удалите её инструментом «Выбрать»","toast.delete_room_pick":"Кликните внутри комнаты, которую нужно удалить","toast.openwall_openings_removed":"Проёмы на виртуальном отрезке удалены","toast.openwall_opened":"Граница «{a}» ↔ «{b}» теперь открыта","toast.openwall_closed":"Граница «{a}» ↔ «{b}» снова закрыта","toast.opening_on_virtual":"Проёмы на виртуальной стене запрещены","marker.from_ha_option":"Выбрать из списка HA","marker.show_entities":"Отображать сущности","marker.show_entities_tip":"Добавляет в список не только устройства, но и все их сущности","marker.pick_ph":"Выберите устройство…","room.open_area":"Открыть зону в HA","kiosk.title":"Размеры на этом экране","kiosk.hint":"Хранится только на этом устройстве — у каждого настенного планшета или ТВ свои удобные размеры.","kiosk.icon_scale":"Размер значков устройств","kiosk.font_scale":"Размер текста карточек комнат","editor.kiosk":"Режим настенного устройства (киоск)","editor.cycle":"Автосмена пространств каждые N секунд (киоск, 0 = выкл)","room.settings_title":"Настройки комнаты","room.settings_section":"Настройки комнаты (переопределяют пространство)","room.fill_label":"Заливка в ЭТОЙ комнате","fill.inherit":"Как у пространства","room.custom_fill_space":"Цвет пространства","room.custom_fill_own":"Цвет комнаты","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.value_badge_title":"Бейдж со значением","marker.value_badge.help":"Показывает одно выбранное значение рядом со значком. Не влияет на комнатные показатели, свет, Glow и действие по нажатию.","marker.value_badge.help.aria":"Подсказка: бейдж со значением устройства","marker.value_badge_enabled":"Отображать бейдж со значением","marker.value_badge_source":"Значение","marker.value_badge_source.help":"Выберите конкретное состояние, поддерживаемый атрибут или производное значение этого устройства.","marker.value_badge_source.help.aria":"Подсказка: источник значения бейджа","marker.value_badge_position":"Расположение","marker.value_badge_position.help":"Выбранная сторона сохраняется постоянно и не переворачивается автоматически у края плана.","marker.value_badge_position.help.aria":"Подсказка: расположение бейджа","marker.value_badge_right":"Справа","marker.value_badge_bottom":"Снизу","marker.value_badge_left":"Слева","marker.value_badge_top":"Сверху","marker.value_badge_empty":"У этого устройства нет доступных значений","marker.value_badge_static":"Статичный значок не показывает живые значения. Настройка сохранится.","marker.value_badge_missing":"Источник недоступен","marker.value_badge_missing_hint":"Сохранённый источник сейчас недоступен. Можно сохранить его, отключить бейдж или выбрать другой.","marker.value_badge_duplicate":"То же значение уже показано внутри значка.","marker.value_badge_state":"Состояние · {name}","marker.value_badge_marker_state":"Состояние источника света · {name}","marker.value_badge_lqi":"Качество Zigbee-сигнала (среднее)","marker.value_badge_attr_current_temperature":"Текущая температура · {name}","marker.value_badge_attr_temperature":"Целевая температура · {name}","marker.value_badge_attr_current_humidity":"Текущая влажность · {name}","marker.value_badge_attr_humidity":"Влажность · {name}","marker.value_badge_attr_current_position":"Положение · {name}","marker.value_badge_attr_percentage":"Скорость · {name}","marker.value_badge_attr_brightness":"Яркость · {name}","marker.value_badge_attr_volume_level":"Громкость · {name}","marker.value_badge_attr_battery_level":"Заряд батареи · {name}","marker.value_badge_attr_fan_speed":"Скорость вентилятора · {name}","marker.light_role_label":"Является источником света","marker.light_role.help":"Определяет, является ли сам маркер пространственным источником света. Привязанные выше лампы остаются независимыми источниками для комнаты.","marker.light_role.help.aria":"Подсказка: является ли устройство источником света","marker.light_role_auto_yes":"Автоматически (источник света)","marker.light_role_auto_no":"Автоматически (не источник света)","marker.light_role_always":"Всегда источник света","marker.light_role_never":"Никогда не источник света","marker.light_entity_label":"Ведущая сущность света","marker.light_entity.help":"Для составного устройства выберите сущность, состояние, цвет и яркость которой представляют этот источник на плане. Автоматический вариант сохраняет совместимость со старыми планами.","marker.light_entity.help.aria":"Подсказка: ведущая сущность света","marker.light_entity_auto":"Автоматически ({entity})","marker.light_entity_none":"нет управляемой сущности","marker.light_entity_missing":"Сохранённая сущность {entity} недоступна. Временно используется {fallback}; выбор восстановится, если сущность вернётся.","marker.glow_color_label":"Цвет и яркость свечения","marker.glow_mode.help":"Используйте данные источника, задайте только цвет или зафиксируйте цвет и яркость. Минимум — 1%; чтобы отключить источник, выберите «Никогда».","marker.glow_mode.help.aria":"Подсказка: цвет и яркость свечения","marker.glow_mode_auto":"Из источника","marker.glow_mode_color":"Задать цвет","marker.glow_mode_fixed":"Задать цвет и яркость","marker.glow_color":"Цвет свечения","marker.glow_brightness":"Яркость","marker.glow_disabled_never":"Настройки свечения недоступны: маркер явно исключён из источников света.","marker.glow_disabled_auto":"В автоматическом режиме у этого маркера не найден пространственный источник света.","marker.glow_disabled_no_entity":"Для пространственного источника нет активной управляемой сущности Home Assistant.","marker.glow_passive_hint":"У источника нет собственных данных Home Assistant. Задайте цвет и яркость вручную; радиус остаётся доступным.","marker.control_broken":"Сохранённый источник отсутствует или больше не отмечен как источник света","marker.control_passive":"пассивный источник","toast.marker_control_cycle":"Такая связь создаст циклическую цепочку управления светом.","toast.marker_binding_required":"Сначала выберите устройство Home Assistant, затем добавьте управляемый источник света.","confirm.unlock":"Открыть замок «{name}»?","toast.files_migrate_failed":"Не удалось перенести вложения к новой привязке, ссылки остались на старые файлы: {err}","space.pick_saved":"Уже загруженные","space.pick_saved_hint":"Планы, сохранённые на сервере, включая отцеплённые ранее","space.no_saved":"На сервере пока нет сохранённых планов.","space.loading":"Загрузка…","space.used_by":"используется: {list}","space.in_use":"План используется пространством — сначала отцепите его","btn.use":"Выбрать","confirm.delete_plan":"Удалить файл плана «{name}» с сервера? Действие необратимо.","toast.plans_list_failed":"Не удалось получить список планов: {err}","toast.plan_delete_failed":"Не удалось удалить план: {err}","marker.hide":"Скрыть","marker.hide_tip":"Устройство исчезнет с плана после сохранения, но продолжит участвовать в расчёте сигнала комнаты. Вернуть его можно через кнопку «Скрытые и деактивированные» в редакторе устройств.","marker.show":"Показать","marker.show_tip":"Устройство снова появится на плане после сохранения.","marker.hidden_ghost":"Устройство скрыто пользователем","marker.ha_disabled_device":"Устройство деактивировано в Home Assistant и скрыто с плана.","marker.ha_disabled_entity":"Сущность деактивирована в Home Assistant и скрыта с плана.","marker.ha_disabled_all_entities":"У устройства нет активных сущностей Home Assistant, поэтому оно скрыто с плана.","marker.ha_registry_limited":"Полный реестр Home Assistant недоступен этому пользователю. Неподтверждённый объект временно нельзя показывать или использовать.","marker.delete_tip":"Полностью удалить устройство с плана и из всех расчётов. Позже его можно добавить заново.","tap.run":"Запустить автоматизацию/скрипт/сцену","tap.cover":"Открыть/закрыть (шторы/жалюзи)","marker.run_target_label":"Что запускать","marker.run_search_ph":"Поиск: автоматизация, скрипт или сцена…","marker.run_target_gone":"Цель {id} не найдена — выберите заново","marker.tap_confirm":"Спрашивать подтверждение","marker.tap_confirm_tip":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","marker.toggle_hint_single":"Цель: {name} ({id}).","marker.toggle_hint_group":"Переключатся источники ({count}): {names}.","marker.toggle_hint_current":"Сейчас: {state} → {effect}.","marker.toggle_hint_group_current":"Сейчас включено {on} из {count} → {effect}.","marker.toggle_hint_skipped":"Не будут затронуты ({count}): {targets}.","marker.toggle_effect_turn_on":"включится","marker.toggle_effect_turn_off":"выключится","marker.toggle_effect_open":"откроется","marker.toggle_effect_close":"закроется","marker.toggle_effect_stop":"остановится","marker.toggle_effect_toggle":"состояние переключится","marker.toggle_skip_missing":"не найдено","marker.toggle_skip_ha_disabled":"деактивировано в HA","marker.toggle_skip_unavailable":"недоступно","marker.toggle_skip_unsupported":"переключение не поддерживается","marker.toggle_skip_secure":"заблокировано из соображений безопасности","marker.toggle_none_no_binding":"У устройства нет привязки, состояние переключать нечем. По нажатию ничего не произойдёт.","marker.toggle_none_no_actionable_entity":"У устройства нет состояния, которое можно переключить. По нажатию ничего не произойдёт.","marker.toggle_none_configured_targets_missing":"Настроенные цели сейчас недоступны. Собственная сущность устройства не будет подставлена вместо них.","marker.toggle_none_ha_disabled":"Цель деактивирована в Home Assistant и не может использоваться на плане.","marker.toggle_none_unavailable":"Цель сейчас недоступна. По нажатию ничего не произойдёт.","marker.toggle_none_unsupported":"Для этой сущности Home Assistant не предоставляет безопасного переключения. По нажатию ничего не произойдёт.","marker.toggle_none_secure":"Переключение замков, сигнализации и защитных ворот с плана заблокировано из соображений безопасности.","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":"Выберите автоматизацию, скрипт или сцену","toast.tap_target_changed":"Цель действия изменилась. Повторите попытку.","toast.value_badge_source_required":"Выберите значение для бейджа","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.autocal":"Настроить автоматически","vac.live":"Живая позиция на плане","vac.trail":"Показывать путь робота","vac.cal_maps":"Откалиброваны карты: {maps}","vac.autocal_no_rooms":"Интеграция не отдаёт список комнат — откройте «Подогнать вручную»","vac.autocal_no_match":"Не совпали имена комнат (нужно ≥3 общих) — откройте «Подогнать вручную»","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.about_group":"О карточке","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · документация и issues","gs.about_telegram":"Чат в Telegram","space.bg_color":"Цвет фона вокруг плана","space.bg_inherit":"Наследовать общий","space.bg_inherited":"наследуется из общих настроек","space.bg_mode":"Фон плана","space.north":"Север на плане (переопределение)","space.north_inherited":"наследуется: {v}","space.sun_rays":"Солнце в окнах","space.sun_inherit":"Наследовать общий","space.sun_on":"Вкл","space.sun_off":"Выкл","canvas.far_objects":"Объектов далеко от плана: {n}","canvas.show_far":"Показать","canvas.home_tip":"План там — нажмите, чтобы вписать","gs.grid_group":"Обслуживание планов","gs.grid_hint":"Обновляет модели данных, выравнивает элементы по сетке и объединяет лишние фрагменты стен. Перед записью будет показан точный отчёт.","gs.align_all":"Оптимизировать планы","gs.align_title":"Оптимизировать планы","gs.align_none":"Все планы уже используют актуальную и оптимальную модель данных.","gs.align_count":"Сдвинется элементов: {n} из {total}, максимум на {cm} см.","gs.align_where":"Наибольший сдвиг — в пространстве «{s}».","gs.align_turned":"Проёмов с исправлением угла: {n}.","gs.align_removed_drafts":"Схлопнувшиеся на сетке некорректные контуры удалены: {n}.","gs.optimize_changes":"Миграций модели: {m}; канонизировано планов: {c}; объединено отрезков реальных стен: {w}; виртуальных: {s}.","gs.optimize_glow_migration":"Старый Glow: пространств — {spaces} → без заливки данных + независимый Glow; комнат — {rooms} → наследуемая заливка + независимый Glow.","gs.align_warn":"Элементы, намеренно поставленные между узлами, будут сдвинуты. После операции доступна одна отмена — только до следующего изменения плана.","gs.align_run":"Оптимизировать","gs.align_done":"Планы оптимизированы: сдвинуто элементов — {n}, обслужено записей — {m}","gs.optimize_undo":"Отменить последнюю оптимизацию","gs.optimize_undone":"Последняя оптимизация отменена","decor.furniture":"Мебель","furn.title":"Библиотека мебели","furn.symbol":"Символ","furn.group_furniture":"Мебель","furn.group_appliance":"Техника","furn.group_sanitary":"Сантехника","furn.group_other":"Прочее","furn.width":"Ширина","furn.depth":"Глубина","furn.pick_hint":"Выберите символ и кликните по плану.","furn.place_hint":"Кликните по плану — предмет встанет к ближайшей стене. Shift — свободно.","furn.sym_sofa":"Диван","furn.sym_armchair":"Кресло","furn.sym_coffee_table":"Журнальный столик","furn.sym_table_dining":"Обеденный стол","furn.sym_table_round":"Круглый стол","furn.sym_chair":"Стул","furn.sym_desk":"Письменный стол","furn.sym_bed_double":"Двуспальная кровать","furn.sym_bed_single":"Односпальная кровать","furn.sym_nightstand":"Тумбочка","furn.sym_wardrobe":"Шкаф","furn.sym_bookshelf":"Стеллаж","furn.sym_fridge":"Холодильник","furn.sym_stove":"Плита","furn.sym_dishwasher":"Посудомоечная машина","furn.sym_washer":"Стиральная машина","furn.sym_dryer":"Сушильная машина","furn.sym_tv":"Телевизор","furn.sym_ac":"Кондиционер","furn.sym_water_heater":"Бойлер","furn.sym_toilet":"Унитаз","furn.sym_bathtub":"Ванна","furn.sym_shower":"Душ","furn.sym_sink":"Раковина","furn.sym_kitchen_sink":"Кухонная мойка","furn.sym_bidet":"Биде","furn.sym_stairs":"Лестница","furn.sym_fireplace":"Камин","furn.sym_plant":"Растение","furn.sym_rug":"Ковёр","common.yes":"Да","common.no":"Нет","vac.diag_source":"Источник","vac.diag_platform":"Интеграция","vac.diag_status":"Статус","vac.diag_position":"Позиция","vac.diag_rooms":"Комнаты","vac.diag_rooms_value":"{total} · совпало имён: {matched} · {readiness}","vac.autocal_ready":"автокалибровка доступна","vac.autocal_not_ready":"нужно 3 совпавших имени","vac.diag_path":"Путь интеграции","vac.diag_map":"ID карты","vac.source_none":"не выбран","vac.source_status_ok":"Готов","vac.source_status_missing":"Не найден","vac.source_status_disabled":"Деактивирован в Home Assistant","vac.source_status_unavailable":"Недоступен","vac.source_status_unverified":"Нельзя проверить с текущими правами","vac.source_status_unsupported":"Нет данных позиции","vac.source_status_none":"Нет источника","vac.source_banner_missing":"Сохранённый источник больше не существует. Он не заменён автоматически: выберите другой или восстановите его в Home Assistant.","vac.source_banner_disabled":"Сохранённый источник деактивирован в Home Assistant. Активируйте его там или выберите другой.","vac.source_banner_unverified":"Текущих прав Home Assistant недостаточно для проверки источника. Привязка сохранена и не будет заменена автоматически.","vac.choose_source":"Выбрать источник","vac.source_auto":"Автоматически","vac.source_auto_hint":"Использовать совместимую сущность этого устройства","vac.all_cameras":"Все камеры","vac.all_cameras_warn":"Камера может не отдавать данные робота. Выбирайте её только если это карта вашего пылесоса.","vac.all_cameras_empty":"Других сущностей камеры не найдено.","vac.platform_unknown":"интеграция неизвестна","vac.cap_position":"позиция","vac.cap_rooms_short":"комнаты","vac.cap_path":"путь","vac.cap_map":"ID карты","vac.cap_none":"данные робота не обнаружены","vac.xcme_hint":"Включите атрибуты Xiaomi Cloud Map Extractor:","vac.documentation":"Документация","vac.residual_title":"Проверьте автокалибровку","vac.residual_message":"Совпавшие комнаты расходятся максимум на {error}. Примените приблизительную калибровку, подгоните её вручную либо отмените без изменения сохранённых настроек.","vac.apply_proposal":"Применить","gs.backup_group":"Резервная копия и перенос","gs.backup_hint":"Скачайте переносимую JSON-копию или проверьте и импортируйте копию, созданную House Plan.","backup.export_open":"Экспорт","backup.import_open":"Импорт","backup.export_title":"Экспорт House Plan","backup.import_title":"Импорт House Plan","backup.export_hint":"Выберите, должна ли копия содержать всю конфигурацию House Plan или только текущее пространство.","backup.full":"Полная копия","backup.current_space":"Текущее пространство","backup.current_space_title":"Текущее пространство: {title}","backup.no_current_space":"Нет текущего пространства","backup.privacy_warning":"Архив сохраняет названия, идентификаторы Home Assistant и точные координаты. Внутренние планы и вложения указываются ссылками, но не вкладываются; текущие состояния и маршруты пылесосов не включаются.","backup.download":"Скачать JSON","backup.export_done":"Резервная копия скачана","backup.reading":"Проверяем резервную копию…","backup.revalidated":"После предпросмотра план изменился. Сводка обновлена — проверьте её и подтвердите ещё раз.","backup.error.unauthorized":"У вас нет прав на экспорт или импорт этого плана.","backup.error.not_ready":"House Plan ещё не готов. Повторите попытку через несколько секунд.","backup.error.too_large":"Размер резервной копии превышает 8 МиБ.","backup.error.invalid_json":"Выбранный файл не является корректным JSON.","backup.error.invalid_format":"Выбранный файл не является резервной копией House Plan.","backup.error.unsupported_export_version":"Эта версия формата резервной копии не поддерживается установленной версией.","backup.error.future_model":"Резервная копия создана в более новой версии модели данных House Plan.","backup.error.invalid_config":"Резервная копия содержит некорректную конфигурацию House Plan.","backup.error.invalid_layout":"Резервная копия содержит некорректные позиции объектов.","backup.error.invalid_content":"Резервная копия содержит некорректные или несогласованные ссылки на файлы.","backup.error.space_not_found":"Выбранное пространство больше не существует.","backup.error.capacity_exceeded":"Добавление этой копии превысит ограничения размера плана.","backup.error.preview_expired":"Время предпросмотра истекло. Выберите файл резервной копии заново.","backup.error.preview_owner_mismatch":"Этот предпросмотр принадлежит другому пользователю Home Assistant.","backup.error.conflict":"После предпросмотра план изменился. Проверьте обновлённую сводку.","backup.error.content_confirmation_required":"Подтвердите отсоединение недоступных локальных файлов.","backup.error.commit_failed":"Не удалось безопасно применить копию. Предыдущий план восстановлен или ожидает восстановления.","backup.error.missing_plan":"После предпросмотра исчез связанный файл плана. Проверьте копию ещё раз.","backup.error.marker_control_missing":"В резервной копии отсутствует связанный источник света на плане.","backup.error.marker_control_not_light":"Связанная цель больше не отмечена как источник света.","backup.error.marker_control_self":"Источник света не может управлять самим собой.","backup.error.marker_control_cycle":"Резервная копия содержит циклическую цепочку управления светом.","backup.error.duplicate_marker_control":"Резервная копия содержит повторяющуюся связь с источником света.","backup.error.no_backup":"Нет снимка импорта или оптимизации, который можно восстановить.","backup.same_source":"Создано на этом экземпляре Home Assistant","backup.foreign_source":"Создано на другом экземпляре Home Assistant","backup.created":"Создано: {value}","backup.versions":"Карточка {card}; интеграция {integration}; модель данных {model}","backup.count_spaces":"Пространств: {n}","backup.count_rooms":"Комнат: {n}","backup.count_walls":"Стен: {n}","backup.count_openings":"Проёмов: {n}","backup.count_decor":"Объектов декора: {n}","backup.count_markers":"Устройств: {n}","backup.count_layout":"Позиций: {n}","backup.bindings":"Привязки — устройства: {device}, сущности: {entity}, виртуальные: {virtual}; позиций без пространства: {legacy}","backup.binding_status":"Статус на целевом экземпляре — активных: {active}, деактивированных: {disabled}, отсутствующих: {missing}","backup.missing_areas":"На целевом экземпляре отсутствуют зоны: {areas}","backup.dropped_marker_links":"Связи с источниками света вне переносимого пространства пропущены: {n}.","backup.replace_warning":"Текущая конфигурация и расположение будут заменены. Загруженные файлы не удаляются. Одну отмену можно выполнить до следующего изменения плана.","backup.foreign_bookkeeping":"Служебные списки известных и новых устройств другого экземпляра импортированы не будут.","backup.final_name":"Название нового пространства","backup.target_settings":"Глобальные настройки целевого экземпляра не изменятся.","backup.duplicates":"Уже существующих привязок: {n}","backup.skip":"Пропустить повторяющиеся устройства","backup.virtual_copy":"Добавить безопасные статичные виртуальные копии","backup.content":"Связанное содержимое","backup.content_available":"доступно локально","backup.content_external":"внешняя ссылка","backup.content_detach_required":"будет отвязано","backup.confirm_detach":"Я понимаю, что недоступные внутренние планы и вложения будут отвязаны от импортируемой конфигурации.","backup.replace":"Заменить","backup.add":"Добавить пространство","backup.space_done":"Пространство импортировано: комнат — {rooms}, устройств — {markers}","backup.full_done":"Копия восстановлена: пространств — {spaces}, комнат — {rooms}, устройств — {markers}","backup.undo_import":"Отменить последний полный импорт","backup.import_undone":"Полный импорт отменён"}};function Zo(t,e){if(e&&e in Jo)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function Xo(t,e,i){return Vi(Jo[t][e]??Yo[e]??e,i)}class Qo extends ht{constructor(){super(...arguments),this.deviceName="",this._metadataConnection=null,this._demoUntil=0,this._demoTimer=0,this._demoGeneration=1,this._reducedMotion=!1,this._onMotionChange=t=>{this._reducedMotion=t.matches,this.requestUpdate()},this._startDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"alarm"!==t.visual.status&&"none"===t.activity&&(this._demoGeneration++,this._demoUntil=Date.now()+3300,window.clearTimeout(this._demoTimer),this._demoTimer=window.setTimeout(()=>{this._demoUntil=0,this._demoTimer=0,this.requestUpdate()},3330),this.requestUpdate())}}connectedCallback(){super.connectedCallback(),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._ensureMetadata()}disconnectedCallback(){this._metadataRelease?.(),this._metadataRelease=void 0,this._metadataConnection=null,this._clearDemo(),this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,super.disconnectedCallback()}willUpdate(t){t.has("hass")&&this._ensureMetadata();const e=this.presentation,i=t.get("presentation");t.has("presentation")&&i?.binding!==e?.binding&&this._clearDemo(),e&&this._demoUntil>Date.now()&&("icon_ripple"!==e.display||"alarm"===e.visual.status||"none"!==e.activity)&&this._clearDemo()}_ensureMetadata(){const t=this.hass?.connection||null;t&&t!==this._metadataConnection&&(this._metadataRelease?.(),this._metadataConnection=t,this._metadataRelease=Uo(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0}get _lang(){return Zo(this.hass)}_t(t,e){return Xo(this._lang,t,e)}_providerText(t){return"houseplan"===t.domain?this._t("marker.preview.virtual_provider"):t.label.toLowerCase()===t.domain.toLowerCase()?t.domain:`${t.label} (${t.domain})`}_providers(){return this.presentation&&this.registry?function(t,e,i,s=Wo(t)){if("virtual"===e)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const n=e.indexOf(":");if(n<1)return[];const o=e.slice(0,n),a=e.slice(n+1);if("entity"===o){const e=Ko(t,a,i,s);return e?[e]:[]}if("device"!==o)return[];const r=i.devices?.[a]||t?.devices?.[a];if(!r)return[];const l=[r.config_entry_id,...Array.isArray(r.config_entries)?r.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const i=s.configEntries[e];return Go(t,i?.domain,"registry-owner",s,e)});if(l.some(Boolean))return Vo(l);const c=[];for(const[e,n]of Object.entries(i.entities||{}))n?.device_id===a&&null==n?.disabled_by&&c.push(Ko(t,e,i,s));return c.some(Boolean)?Vo(c):Vo((Array.isArray(r.identifiers)?r.identifiers:[]).map(e=>{const i=Array.isArray(e)?e[0]:null;return Go(t,i,"identifier-fallback",s)}))}(this.hass,this.presentation.binding,this.registry,Wo(this.hass)):[]}_providerSummary(t){if(!t.length)return this._t("marker.preview.unknown_provider");const e=t.slice(0,2).map(t=>this._providerText(t)),i=t.length-e.length;return e.join(", ")+(i>0?` · ${this._t("marker.preview.more_sources",{n:i})}`:"")}_sourceSummary(t){const e=t.visualSources;return e.length?1===e.length?`${e[0].name} · ${e[0].eid} · ${this._sourceProvider(e[0].eid)}`:2===e.length?e.map(t=>t.name).join(" · "):this._t("marker.preview.multiple_sources",{n:e.length}):this._t("marker.preview.no_source")}_stateSummary(t){if(!t.visualSources.length)return this._t("marker.preview.no_state");if(1===t.visualSources.length)return t.visualSources[0].stateText||this._t("marker.preview.no_state");const e=[...new Set(t.visualSources.map(t=>t.stateText).filter(Boolean))];return 1===e.length?e[0]:this._t("marker.preview.mixed_states")}_reason(t){return this._t(`marker.preview.reason.${t}`)}_sourceProvider(t){if(!this.registry)return this._t("marker.preview.unknown_provider");const e=Ko(this.hass,t,this.registry,Wo(this.hass));return e?this._providerText(e):this._t("marker.preview.unknown_provider")}render(){const t=this.presentation;if(!t)return G;const e=this._demoUntil>Date.now()&&"icon_ripple"===t.display&&"alarm"!==t.visual.status&&"none"===t.activity,i=e?function(t,e){if("icon_ripple"!==t.display)return t;const i={...t,activity:"event",activityGeneration:e,classes:[]};return{...i,classes:Oo(i)}}(t,this._demoGeneration):t,s=this._providers(),n="alarm"===t.visual.status||"none"!==t.activity,o=i.scale*("none"!==i.activity?i.rippleScale:1);let a=-o/2,r=o/2,l=-o/2,c=o/2;if(i.valueBadge){const t=i.scale*Math.min(4,Math.max(.9,.29*i.valueBadge.text.length+.28)),e=.7*i.scale,s=.6*i.scale;"right"===i.valueBadge.position&&(r=Math.max(r,s+t)),"left"===i.valueBadge.position&&(a=Math.min(a,-s-t)),"top"===i.valueBadge.position&&(l=Math.min(l,-s-e)),"bottom"===i.valueBadge.position&&(c=Math.max(c,s+e))}if(null!=i.lqiText){const t=i.scale*("bottom"===i.valueBadge?.position?1.55:.95);c=Math.max(c,t)}const h=Math.min(1,2.35/Math.max(1,r-a),2.35/Math.max(1,c-l)),d=Math.round(100*h),u=[`left:calc(50% - ${54*((a+r)/2)*h}px)`,`top:calc(50% - ${54*((l+c)/2)*h}px)`,h<1?`transform:scale(${h})`:""].filter(Boolean).join(";"),p=["dev",...i.classes,"virtual"===i.binding?"virtual":"",i.haDisabled?"ghost ha-disabled":"",null!=i.valueText?"valonly":""].filter(Boolean).join(" "),_=Lt(i).join(";"),m=e?this._t("marker.preview.demo_notice"):this._reason(t.explanation.reason),g=[this.deviceName,this._providerSummary(s),this._stateSummary(t),m,t.valueFullText||"",t.valueBadge?`${t.valueBadge.sourceLabel}: ${t.valueBadge.fullText}`:""].filter(Boolean).join(". ");return U`
+ `}const qt=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),Bt=[{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|thermometer|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 Ut(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 Wt=Ut(Bt),jt={temperature:"mdi:thermometer",humidity:"mdi:water-percent",motion:"mdi:motion-sensor",occupancy:"mdi:motion-sensor",presence:"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 Vt(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??Wt)if(t.test(s))return e;return Gt}const Kt=["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 Yt=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,Jt=Math.ceil,Zt=Math.floor,Xt="[BigNumber Error] ",Qt=Xt+"Number primitive has more than 15 significant digits: ",te=1e14,ee=14,ie=9007199254740991,se=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],ne=1e7,oe=1e9;function ae(t){var e=0|t;return t>0||t===e?e:e-1}function re(t){for(var e,i,s=1,n=t.length,o=t[0]+"";sc^i?1:-1;for(r=(l=n.length)<(c=o.length)?l:c,a=0;ao[a]^i?1:-1;return l==c?0:l>c^i?1:-1}function ce(t,e,i,s){if(ti||t!==Zt(t))throw Error(Xt+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function he(t){var e=t.c.length-1;return ae(t.e/ee)==e&&t.c[e]%2!=0}function de(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function ue(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 ey?u.c=u.e=null:t.e=10;l/=10,r++);return void(r>y?u.c=u.e=null:(u.e=r,u.c=[t]))}d=String(t)}else{if(!Yt.test(d=String(t)))return n(u,d,c);u.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(r=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(r<0&&(r=l),r+=+d.slice(l+1),d=d.substring(0,l)):r<0&&(r=d.length)}else{if(ce(e,2,x.length,"Base"),10==e&&S)return P(u=new M(t),_+u.e+1,m);if(d=String(t),c="number"==typeof t){if(0*t!=0)return n(u,d,c,e);if(u.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(Qt+t)}else u.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),r=l=0,h=d.length;lr){r=h;continue}}else if(!a&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){a=!0,l=-1,r=0;continue}return n(u,String(t),c,e)}c=!1,(r=(d=s(d,e,10,u.s)).indexOf("."))>-1?d=d.replace(".",""):r=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>ie||t!==Zt(t)))throw Error(Qt+u.s*t);if((r=r-l-1)>y)u.c=u.e=null;else if(r=f)?de(l,a):ue(l,a,"0");else if(o=(t=P(new M(t),e,i)).e,r=(l=re(t.c)).length,1==s||2==s&&(e<=o||o<=g)){for(;ra),l=ue(l,o,"0"),o+1>r){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=o-r)>0)for(o+1==r&&(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*ee-1)>y?t.c=t.e=null:i=10;r/=10,n++);if((o=e-n)<0)o+=ee,a=e,l=d[c=0],h=Zt(l/u[n-a-1]%10);else if((c=Jt((o+1)/ee))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,n=1,a=(o%=ee)-ee+1}else{for(l=r=d[c],n=1;r>=10;r/=10,n++);h=(a=(o%=ee)-ee+n)<0?0:Zt(l/u[n-a-1]%10)}if(s=s||e<0||null!=d[c+1]||(a<0?l:l%u[n-a-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?a>0?l/u[n-a]: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]=u[(ee-e%ee)%ee],t.e=-e||0):d[0]=t.e=0,t;if(0==o?(d.length=c,r=1,c--):(d.length=c+1,r=u[ee-o],d[c]=a>0?Zt(l/u[n-a]%u[a])*r:0),s)for(;;){if(0==c){for(o=1,a=d[0];a>=10;a/=10,o++);for(a=d[0]+=r,r=1;a>=10;a/=10,r++);o!=r&&(t.e++,d[0]==te&&(d[0]=1));break}if(d[c]+=r,d[c]!=te)break;d[c--]=0,r=1}for(o=d.length;0===d[--o];d.pop());}t.e>y?t.c=t.e=null:t.e=f?de(e,i):ue(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(Xt+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(ce(i=t[e],0,oe,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(ce(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(ce(i[0],-oe,0,e),ce(i[1],0,oe,e),g=i[0],f=i[1]):(ce(i,-oe,oe,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)ce(i[0],-oe,-1,e),ce(i[1],1,oe,e),v=i[0],y=i[1];else{if(ce(i,-oe,oe,e),!i)throw Error(Xt+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(Xt+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(Xt+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(ce(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(ce(i=t[e],0,oe,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(Xt+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(Xt+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:k,FORMAT:$,ALPHABET:x}},M.isBigNumber=function(t){if(!t||!0!==t._isBigNumber)return!1;if(!M.DEBUG)return!0;var e,i,s=t.c,n=t.e,o=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===o||-1===o)&&n>=-oe&&n<=oe&&n===Zt(n)){if(0===s[0]){if(0===n&&1===s.length)return!0;break t}if((e=(n+1)%ee)<1&&(e+=ee),String(s[0]).length==e){for(e=0;e=te||i!==Zt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===n&&(null===o||1===o||-1===o))return!0;throw Error(Xt+"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,a=Math.random()*o&2097151?function(){return Zt(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,n,o,r=0,l=[],c=new M(p);if(null==t?t=_:ce(t,0,oe),n=Jt(t/ee),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(n*=2));r>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[r]=i[0],e[r+1]=i[1]):(l.push(o%1e14),r+=2);r=n/2}else{if(!crypto.randomBytes)throw b=!1,Error(Xt+"crypto unavailable");for(e=crypto.randomBytes(n*=7);r=9e15?crypto.randomBytes(7).copy(e,r):(l.push(o%1e14),r+=7);r=n/7}if(!b)for(;r=10;o/=10,r++);ri-1&&(null==a[n+1]&&(a[n+1]=0),a[n+1]+=a[n]/i|0,a[n]%=i)}return a.reverse()}return function(s,n,o,a,r){var l,c,h,d,u,p,g,f,v=s.indexOf("."),y=_,b=m;for(v>=0&&(d=k,k=0,s=s.replace(".",""),p=(f=new M(n)).pow(s.length-v),k=d,f.c=e(ue(re(p.c),p.e,"0"),10,o,t),f.e=f.c.length),h=d=(g=e(s,n,o,r?(l=x,t):(l=t,x))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--h:(p.c=g,p.e=h,p.s=a,g=(p=i(p,f,y,b,o)).c,u=p.r,h=p.e),v=g[c=h+y+1],d=o/2,u=u||c<0||null!=g[c+1],u=b<4?(null!=v||u)&&(0==b||b==(p.s<0?3:2)):v>d||v==d&&(4==b||u||6==b&&1&g[c-1]||b==(p.s<0?8:7)),c<1||!g[0])s=u?ue(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=c,u)for(--o;++g[--c]>o;)g[c]=0,c||(++h,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,s="";v<=d;s+=l.charAt(g[v++]));s=ue(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,n,o,a,r=0,l=t.length,c=e%ne,h=e/ne|0;for(t=t.slice();l--;)r=((n=c*(o=t[l]%ne)+(s=h*o+(a=t[l]/ne|0)*c)%ne*ne+r)/i|0)+(s/ne|0)+h*a,t[l]=n%i;return r&&(t=[r].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,a,r){var l,c,h,d,u,p,_,m,g,f,v,y,b,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(g=(m=new M(S)).c=[],S=o+(c=s.e-n.e)+1,r||(r=te,c=ae(s.e/ee)-ae(n.e/ee),S=S/ee|0),h=0;D[h]==(C[h]||0);h++);if(D[h]>(C[h]||0)&&c--,S<0)g.push(1),d=!0;else{for(w=C.length,$=D.length,h=0,S+=2,(u=Zt(r/(D[0]+1)))>1&&(D=t(D,u,r),C=t(C,u,r),$=D.length,w=C.length),b=$,v=(f=C.slice(0,$)).length;v<$;f[v++]=0);x=D.slice(),x=[0].concat(x),k=D[0],D[1]>=r/2&&k++;do{if(u=0,(l=e(D,f,$,v))<0){if(y=f[0],$!=v&&(y=y*r+(f[1]||0)),(u=Zt(y/k))>1)for(u>=r&&(u=r-1),_=(p=t(D,u,r)).length,v=f.length;1==e(p,f,_,v);)u--,i(p,$<_?x:D,_,r),_=p.length,l=1;else 0==u&&(l=u=1),_=(p=D.slice()).length;if(_=10;S/=10,h++);P(m,o+(m.e=h+c*ee-1)+1,a,d)}else m.e=c,m.r=+d;return m}}(),r=/^(-?)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(r,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(Xt+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},u.absoluteValue=u.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},u.comparedTo=function(t,e){return le(this,new M(t,e))},u.decimalPlaces=u.dp=function(t,e){var i,s,n,o=this;if(null!=t)return ce(t,0,oe),null==e?e=m:ce(e,0,8),P(new M(o),t+o.e+1,e);if(!(i=o.c))return null;if(s=((n=i.length-1)-ae(this.e/ee))*ee,n=i[n])for(;n%10==0;n/=10,s--);return s<0&&(s=0),s},u.dividedBy=u.div=function(t,e){return i(this,new M(t,e),_,m)},u.dividedToIntegerBy=u.idiv=function(t,e){return i(this,new M(t,e),0,1)},u.exponentiatedBy=u.pow=function(t,e){var i,s,n,o,a,r,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(Xt+"Exponent not an integer: "+R(t));if(null!=e&&(e=new M(e)),a=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(+R(h),a?t.s*(2-he(t)):+R(t))),e?c.mod(e):c;if(r=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!r&&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||a&&h.c[1]>=24e7:h.c[0]<8e13||a&&h.c[0]<=9999975e7)))return o=h.s<0&&he(t)?-0:0,h.e>-1&&(o=1/o),new M(r?1/o:o);k&&(o=Jt(k/ee+2))}for(a?(i=new M(.5),r&&(t.s=1),l=he(t)):l=(n=Math.abs(+R(t)))%2,c=new M(p);;){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=Zt(n/2)))break;l=n%2}else if(P(t=t.times(i),t.e+1,1),t.e>14)l=he(t);else{if(0===(n=+R(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:(r&&(c=p.div(c)),e?c.mod(e):o?P(c,k,m,void 0):c)},u.integerValue=function(t){var e=new M(this);return null==t?t=m:ce(t,0,8),P(e,e.e+1,t)},u.isEqualTo=u.eq=function(t,e){return 0===le(this,new M(t,e))},u.isFinite=function(){return!!this.c},u.isGreaterThan=u.gt=function(t,e){return le(this,new M(t,e))>0},u.isGreaterThanOrEqualTo=u.gte=function(t,e){return 1===(e=le(this,new M(t,e)))||0===e},u.isInteger=function(){return!!this.c&&ae(this.e/ee)>this.c.length-2},u.isLessThan=u.lt=function(t,e){return le(this,new M(t,e))<0},u.isLessThanOrEqualTo=u.lte=function(t,e){return-1===(e=le(this,new M(t,e)))||0===e},u.isNaN=function(){return!this.s},u.isNegative=function(){return this.s<0},u.isPositive=function(){return this.s>0},u.isZero=function(){return!!this.c&&0==this.c[0]},u.minus=function(t,e){var i,s,n,o,a=this,r=a.s;if(e=(t=new M(t,e)).s,!r||!e)return new M(NaN);if(r!=e)return t.s=-e,a.plus(t);var l=a.e/ee,c=t.e/ee,h=a.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?a:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?a:3==m?-0:0)}if(l=ae(l),c=ae(c),h=h.slice(),r=l-c){for((o=r<0)?(r=-r,n=h):(c=l,n=d),n.reverse(),e=r;e--;n.push(0));n.reverse()}else for(s=(o=(r=h.length)<(e=d.length))?r:e,r=e=0;e0)for(;e--;h[i++]=0);for(e=te-1;s>r;){if(h[--s]=0;){for(i=0,u=y[n]%g,p=y[n]/g|0,o=n+(a=l);o>n;)i=((c=u*(c=v[--a]%g)+(r=p*c+(h=v[a]/g|0)*u)%g*g+_[o]+i)/m|0)+(r/g|0)+p*h,_[o--]=c%m;_[o]=i}return i?++s:_.splice(0,1),T(t,_,s)},u.negated=function(){var t=new M(this);return t.s=-t.s||null,t},u.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/ee,a=t.e/ee,r=s.c,l=t.c;if(!o||!a){if(!r||!l)return new M(n/0);if(!r[0]||!l[0])return l[0]?t:new M(r[0]?s:0*n)}if(o=ae(o),a=ae(a),r=r.slice(),n=o-a){for(n>0?(a=o,i=l):(n=-n,i=r),i.reverse();n--;i.push(0));i.reverse()}for((n=r.length)-(e=l.length)<0&&(i=l,l=r,r=i,e=n),n=0;e;)n=(r[--e]=r[e]+l[e]+n)/te|0,r[e]=te===r[e]?0:r[e]%te;return n&&(r=[n].concat(r),++a),T(t,r,a)},u.precision=u.sd=function(t,e){var i,s,n,o=this;if(null!=t&&t!==!!t)return ce(t,1,oe),null==e?e=m:ce(e,0,8),P(new M(o),t,e);if(!(i=o.c))return null;if(s=(n=i.length-1)*ee+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},u.shiftedBy=function(t){return ce(t,-9007199254740991,ie),this.times("1e"+t)},u.squareRoot=u.sqrt=function(){var t,e,s,n,o,a=this,r=a.c,l=a.s,c=a.e,h=_+4,d=new M("0.5");if(1!==l||!r||!r[0])return new M(!l||l<0&&(!r||r[0])?NaN:r?a:1/0);if(0==(l=Math.sqrt(+R(a)))||l==1/0?(((e=re(r)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=ae((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(a,o,h,1))),re(o.c).slice(0,l)===(e=re(s.c)).slice(0,l)){if(s.e0&&_>0){for(o=_%r||r,h=p.substr(0,o);o<_;o+=r)h+=c+p.substr(o,r);l>0&&(h+=c+p.slice(o)),u&&(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||"")},u.toFraction=function(t){var e,s,n,o,a,r,l,c,h,d,u,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(p)))throw Error(Xt+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+R(l));if(!f)return new M(g);for(e=new M(p),h=s=new M(p),n=c=new M(p),_=re(f),a=e.e=_.length-g.e-1,e.c[0]=se[(r=a%ee)<0?ee+r:r],t=!t||l.comparedTo(e)>0?a>0?e:h:l,r=y,y=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=g.s,u=i(h,n,a*=2,m).minus(g).abs().comparedTo(i(c,s,a,m).minus(g).abs())<1?[h,n]:[c,s],y=r,u},u.toNumber=function(){return+R(this)},u.toPrecision=function(t,e){return null!=t&&ce(t,1,oe),C(this,t,e,2)},u.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<=g||o>=f?de(re(i.c),o):ue(re(i.c),o,"0"):10===t&&S?e=ue(re((i=P(new M(i),_+o+1,m)).c),i.e,"0"):(ce(t,2,x.length,"Base"),e=s(ue(re(i.c),o,"0"),10,t,n,!0)),n<0&&i.c[0]&&(e="-"+e)),e},u.valueOf=u.toJSON=function(){return R(this)},u._isBigNumber=!0,u[Symbol.toStringTag]="BigNumber",u[Symbol.for("nodejs.util.inspect.custom")]=u.valueOf,null!=e&&M.set(e),M}(),_e=class{key;left=null;right=null;constructor(t){this.key=t}},me=class extends _e{constructor(t){super(t)}},ge=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,a=e;const r=this.compare;let l;for(;;)if(l=r(a.key,t),l>0){let e=a.left;if(null==e)break;if(l=r(e.key,t),l>0&&(a.left=e.right,e.right=a,a=e,e=a.left,null==e))break;null==i?s=a:i.left=a,i=a,a=e}else{if(!(l<0))break;{let e=a.right;if(null==e)break;if(l=r(e.key,t),l<0&&(a.right=e.left,e.left=a,a=e,e=a.right,null==e))break;null==n?o=a:n.right=a,n=a,a=e}}return null!=n&&(n.right=a.left,a.left=o),null!=i&&(i.left=a.right,a.right=s),this.root!==a&&(this.root=a,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)}}},fe=class t extends ge{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 me(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new me(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 me(t.key);return function t(e,i){let s,n;do{if(s=e.left,n=e.right,null!=s){const e=new me(s.key);i.left=e,t(s,e)}if(null!=n){const t=new me(n.key);i.right=t,e=n,i=t}}while(null!=n)}(t,e),e}toSet(){return this.clone()}entries(){return new be(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new ye(this.wrap())}[Symbol.toStringTag]="[object Set]"},ve=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}},ye=class extends ve{getValue(t){return t.key}},be=class extends ve{getValue(t){return[t.key,t.key]}},we=t=>()=>t,ke=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):we(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function $e(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)):we(!1);return(t,i,s)=>{const n=t.x,o=t.y,a=s.x,r=s.y,l=o.minus(r).times(i.x.minus(a)).minus(n.minus(a).times(i.y.minus(r)));return e(l,n,o,a,r)?0:l.comparedTo(0)}}var xe=t=>t,Se=t=>{if(t){const e=new fe(ke(t)),i=new fe(ke(t)),s=(t,e)=>e.addAndReturn(t),n=t=>({x:s(t.x,e),y:s(t.y,i)});return n({x:new pe(0),y:new pe(0)}),n}return xe},Me=t=>({set:t=>{Ce=Me(t)},reset:()=>Me(t),compare:ke(t),snap:Se(t),orient:$e(t)}),Ce=Me(),De=(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),Te=(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}}},Pe=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),Re=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),Ae=t=>Re(t,t).sqrt(),Ee=(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 Pe(n,s).div(Ae(n)).div(Ae(s))},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 Re(n,s).div(Ae(n)).div(Ae(s))},ze=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},Fe=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},Ne=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:je.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:Ee(this.point,t.point,s.point),cosine:Ie(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:a,cosine:r}=e.get(s);return n.isGreaterThanOrEqualTo(0)&&a.isGreaterThanOrEqualTo(0)?o.isLessThan(r)?1:o.isGreaterThan(r)?-1:0:n.isLessThan(0)&&a.isLessThan(0)?o.isLessThan(r)?-1:o.isGreaterThan(r)?1:0:a.isLessThan(n)?-1:a.isGreaterThan(n)?1:0}}},Oe=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(a.isLessThan(r)&&a.isLessThan(c))return-1;if(a.isGreaterThan(r)&&a.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(a.isLessThan(r))return-1;if(a.isGreaterThan(r))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(a),e=n.minus(i),h=c.minus(r),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=++We,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,a;const r=Ne.comparePoints(e,i);if(r<0)n=e,o=i,a=1;else{if(!(r>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);n=i,o=e,a=-1}const l=new Ne(n,!0),c=new Ne(o,!1);return new t(l,c,[s],[a])}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 Ce.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=Te(e,i);if(null===s)return null;const n=this.leftSE.point,o=this.rightSE.point,a=t.leftSE.point,r=t.rightSE.point,l=De(e,a)&&0===this.comparePoint(a),c=De(i,n)&&0===t.comparePoint(n),h=De(e,r)&&0===this.comparePoint(r),d=De(i,o)&&0===t.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?r:null;if(c)return h&&n.x.eq(r.x)&&n.y.eq(r.y)?null:n;if(l)return d&&o.x.eq(a.x)&&o.y.eq(a.y)?null:a;if(d&&h)return null;if(d)return o;if(h)return r;const u=((t,e,i,s)=>{if(e.x.isZero())return Fe(i,s,t.x);if(s.x.isZero())return Fe(t,e,i.x);if(e.y.isZero())return ze(i,s,t.y);if(s.y.isZero())return ze(t,e,i.y);const n=Pe(e,s);if(n.isZero())return null;const o={x:i.x.minus(t.x),y:i.y.minus(t.y)},a=Pe(o,e).div(n),r=Pe(o,s).div(n),l=t.x.plus(r.times(e.x)),c=i.x.plus(a.times(s.x)),h=t.y.plus(r.times(e.y)),d=i.y.plus(a.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(n,this.vector(),a,t.vector());return null===u?null:De(s,u)?Ce.snap(u):null}split(e){const i=[],s=void 0!==e.events,n=new Ne(e,!0),o=new Ne(e,!1),a=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(n);const r=new t(n,a,this.rings.slice(),this.windings.slice());return Ne.comparePoints(r.leftSE.point,r.rightSE.point)>0&&r.swapEvents(),Ne.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=Ce.snap({x:new pe(t[0][0]),y:new pe(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;eUe.run("union",t,e),Je=(t,...e)=>Ue.run("intersection",t,e),Ze=(t,...e)=>Ue.run("difference",t,e);function Xe(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function Qe(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 ti(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 ei(t,e,i=1){const s=t[0].toFixed(i),n=t[1].toFixed(i),o=e[0].toFixed(i),a=e[1].toFixed(i),r=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 si(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 ni(t){const e=[],i=new Set;for(const s of t||[]){const t=ii(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 r=Math.round(u*i/s.step)*s.step;Math.abs(u*i-i/2)<=s.step/2&&(r=i/2),r=Math.max(o,Math.min(i-o,r));const l=r/i;n={x:e+l*c,y:a+l*h,angle:t}}else n={x:p[0],y:p[1],angle:t}}}return n}function ai(t,e,i,s,n,o=1){const a=e*Math.PI/180,r=[Math.cos(a),Math.sin(a)];let l=null,c=o;for(const e of ni(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(r[0]*(e[1]-t[1])-r[1]*(e[0]-t[0]));if(s(i[0])>o||s(i[1])>o)continue;const n=(i[0][0]-t[0])*r[0]+(i[0][1]-t[1])*r[1],a=(i[1][0]-t[0])*r[0]+(i[1][1]-t[1])*r[1],h=Math.min(n,a),d=Math.max(n,a),u=h>0?h:d<0?-d:0;u[t[0]+r[0]*e,t[1]+r[1]*e],g=(h+d)/2;return{wallA:m(h),wallB:m(d),sideA:p,sideB:_,midA:m((h-u)/2),midB:m((u+d)/2),wallCenter:m(g),centered:Math.abs(g)<=n}}function ri(t,e,i=!1){if(null==e||"unavailable"===e||"unknown"===e)return"window"===t?0:1;const s=function(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}(e)!==!!i;return s?1:0}function li(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(r-o)*(t[1]-a)/(l-a)+o&&(i=!i)}return i}function hi(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;let a=o?((t[0]-e[0])*s+(t[1]-e[1])*n)/o:0;return a=Math.max(0,Math.min(1,a)),Math.hypot(t[0]-(e[0]+a*s),t[1]-(e[1]+a*n))}function di(t,e,i=1e-6){if(!e||e.length<2)return!1;for(let s=0;sn&&a<-n||o<-n&&a>n)&&(r>n&&l<-n||r<-n&&l>n)}function mi(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),n=Math.min(...i),o=Math.max(...i),a=Math.min(...s),r=Math.max(...s),l=Math.max(o-n,r-a)||1;let c=0,h=0,d=0;for(let e=0;e1e-9?[h/(3*c),d/(3*c)]:[(n+o)/2,(a+r)/2],p=(e,i)=>{const s=((e,i)=>{if(!ci([e,i],t))return-1/0;let s=1/0;for(let n=0;nm&&(m=c,_=[s,l])}if(_){const[t,i]=_,s=(o-n)/e,l=(r-a)/e;for(let e=-4;e<=4;e++)for(let n=-4;n<=4;n++){const o=t+s*e/4,a=i+l*n/4,r=p(o,a);r>m&&(m=r,_=[o,a])}}return _||gi(t)||t[0]}function gi(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(ui(s,t,e))return s;for(let s=0;svi(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&vi(e,t,i)))}function wi(t){if(!t||t.length<3)return 0;let e=0;for(let i=0;i[t[0],t[1]]),[t[0][0],t[0][1]]]]}function $i(t,e,i){for(let s=0;s1&&li(i[0],i[i.length-1],e)&&i.pop(),i}function Si(t,e,i){const[s,n]=t.split(":");return"device"===s?n:"entity"===s?"lg_"+n:e&&e.startsWith("v_")?e:i()}function Mi(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function Ci(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 Di(t,e,i,s){if(t.length<2)return;const n=e.x+s,o=e.x+e.w-s,a=e.y+s,r=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;s=0)i=e.slice(0,n).trim(),s=e.slice(n+1).trim();else{const t=e.split(".");t.length>2&&(i=t.slice(0,2).join("."),s=t.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?n>=0&&!s||s&&!/^[a-zA-Z0-9_.-]+$/.test(s)?null:s?{entity:i,attr:s}:{entity:i}:null}function Hi(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),n=Li(s?`${i}:${s}`:i);return n?`{${n.entity}${n.attr?`:${n.attr}`:""}}`:""}function qi(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(),a=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:a(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const r=n.state;if(null==r||""===r)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,n);if("string"==typeof e&&""!==e)return{text:a(e),formatted:!0}}catch{}return{text:a(String(r)),formatted:!1}}function Bi(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 Ui(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 Ni;const o=(e?.attr||"").trim(),a=qi(t,i,o||null);if(null===a)return Ni;return Bi(a,o?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}function Wi(t,e,i,s=()=>!0){const n=t??"";let o=!1;const a=n.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const n=Li(e);return n?(o=!0,s(n.entity||"")?Ui(i,n):Ni):t});if(o)return a;const r=(e?.entity||"").trim();if(!r)return n;if(!s(r)){const t=n.indexOf(Oi);return t>=0?n.slice(0,t)+Ni+n.slice(t+2):n?`${n} ${Ni}`:Ni}const l=Ui(i,e),c=n.indexOf(Oi);return c>=0?n.slice(0,c)+l+n.slice(c+2):n?`${n} ${l}`:l}const ji=20;function Gi(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 Vi(t,e){if(!e)return t;let i=t;for(const[t,s]of Object.entries(e))i=i.split("{"+t+"}").join(String(s));return i}const Ki="#55606c",Yi=.55,Ji={c:"#607d8b",a:.18};function Zi(t,e=Ji){const i=t&&"object"==typeof t?t:null,s=i?.a;return{c:Dt(i?.c,e.c),a:"number"==typeof s&&Number.isFinite(s)?Math.min(1,Math.max(0,s)):e.a}}function Xi(t,e){const i=Zi(t),s=e?.settings?.custom_fill;return s&&"object"==typeof s?Zi(s,i):i}function Qi(t){const e=t?.settings||{},i=!t?.plan_url,s="glow"===e.fill_mode;return{showBorders:e.show_borders??i,showNames:e.show_names??i,color:Dt(e.room_color,Ki),opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):Yi,fill:["lqi","light","temp","custom"].includes(e.fill_mode)?e.fill_mode:"none",customFill:Zi(e.custom_fill),glow:"boolean"==typeof e.glow_enabled?e.glow_enabled:s,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:Dt(e.bg_color,null),hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function ts(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return Dt(i,"")}const es={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}};function is(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(es)){const s=es[t],n=i[t];e[t]={c:Dt(n?.c,s.c),a:n&&"number"==typeof n.a?Math.min(1,Math.max(0,n.a)):s.a}}return e}function ss(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)),a=n.map((t,e)=>Math.round(t+(o[e]-t)*s));return"#"+a.map(t=>t.toString(16).padStart(2,"0")).join("")}function ns(t,e,i,s,n,o,a,r=Ji){const l=function(t,e,i,s,n,o,a,r=Ji){if("custom"===t)return Zi(r);if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:ss(a.lqi_low.c,a.lqi_high.c,t),a:a.lqi_low.a+(a.lqi_high.a-a.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?a.light_none.a>0?a.light_none:null:"on"===i?a.light_on:a.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(n,o),e=Math.max(n,o);return se?a.temp_hot:a.temp_ok}return null}(t,e,i,s,n,o,a,r);return l?{color:l.c,opacity:l.a,mode:t}:null}const os={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"]},as=[["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 rs(t){for(const e of[...Object.values(os),...as])if(t===e[0]||t===e[1])return e;return null}function ls(t,e,i,s,n){if(!s||"unavailable"===s||"unknown"===s)return t;if(n){const i="cover"===e?rs(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=os[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const n=rs(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 cs(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)]}const hs=1/2.2;function ds(t){const e=/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/.exec(t);return e?"#"+e.slice(1).map(t=>Math.min(255,Number(t)).toString(16).padStart(2,"0")).join(""):null}function us(t){if(!t||"object"!=typeof t||Array.isArray(t))return null;const e=t;if(Object.keys(e).some(t=>"c"!==t&&"bri"!==t))return null;const i=Dt(e.c,null);return i?void 0===e.bri||null===e.bri?{c:i}:"number"!=typeof e.bri||!Number.isFinite(e.bri)||e.bri<.01||e.bri>1?null:{c:i,bri:e.bri}:null}function ps(t,e,i){const s=us(e),n=t?.attributes||{},o=s?.bri??function(t){const e=t?.attributes?.brightness,i="number"==typeof e?e:"string"==typeof e&&""!==e.trim()?Number(e):Number.NaN;return Number.isFinite(i)?Math.max(0,Math.min(1,i/255)):1}(t);if(s)return{c:s.c,bri:o};const a=Rt(n.rgb_color);if(a)return{c:ds(a)||Dt(i,"#ffd9a0"),bri:o};const r=Number(n.color_temp_kelvin)||(Number(n.color_temp)>0?1e6/Number(n.color_temp):NaN);return Number.isFinite(r)&&r>0?{c:(l=cs(r),"#"+l.slice(0,3).map(t=>Math.min(255,Math.max(0,Math.round(Number(t)||0))).toString(16).padStart(2,"0")).join("")),bri:o}:{c:Dt(i,"#ffd9a0"),bri:o};var l}function _s(t,e,i){return"on"===t?.state?ps(t,e,i):null}function ms(t,e=1){const i=Math.max(0,Math.min(1,Number.isFinite(t)?t:1)),s=.7*Math.max(0,Math.min(1,Number.isFinite(e)?e:1))*(.4+.6*Math.pow(i,hs));return Math.max(0,Math.min(1,s))}function gs(t){return t.startsWith("light.")||t.startsWith("switch.")}function fs(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let n=0;nu||l>u)continue;const p=(n[0]-o[0])*h+(n[1]-o[1])*d,_=(a[0]-o[0])*h+(a[1]-o[1])*d,m=Math.max(0,Math.min(p,_)),g=Math.min(c,Math.max(p,_));g-m>i&&s.push([o[0]+h*m,o[1]+d*m,o[0]+h*g,o[1]+d*g])}}return s}function vs(t,e,i=1e-6){const s=[];for(const n of t){const t=[n[0],n[1]],o=[n[2],n[3]],a=o[0]-t[0],r=o[1]-t[1],l=Math.hypot(a,r);if(lo||n>o)continue;const a=(s[0]-t[0])*c+(s[1]-t[1])*h,r=(s[2]-t[0])*c+(s[3]-t[1])*h,u=Math.max(0,Math.min(a,r)),p=Math.min(l,Math.max(a,r));p-u>i&&d.push([u,p])}if(!d.length){s.push([t[0],t[1],o[0],o[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let u=0;for(const[e,n]of d)e-u>i&&s.push([t[0]+c*u,t[1]+h*u,t[0]+c*e,t[1]+h*e]),u=Math.max(u,n);l-u>i&&s.push([t[0]+c*u,t[1]+h*u,o[0],o[1]])}return s}function ys(t,e,i=1e-6){const s=[];for(let e=0;e{if("string"!=typeof t||!t)return;const i=$s(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 $s(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 xs(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i||"custom"===i?i:t}function Ss(t,e){const i=e?.settings;return"boolean"==typeof i?.glow?i.glow:"glow"===i?.fill_mode||t}function Ms(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Cs(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Ds(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Ts(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 Ps=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);function Rs(t,e){return"alarm_control_panel"===t||"siren"===t||"binary_sensor"===t&&!!e&&Ps.has(e)}function As(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}}const Es={availability:"available",status:"alarm",activity:"none"},Is=new Set(["motion","vibration","sound"]),zs=new Set(["occupancy","presence"]),Fs=new Set(["door","window","garage_door","opening"]),Ns=new Set(["running","power"]),Os=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Ls=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),Hs=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping"]),qs=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),Bs=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,Us=t=>String(t??"").trim().toLowerCase();function Ws(t,e){if(!e.startsWith("switch."))return!1;const i=t?.entities?.[e]||{},s=t?.states?.[e],n=e.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(n))return!0;if([i.translation_key,i.original_name,i.name].map(Us).some(t=>["power","main power","power switch","питание"].includes(t)))return!0;const o=Us(s?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(o)||/(?:^|[\s._-])питание$/.test(o)}function js(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=Us(t?.[e]);if(Ls.has(i)||Hs.has(i))return i}return""}function Gs(t,e){const i=t?.states?.[e],s=i?Us(i.state):"__missing__",n=String(e||"").split(".")[0],o=Us(i?.attributes?.device_class),a={eid:e,state:s,availability:Bs(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===a.availability)return a;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&Rs(t,e)}(n,o,s))return{...a,status:"alarm"};if("binary_sensor"===n)return Is.has(o)?{...a,edge:"rising"}:zs.has(o)?{...a,activity:"on"===s?"presence":"none"}:Fs.has(o)?{...a,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===o?{...a,activity:"on"===s?"transition":"none"}:Ns.has(o)&&"on"===s?{...a,status:"working",activity:"running"}:a;if("cover"===n)return{...a,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===n)return{...a,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===n)return{...a,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===n){const t=js(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(Us):[],n=!Hs.has(s)&&(qs.has(s)||Ls.has(s)||e.includes(s));return(t?Ls.has(t):n)?{...a,status:"working",activity:"running"}:a}if(["light","switch","fan","humidifier"].includes(n))return"on"===s?{...a,status:"working",activity:"running"}:a;if("media_player"===n)return"off"===s?{...a,availability:"unavailable"}:a;if("vacuum"===n)return"cleaning"===s?{...a,status:"working",activity:"running"}:"returning"===s?{...a,status:"working",activity:"transition"}:a;if("script"===n)return"on"===s?{...a,status:"working",activity:"running"}:a;if("automation"===n)return a;if("button"===n||"event"===n)return{...a,edge:"change"};const r=js(i.attributes);return Ls.has(r)||Ls.has(s)&&!Hs.has(s)?{...a,status:"working",activity:"running"}:a}function Vs(t,e){if(!(t=>!!t&&!Bs(t))(t)||"unavailable"===e.availability||t===e.state)return null;if("rising"===e.edge)return"off"===t&&"on"===e.state?"event":null;if("change"===e.edge)return"event";if("terminal_transition"===e.edge){const i=new Set([t,e.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}const Ks=new WeakMap,Ys=new WeakMap;let Js=1;const Zs="houseplan.ha-binding-status.v1";let Xs=null;function Qs(t){const e=function(t){const e=t?.connection||t;return!e||"object"!=typeof e&&"function"!=typeof e?null:e}(t);if(!e)return null;let i=Ks.get(e);return i||(i={revision:0,authoritative:!1,access:"pending",devices:{},entities:{},lastSuccess:0,listeners:new Set,refs:0},Ks.set(e,i)),i}function tn(t,e){const i=Array.isArray(t)?t:t&&"object"==typeof t&&Array.isArray(t.entries)?t.entries:null;if(!i)return null;const s={};for(const t of i){const i=t?.[e];"string"==typeof i&&i&&(s[i]=t)}return s}async function en(t,e){return e.loading||!t?.callWS||(e.loading=(async()=>{try{const[i,s]=await Promise.all([t.callWS({type:"config/device_registry/list"}),t.callWS({type:"config/entity_registry/list"})]),n=tn(i,"id"),o=tn(s,"entity_id");if(!n||!o)throw new Error("invalid_registry_response");e.devices=n,e.entities=o,e.authoritative=!0,e.access="full",e.lastSuccess=Date.now(),e.error=void 0}catch(t){e.authoritative=!1,e.access="limited",e.error=function(t){if(t&&"object"==typeof t){const e=t;return String(e.message||e.code||e.error||"registry_unavailable")}return String(t||"registry_unavailable")}(t)}finally{e.revision++,e.loading=void 0,function(t){for(const e of[...t.listeners])try{e()}catch{}}(e)}})()),e.loading}function sn(t,e){void 0===e.reloadTimer&&(e.reloadTimer=globalThis.setTimeout(()=>{e.reloadTimer=void 0,en(t,e)},80))}async function nn(t,e){if(e.subscribing||e.unsubDevice&&e.unsubEntity)return e.subscribing;const i=t?.connection?.subscribeEvents;return"function"==typeof i?(e.subscribing=(async()=>{try{e.unsubDevice||(e.unsubDevice=await i.call(t.connection,()=>sn(t,e),"device_registry_updated")),e.unsubEntity||(e.unsubEntity=await i.call(t.connection,()=>sn(t,e),"entity_registry_updated"))}catch{}finally{0===e.refs&&(e.unsubDevice?.(),e.unsubEntity?.(),e.unsubDevice=void 0,e.unsubEntity=void 0),e.subscribing=void 0}})(),e.subscribing):void 0}function on(t,e){const i=Qs(t);if(!i)return()=>{};const s=0===i.refs;i.refs++,i.listeners.add(e),("pending"===i.access||s)&&en(t,i),nn(t,i);let n=!1;return()=>{n||(n=!0,i.listeners.delete(e),i.refs=Math.max(0,i.refs-1),i.refs>0||(i.unsubDevice?.(),i.unsubEntity?.(),i.unsubDevice=void 0,i.unsubEntity=void 0,void 0!==i.reloadTimer&&globalThis.clearTimeout(i.reloadTimer),i.reloadTimer=void 0))}}function an(t){const e=Qs(t);if(!e)return{revision:0,authoritative:!1,access:"limited",devices:t?.devices||{},entities:t?.entities||{},lastSuccess:0,error:"registry_unavailable"};let i=t?.devices||{},s=t?.entities||{};if(e.authoritative){const n=e.liveDevices!==i||e.liveEntities!==s,o=void 0!==e.liveDevices||void 0!==e.liveEntities;if(n&&(e.liveDevices=i,e.liveEntities=s,e.projectedRevision=void 0,o&&sn(t,e)),e.projectedRevision!==e.revision||!e.projectedDevices||!e.projectedEntities){const t={...e.devices},n={...e.entities};for(const[e,s]of Object.entries(i))Object.prototype.hasOwnProperty.call(t,e)||(t[e]=s);for(const[t,e]of Object.entries(s))Object.prototype.hasOwnProperty.call(n,t)||(n[t]=e);e.projectedDevices=t,e.projectedEntities=n,e.projectedRevision=e.revision}i=e.projectedDevices,s=e.projectedEntities}return{revision:e.revision,authoritative:e.authoritative,access:e.access,devices:i,entities:s,lastSuccess:e.lastSuccess,error:e.error}}function rn(t){if(!t||"object"!=typeof t&&"function"!=typeof t)return 0;let e=Ys.get(t);return e||(e=Js++,Ys.set(t,e)),e}function ln(t,e=an(t)){return[e.revision,e.access,rn(e.devices),rn(e.entities)].join(":")}function cn(t){return!!t&&null==t.disabled_by}function hn(t,e=an(t)){const i={},s={},n={};for(const[t,s]of Object.entries(e.devices||{}))cn(s)&&(i[t]=s);for(const[t,i]of Object.entries(e.entities||{})){if(!cn(i))continue;const n=i.device_id?e.devices?.[i.device_id]:null;e.authoritative&&i.device_id&&!n||(n&&!cn(n)||(s[t]=i))}for(const[i,s]of Object.entries(t?.states||{})){const t=e.entities?.[i];if(e.authoritative&&!t)continue;if(t&&!cn(t))continue;const o=t?.device_id?e.devices?.[t.device_id]:null;e.authoritative&&t?.device_id&&!o||(o&&!cn(o)||(n[i]=s))}return{...t,devices:i,entities:s,states:n}}function dn(t,e=an(t)){return{...t,devices:e.devices||{},entities:e.entities||{}}}function un(t,e){const i=[];for(const[s,n]of Object.entries(t||{}))n?.device_id===e&&i.push(s);return i}function pn(){if(Xs)return Xs;if("undefined"==typeof localStorage)return{};try{const t=JSON.parse(localStorage.getItem(Zs)||"{}");if(!t||"object"!=typeof t||Array.isArray(t))return{};const e=Date.now(),i={};for(const[s,n]of Object.entries(t))!n||"active"!==n.kind&&"ha_disabled"!==n.kind||!Number.isFinite(n.ts)||e-n.ts>7776e6||(i[s]=n);return Xs=i,Xs}catch{return{}}}function _n(t,e,i=an(t)){if(!e||"virtual"===e)return{kind:"active",enabledEntityIds:[],allEntityIds:[]};const s=e.indexOf(":");if(s<1)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const n=e.slice(0,s),o=e.slice(s+1);if("device"!==n&&"entity"!==n||!o)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const a=i.devices||{},r=i.entities||{};if(i.authoritative){if("device"===n){const t=a[o];if(!t)return{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[]};const e=un(r,o);if(!cn(t))return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:e};const i=e.filter(t=>cn(r[t]));return e.length&&!i.length?{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:e}:{kind:"active",enabledEntityIds:i,allEntityIds:e}}const e=r[o];return e&&!cn(e)?{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]}:e?.device_id&&!a[e.device_id]?{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[o]}:e?.device_id&&!cn(a[e.device_id])?{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]}:e||t?.states?.[o]?{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}:{kind:"orphaned",reason:"entity_missing",enabledEntityIds:[],allEntityIds:[]}}const l=function(t){return pn()[t]||null}(e);if("device"===n){const e=a[o],i=un(r,o);if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:i};const s=i.filter(t=>{const e=r[t];return null==e?.disabled_by&&(!e.device_id||null==a[e.device_id]?.disabled_by)});if(i.length&&!s.length&&i.every(t=>null!=r[t]?.disabled_by))return{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:i};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"device",enabledEntityIds:[],allEntityIds:i};if(e||s.some(e=>!!t?.states?.[e]))return{kind:"active",enabledEntityIds:s,allEntityIds:i}}else{const e=r[o];if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]};if(e?.device_id&&null!=a[e.device_id]?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"entity",enabledEntityIds:[],allEntityIds:[o]};if(e||t?.states?.[o])return{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}}return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]}}function mn(t){const e={};for(const[i,s]of Object.entries(t.entities)){if(!s?.device_id||!cn(s))continue;const n=t.devices?.[s.device_id];n&&!cn(n)||(e[s.device_id]=e[s.device_id]||[]).push(i)}return e}function gn(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 fn(t,e){if(/_device_temperature$/.test(e))return!1;if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_temperature$/.test(e);const s=i.attributes||{};return"temperature"===s.device_class||/°C|°F/.test(s.unit_of_measurement||"")||/_temperature$/.test(e)}const vn=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],yn=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function bn(t,e){const i=e.map(e=>({eid:e,reg:t?.entities?.[e]})).filter(t=>!!t.reg);if(!i.length)return[];const s=i.filter(t=>!t.reg.entity_category),n=s.length?s:i;for(const t of vn){const e=n.filter(e=>e.eid.startsWith(t+"."));if(e.length)return yn(e).map(t=>t.eid)}const o=n.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=Us(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return Is.has(i)||zs.has(i)||Fs.has(i)||Ns.has(i)||"moving"===i||Os.has(i)}(t,e.eid));if(o.length)return yn(o).map(t=>t.eid);const a=n.filter(t=>t.eid.startsWith("switch."));if(a.length){const e=yn(a);return[(e.find(e=>Ws(t,e.eid))||e[0]).eid]}const r=[];for(const t of Kt)r.push(...yn(n.filter(e=>e.eid.startsWith(t+"."))));return r.push(...yn(n.filter(t=>!Kt.includes(t.eid.split(".")[0])))),r.map(t=>t.eid)}function wn(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=>fn(t,e.eid));if(i)return i.eid}return bn(t,e)[0]}function kn(t,e,i=[]){return[...new Set($n(t,e,i))].filter(t=>gs(t))}function $n(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).filter(t=>"string"==typeof t&&!s.has(t))}function xn(t,e){if("string"==typeof e)return t.area===e;const i=t.marker?.room_id;return i?!!e.id&&i===e.id:!!e.area&&t.area===e.area}function Sn(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(gs),s=t.primary&&gs(t.primary)?t.primary:null;return[...new Set([e,s,...i].filter(t=>!!t&&gs(t)))]}function Mn(t){const e=Sn(t),i=t.marker?.light_entity;return i&&e.includes(i)?i:e[0]||null}function Cn(t,e){if(!1===e.marker?.is_light)return[];if(!0===e.marker?.is_light){return[{eid:Mn(e),via:"forced"}]}const i=e.primary||bn(t,e.entities)[0];return i&&!i.startsWith("light.")?[]:e.entities.filter(e=>e.startsWith("light.")&&!!t.states?.[e]).map(t=>({eid:t,via:"light"}))}const Dn=new WeakMap;function Tn(t,e){const i=kn(e.marker?.binding,e.controls??e.marker?.controls,e.entities),s=$n(e.marker?.binding,e.marker?.controls??e.controls,e.entities);return(null!=e.marker?.is_light||!i.length&&!s.some(t=>t.startsWith("marker:")))&&Cn(t,e).length>0}function Pn(t,e){const i=kn(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"})),s=Tn(t,e)?Cn(t,e):[];for(const t of s)t.eid&&i.some(e=>e.eid===t.eid)||i.push(t);return i}function Rn(t,e,i){const{visible:s,markerById:n,persistedByDevice:o}=function(t){const e=function(t){return t.map(t=>[t.id||"",!0===t.hidden?1:0,t.primary||"",[...t.entities].join(","),void 0===t.controls?"":[...t.controls].join(","),t.marker?.id||"",t.marker?.binding||"",t.marker?.is_light,t.marker?.light_entity||"",null==t.marker?.controls?"":[...t.marker.controls].join(",")].join("")).join("")}(t),i=Dn.get(t);if(i?.fingerprint===e)return i;const s=t.filter(t=>!t.hidden),n=new Map,o=new Map;for(const e of t){o.set(e,$n(e.marker?.binding,e.marker?.controls??e.controls,e.entities));const t=e.hidden||!0!==e.marker?.is_light?null:e.marker?.id||e.id;t&&n.set(String(t),e)}const a={fingerprint:e,visible:s,markerById:n,persistedByDevice:o};return Dn.set(t,a),a}(e),a=null==i?s:s.filter(t=>xn(t,i)),r=new Map,l=new Map;for(const e of a){const i=Pn(t,e).filter(t=>"controls"!==t.via);for(const s of i){const i=!0===e.marker?.is_light?String(e.marker?.id||e.id||""):"",n=i?`marker:${i}`:`entity:${s.eid}`,o=s.eid?[s.eid]:[],a={key:n,eid:s.eid||"",stateEids:o,serviceEids:o,device:e,via:s.via,castsGlow:!0,passive:!s.eid,on:!s.eid||"on"===t.states?.[s.eid]?.state};r.set(e,[...r.get(e)||[],a]),s.eid&&l.set(s.eid,a)}}const c=new Map,h=new Map;for(const t of e){const e=o.get(t)||[],i=new Set(t.hidden?[]:void 0===t.controls?e.filter(gs):t.controls);h.set(t,i);const s=t.hidden?null:Mn(t);for(const t of e)if(t.startsWith("marker:")){const e=t.slice(7),o=n.get(e);if(!o||!r.has(o))continue;const a=c.get(e)||{linked:!1,drivers:new Set};a.linked=!0;const l=i.size?i:new Set(s?[s]:[]);for(const t of l)a.drivers.add(t);c.set(e,a)}}for(const e of r.values())for(const i of e){if(!i.passive)continue;const e=i.key.slice(7),s=c.get(e);i.on=!s?.linked||[...s.drivers].some(e=>"on"===t.states?.[e]?.state)}const d=new Map,u=t=>{const e=d.get(t.key);(!e||t.castsGlow&&!e.castsGlow)&&d.set(t.key,t)};for(const e of s){const s=o.get(e)||[],a=null==i||xn(e,i);for(const i of s){if(i.startsWith("marker:")){const t=n.get(i.slice(7)),e=t?r.get(t):null;if(e)for(const t of e)u(t);continue}if(!gs(i)||!h.get(e)?.has(i))continue;const s=l.get(i);s?u(s):a&&u({key:`entity:${i}`,eid:i,stateEids:[i],serviceEids:[i],device:e,via:"controls",castsGlow:!1,passive:!1,on:"on"===t.states?.[i]?.state})}for(const t of r.get(e)||[])u(t)}return[...d.values()]}function An(t){const e=t.filter(t=>t.castsGlow);return e.find(t=>t.on)||e[0]||null}function En(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function In(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 Mi(i)}function zn(t,e){for(const i of e){if(!fn(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 Fn(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 Nn(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 On(t,e){for(const i of e){if(!Nn(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 Ln(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden||!cn(s))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(!cn(e))continue;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 Hn(t,e,i,s,n){const o=Vt(e,i,n);if(o!==Gt)return o;const a=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&a.push(i)}return function(t){for(const e of t){const t=jt[e];if(t)return t}return null}(a)??Gt}function qn(t){const e=new Set,i=new Set;for(const s of t||[]){if(!0!==s?.removed)continue;const t=String(s.binding||"").indexOf(":");if(t<1)continue;const n=s.binding.slice(0,t),o=s.binding.slice(t+1);o&&("device"===n?e.add(o):"entity"===n&&i.add(o))}return{devices:e,entities:i}}function Bn(t,e,i){if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function Un(t,e,i,s,n){const o=kn(e.binding,e.controls,t.entities).filter(t=>!Bn(i,t,s)).filter(t=>"active"===_n(i,"entity:"+t,n).kind);t.marker=e,t.controls=o,t.userHidden=!0===e.hidden,t.hidden=t.userHidden||"ha_disabled"===t.bindingStatus?.kind,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 Wn(t){const e=t.hass,i=t.registry||an(e),s=hn(e,i),n=dn(e,i),{areaToSpace:o,markers:a,settings:r,excluded:l,showAll:c,firstSpaceId:h,loc:d,iconRules:u}=t,p=!1!==r.group_lights,_=qn(a),m=Ln(s,p).filter(t=>!Bn(s,t.eid,_)),g=new Set(m.map(t=>t.area)),f=mn(s),v=function(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}(n),y=new Set;for(const t of a){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||y.add(t.binding)}const b=(t,e)=>a.find(i=>i.binding===t+":"+e),w={},k=[];for(const t of Object.values(s.devices)){const n=t.area_id;if(!n||!o[n])continue;if("service"===t.entry_type)continue;if(y.has("device:"+t.id))continue;const a=_n(e,"device:"+t.id,i);if("active"!==a.kind)continue;const h=b("device",t.id);if(h&&h.hidden&&!r.filter_seeded)continue;const _=f[t.id]||[],m=gn(s,t,_),v=!r.filter_seeded;if(v&&!c){if(l.has(m))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===m&&t.via_device_id)continue}const $=(t.name_by_user||t.name||d("device.unnamed")).trim(),x=$+"|"+n;let S=Hn(s,$,t.model,_,u);if(_.some(t=>t.startsWith("lock."))&&(S="mdi:lock"),v&&!c&&p&&"mdi:lightbulb"===S&&g.has(n))continue;w[x]=(w[x]||0)+1;const M=w[x]>1?$+" "+w[x]:$,C={id:t.id,name:M,model:t.model||"",area:n,space:o[n],icon:S,entities:_,allEntities:a.allEntityIds,bindingStatus:a,bindingKind:"device",bindingRef:t.id,pdfs:[]};C.primary=wn(s,_,S),"mdi:thermometer"!==S&&"mdi:air-filter"!==S||(C.temp=zn(s,_)),C.primary&&Nn(s,C.primary)&&(C.hum=On(s,_)),k.push(C)}for(const t of m)o[t.area]&&(y.has("entity:"+t.eid)||k.push({id:"lg_"+t.eid,name:t.name,model:d("device.light_group"),area:t.area,space:o[t.area],icon:"mdi:lightbulb-group",entities:[t.eid],allEntities:[t.eid],bindingStatus:{kind:"active",enabledEntityIds:[t.eid],allEntityIds:[t.eid]},primary:t.eid,bindingKind:"entity",bindingRef:t.eid,pdfs:[]}));for(const t of a){if(t.removed)continue;const[a,l]=t.binding.split(":"),c="device"===a||"entity"===a?_n(e,t.binding,i):null;if(!t.hidden||r.filter_seeded||"ha_disabled"===c?.kind)if("device"===a){const e=c;if("unverified"===e.kind)continue;const a=n.devices[l],r=t.area||a?.area_id||"",p=r&&o[r]||t.space||h,m="active"===e.kind?e.enabledEntityIds:[];let g=a?Hn(s,a.name_by_user||a.name||"",a.model,m,u):"mdi:help-circle";m.some(t=>t.startsWith("lock."))&&(g="mdi:lock");const f={id:t.id,name:a?.name_by_user||a?.name||d("device.fallback"),model:a?.model||"",area:r,space:p,icon:g,entities:m,allEntities:e.allEntityIds.length?e.allEntityIds:a&&v[a.id]||[],bindingStatus:e,bindingKind:"device",bindingRef:l};f.primary=wn(s,m,g),"mdi:thermometer"!==g&&"mdi:air-filter"!==g||(f.temp=zn(s,m)),f.primary&&Nn(s,f.primary)&&(f.hum=On(s,m)),Un(f,t,n,_,i),k.push(f)}else if("entity"===a){if(Bn(n,l,_))continue;const e=c;if("unverified"===e.kind)continue;const a=n.entities[l],r=t.area||a?.area_id||a?.device_id&&n.devices[a.device_id]?.area_id||"",d=r&&o[r]||t.space||h,p=s.states[l],m=a?.name||p?.attributes?.friendly_name||l;let g=Hn(s,m,"",[l],u);l.startsWith("lock.")&&(g="mdi:lock");const f={id:t.id,name:m,model:"",area:r,space:d,icon:g,entities:"active"===e.kind?[l]:[],allEntities:[l],bindingStatus:e,primary:"active"===e.kind?l:void 0,bindingKind:"entity",bindingRef:l};"mdi:thermometer"!==g&&"mdi:air-filter"!==g||!f.entities.length||(f.temp=zn(s,f.entities)),f.entities.length&&Nn(s,l)&&(f.hum=On(s,f.entities)),Un(f,t,n,_,i),k.push(f)}else{const e=t.area||"",s=t.space||e&&o[e]||h,a={id:t.id,name:t.name||d("device.virtual"),model:t.model||"",area:e,space:s,icon:t.icon||"mdi:map-marker",entities:[],allEntities:[],bindingStatus:{kind:"active",enabledEntityIds:[],allEntityIds:[]},bindingKind:"virtual",virtual:!0};Un(a,t,n,_,i),k.push(a)}}return k}function jn(t,e,i,s){if(!e)return null;if(function(t,e,i){if(!e)return!1;const s=e.indexOf(":");if(s<1)return!1;const n=e.slice(0,s),o=e.slice(s+1),a=qn(i);return"device"===n?a.devices.has(o):"entity"===n&&Bn(t,o,a)}(t,e,s))return null;const n=e.indexOf(":");if(n<0)return null;const o=e.slice(0,n),a=e.slice(n+1);if(!a)return null;if("entity"===o){const e=parseFloat(t.states[a]?.state);return Number.isFinite(e)?"temp"===i?Math.round(10*e)/10:Math.round(e):null}if("device"===o){const e=Object.entries(t.entities).filter(([,t])=>t.device_id===a).map(([t])=>t);return"temp"===i?zn(t,e):On(t,e)}return null}const Gn=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");function Vn(t,e,i){const s=[];for(const n of e){if(n.area!==i||n.virtual)continue;const e=In(t,n.entities);null!=e&&s.push(e)}return Mi(s)}function Kn(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=zn(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}const Yn={offStates:["off"],unknownUsesToggle:!0},Jn={light:Yn,switch:Yn,fan:Yn,humidifier:Yn,input_boolean:Yn,automation:Yn,remote:Yn,climate:{...Yn,featureMasks:{turn_on:256,turn_off:128,toggle:384}},media_player:{...Yn,featureMasks:{turn_on:128,turn_off:256,toggle:384}},siren:{...Yn,featureMasks:{turn_on:1,turn_off:2,toggle:3}},vacuum:{...Yn,featureMasks:{turn_on:1,turn_off:2,toggle:3}},water_heater:{...Yn,featureMasks:{turn_on:8,turn_off:8,toggle:8}},camera:{...Yn,featureMasks:{turn_on:1,turn_off:1,toggle:1}}};function Zn(t){return t.slice(0,t.indexOf("."))}function Xn(t,e,i){if(!t?.services||"object"!=typeof t.services)return!1;const s=t.services?.[e];return!!s&&Object.prototype.hasOwnProperty.call(s,i)}function Qn(t,e,i){return Xn(t,e,i)?{domain:e,service:i}:Xn(t,"homeassistant",i)?{domain:"homeassistant",service:i}:null}function to(t,e){return t?.entities?.[e]||null}function eo(t,e,i){const s=t?.states?.[i],n=to(e,i);return s?.attributes?.friendly_name||n?.name||n?.original_name||i}function io(t,e){const i=to(t,e);if(null!=i?.disabled_by)return!0;const s=i?.device_id?t?.devices?.[i.device_id]:null;return null!=s?.disabled_by}function so(t,e){const i=t?.attributes?.supported_features;if(null==i||""===i)return!1;const s=Number(i);return Number.isFinite(s)&&(s&e)===e}function no(t,e,i){const s=Zn(i);if("lock"===s||"alarm_control_panel"===s)return!0;if("cover"!==s)return!1;const n=t?.states?.[i],o=to(e,i),a=String(n?.attributes?.device_class||o?.device_class||o?.original_device_class||"");return zi.has(a)}function oo(t,e,i,s,n){return{ref:i,entityId:s,name:s?eo(t,e,s):null,reason:n}}function ao(t,e,i,s,n,o=null){return{target:null,skipped:oo(t,e,i,s,n),semantics:o,nextEffect:null,command:null}}function ro(t,e,i,s,n=i){const o=Zn(i);if("cover"!==o&&"valve"!==o)return function(t,e,i,s,n=i){const o=Zn(i),a=t?.states?.[i];if(io(e,i))return ao(t,e,n,i,"ha-disabled","power");if(no(t,e,i))return ao(t,e,n,i,"secure");if(!a)return ao(t,e,n,i,"missing","power");if("unavailable"===a.state)return ao(t,e,n,i,"unavailable","power");const r=Jn[o];if(!r)return ao(t,e,n,i,"unsupported");let l,c;if("unknown"===a.state||""===a.state){if(!r.unknownUsesToggle)return ao(t,e,n,i,"unsupported","power");l="toggle",c="toggle"}else r.offStates.includes(String(a.state))?(l="turn-on",c="turn_on"):(l="turn-off",c="turn_off");const h=r.featureMasks?.[c];if(h&&!so(a,h))return ao(t,e,n,i,"unsupported","power");const d=Qn(t,o,c);return d?{target:{entityId:i,name:eo(t,e,i),state:String(a.state||""),via:s},skipped:null,semantics:"power",nextEffect:l,command:{...d,data:{entity_id:i}}}:ao(t,e,n,i,"unsupported","power")}(t,e,i,s,n);if(io(e,i))return ao(t,e,n,i,"ha-disabled",o);if(no(t,e,i))return ao(t,e,n,i,"secure",o);const a=t?.states?.[i];if(!a)return ao(t,e,n,i,"missing",o);if("unavailable"===a.state)return ao(t,e,n,i,"unavailable",o);const r=function(t,e,i){const s=String(i?.state||""),n="closed"===s?{service:`open_${e}`,effect:"open",feature:1}:"open"===s?{service:`close_${e}`,effect:"close",feature:2}:"opening"===s||"closing"===s?{service:`stop_${e}`,effect:"stop",feature:8}:null;return n&&so(i,n.feature)&&Xn(t,e,n.service)?n:Xn(t,e,"toggle")?{service:"toggle",effect:"toggle"}:null}(t,o,a);if(!r)return ao(t,e,n,i,"unsupported",o);return{target:{entityId:i,name:eo(t,e,i),state:String(a.state||""),via:s},skipped:null,semantics:o,nextEffect:r.effect,command:{domain:o,service:r.service,data:{entity_id:i}}}}function lo(t,e){if("entity"===t.bindingKind&&t.bindingRef)return[t.bindingRef];return bn(e,t.entities.length?t.entities:t.allEntities||[])}function co(t){const e=t.skipped?.reason;return"missing"===e?"unavailable":e||"unsupported"}function ho(t,e){return{origin:t,kind:"none",semantics:null,targets:[],skippedTargets:[],noneReason:e,nextEffect:null,command:null}}function uo(t,e){return"cover"===t||"toggle"===t?"toggle":"more-info"===t||"run"===t||"info"===t?t:(null==t||""===t)&&"light"===e?"toggle":"info"}function po(t,e){return{origin:t,kind:"single",semantics:e.semantics,targets:e.target?[e.target]:[],skippedTargets:e.skipped?[e.skipped]:[],noneReason:e.command?null:co(e),nextEffect:e.nextEffect,command:e.command}}function _o(t){const{hass:e,device:i,devices:s}=t,n=t.registryHass||e,o=$n(i.marker?.binding,i.marker?.controls??i.controls,i.entities),a=t.lightSources||Rn(e,s),r=new Map;for(const t of a){if(!t.key.startsWith("marker:"))continue;const e=r.get(t.key)||[];e.push(t),r.set(t.key,e)}const l=function(t,e){const i=lo(t,e).find(gs)||null;return i?{entityId:i,via:"entity"===t.bindingKind?"binding":"device-role"}:null}(i,n),c=new Map;for(const t of s){const e=String(t.marker?.id||t.id||"");e&&c.set(e,t)}const h=new Map,d=[],u=new Set,p=t=>{const e=`${t.ref}\n${t.entityId||""}\n${t.reason}`;u.has(e)||(u.add(e),d.push(t))},_=(t,i,s)=>{const o=ro(e,n,t,i,s);o.target?h.set(t,h.get(t)||o.target):o.skipped&&p(o.skipped)};for(const t of o){if(!t.startsWith("marker:")){gs(t)?_(t,"control-entity",t):p(oo(e,n,t,t.includes(".")?t:null,"unsupported"));continue}const i=r.get(t)||[];if(!i.length){const i=c.get(t.slice(7));if("ha_disabled"===i?.bindingStatus?.kind){const s=i.bindingStatus.allEntityIds[0]||null;p({ref:t,entityId:s,name:i.name||(s?eo(e,n,s):null),reason:"ha-disabled"})}else p(oo(e,n,t,null,"missing"));continue}const s=[...new Set(i.flatMap(t=>t.serviceEids))];if(s.length){for(const e of s)_(e,"control-entity",t);continue}const o=i.some(t=>t.passive);o&&l&&gs(l.entityId)?_(l.entityId,"control-marker-driver",t):p(oo(e,n,t,null,o?"missing":"unsupported"))}const m=[...h.values()];if(!m.length)return{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:m,skippedTargets:d,noneReason:d.length&&d.every(t=>"secure"===t.reason)?"secure":"configured-targets-missing",nextEffect:null,command:null};const g=m.some(t=>"on"===t.state)?"turn_off":"turn_on",f=Qn(e,"homeassistant",g);return f?{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:m,skippedTargets:d,noneReason:null,nextEffect:"turn_on"===g?"turn-on":"turn-off",command:{...f,data:{entity_id:m.map(t=>t.entityId)}}}:{origin:"explicit-toggle",kind:"group",semantics:"group-power",targets:[],skippedTargets:[...d,...m.map(t=>oo(e,n,t.entityId,t.entityId,"unsupported"))],noneReason:"unsupported",nextEffect:null,command:null}}function mo(t){const{hass:e,device:i}=t,s=t.registryHass||e,n=function(t){return"toggle"===t.tapAction?"explicit-toggle":"cover"===t.tapAction?"legacy-cover":!t.tapAction&&t.primary?.startsWith("light.")?"default-light":null}(i);if(!n)return null;if("explicit-toggle"===n){if($n(i.marker?.binding,i.marker?.controls??i.controls,i.entities).length)return _o(t)}if("legacy-cover"===n){const t=i.entities.find(t=>t.startsWith("cover."))||i.allEntities?.find(t=>t.startsWith("cover."));return t?po(n,ro(e,s,t,"device-role")):ho(n,"no-actionable-entity")}const o=function(t,e,i){const s=lo(i,e);if(!s.length)return null;const n="entity"===i.bindingKind?"binding":"device-role";if("binding"===n)return ro(t,e,s[0],n);let o=null,a=null;for(const i of s){const s=ro(t,e,i,n);if(s.command)return s;const r=s.skipped?.reason;if("missing"===r||"unavailable"===r||"secure"===r)return s;"ha-disabled"!==r?o||=s:a||=s}return o||a}(e,s,i);return o?po(n,o):ho(n,i.virtual||"virtual"===i.bindingKind?"no-actionable-entity":"no-binding")}function go(t){if(!t)return[];const e=Array.isArray(t.data.entity_id)?t.data.entity_id:[t.data.entity_id];return[...new Set(e)].sort()}const fo={climate:["current_temperature","temperature","current_humidity","humidity"],water_heater:["current_temperature","temperature"],cover:["current_position"],valve:["current_position"],fan:["percentage"],humidifier:["current_humidity","humidity"],light:["brightness"],media_player:["volume_level"],vacuum:["battery_level","fan_speed"],lawn_mower:["battery_level","fan_speed"]};function vo(t){return t?"entity_state"===t.kind?`state:${t.entity_id}`:"entity_attribute"===t.kind?`attr:${t.entity_id}:${t.attribute}`:"derived_marker_state"===t.kind?`marker:${t.ref}`:"derived:lqi":""}function yo(t){if("derived:lqi"===t)return{kind:"derived_lqi"};if(t.startsWith("state:"))return{kind:"entity_state",entity_id:t.slice(6)};if(t.startsWith("attr:")){const e=t.slice(5),i=e.lastIndexOf(":");if(i>0)return{kind:"entity_attribute",entity_id:e.slice(0,i),attribute:e.slice(i+1)}}return t.startsWith("marker:marker:")?{kind:"derived_marker_state",ref:t.slice(7)}:null}function bo(t,e){const i=t?.entities?.[e],s=t?.states?.[e];return String(i?.name||i?.original_name||s?.attributes?.friendly_name||e)}function wo(t){return t.replaceAll("_"," ")}function ko(t,e=1){const i=Number(t);return Number.isFinite(i)?`${Math.round(i*e)} %`:null}function $o(t){if("derived_lqi"===t.kind)return"lqi";if("entity_attribute"===t.kind){if(t.attribute.includes("temperature"))return"temperature";if(t.attribute.includes("humidity"))return"humidity"}return"default"}function xo(t,e,i,s=[]){let n=null,o="",a="available";if("entity_state"===i.kind){o=bo(t,i.entity_id);const e=t?.states?.[i.entity_id];if(e)if(Mo(t,i.entity_id))if(["unknown","unavailable"].includes(String(e.state).toLowerCase()))a="unavailable";else{const s=qi(t,i.entity_id);n=s?Bi(s,String(e.attributes?.unit_of_measurement||"")):null,n||(a="unavailable")}else a="unavailable";else a="missing"}else if("entity_attribute"===i.kind){o=`${wo(i.attribute)} · ${bo(t,i.entity_id)}`;const e=t?.states?.[i.entity_id];e?Mo(t,i.entity_id)?(n=function(t,e,i){const s=t?.states?.[e];if(!s||!(i in(s.attributes||{})))return null;const n=qi(t,e,i);if(n?.formatted)return n.text;const o=s.attributes?.[i];if("brightness"===i)return ko(o,100/255);if("volume_level"===i)return ko(o,100);if(["current_position","percentage","current_humidity","humidity","battery_level"].includes(i))return ko(o);if("current_temperature"===i||"temperature"===i){const e=Number(o);if(!Number.isFinite(e))return null;const i=String(t?.config?.unit_system?.temperature||"°C");return`${Math.round(10*e)/10} ${i}`}return n?.text||(["string","number","boolean"].includes(typeof o)?String(o):null)}(t,i.entity_id,i.attribute),null==n&&(a="unavailable")):a="unavailable":a="missing"}else if("derived_lqi"===i.kind){o="LQI";const i=In(t,e.entities);null==i?a="unavailable":n=String(i)}else{o="Light state";const e=s.find(t=>t.ref===i.ref);if(e){const i=e.on?"on":"off",s=t?.localize?.(`state.default.${i}`);n="string"==typeof s&&s?s:i,o=e.name||o}else a="missing"}const r=t?.localize?.("state.default.unavailable")||"Unavailable";return{source:i,sourceLabel:o,text:n??"—",fullText:n??r,availability:a,isLqi:"derived_lqi"===i.kind,tone:$o(i)}}function So(t,e){if(!0===e.marker?.use_climate_temp){const i=e.entities.find(e=>e.startsWith("climate.")&&Number.isFinite(Number(t?.states?.[e]?.attributes?.current_temperature)));if(i)return{kind:"entity_attribute",entity_id:i,attribute:"current_temperature"}}if("mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon){const i=e.entities.find(e=>fn(t,e)&&Number.isFinite(Number(t?.states?.[e]?.state)));if(i)return{kind:"entity_state",entity_id:i}}return e.primary&&Nn(t,e.primary)&&Number.isFinite(Number(t?.states?.[e.primary]?.state))?{kind:"entity_state",entity_id:e.primary}:null}function Mo(t,e){const i=t?.entities?.[e];if(i&&!cn(i))return!1;const s=i?.device_id?t?.devices?.[i.device_id]:null;return!s||cn(s)}function Co(t,e){return Mo(t,e)&&!!t?.states?.[e]}function Do(t,e,i=[e]){const s=new Set(e.entities);for(const t of e.controls||[])t.startsWith("marker:")||s.add(t);const n=[],o=Rn(t,i),a=o.filter(t=>t.key.startsWith("marker:")).map(t=>({ref:t.key,on:t.on,name:t.device?.name||t.key})),r=i=>{const s=vo(i);if(n.some(t=>t.key===s))return;const o=xo(t,e,i,a),r="entity_attribute"===i.kind?`${i.entity_id} · ${i.attribute}`:"entity_state"===i.kind?i.entity_id:"derived_marker_state"===i.kind?i.ref:"LQI";n.push({key:s,source:i,label:o.sourceLabel,technical:r,value:o.text,available:"available"===o.availability})};for(const e of[...s].sort()){if(!Co(t,e))continue;const i=e.split(".")[0],s=t?.entities?.[e];if("button"===i||"event"===i||"config"===s?.entity_category)continue;r({kind:"entity_state",entity_id:e});const n=t?.states?.[e];for(const t of fo[i]||[])t in(n?.attributes||{})&&r({kind:"entity_attribute",entity_id:e,attribute:t})}const l=new Set((e.marker?.controls||e.controls||[]).filter(t=>t.startsWith("marker:")));!0===e.marker?.is_light&&e.marker.id&&l.add(`marker:${e.marker.id}`);const c=new Set(o.map(t=>t.key));for(const t of[...l].sort())c.has(t)&&r({kind:"derived_marker_state",ref:t});return e.virtual||null==In(t,e.entities)||r({kind:"derived_lqi"}),n}function To(t,e,i){const s=So(t,e);if(s&&i.some(t=>t.key===vo(s)))return s;const n=e.primary&&i.find(t=>t.key===`state:${e.primary}`);if(n)return n.source;const o=[t=>t.technical.includes("temperature"),t=>t.technical.includes("humidity"),t=>t.technical.includes("battery")];for(const t of o){const e=i.find(t);if(e)return e.source}return i.find(t=>"derived_lqi"!==t.source.kind)?.source||i[0]?.source||null}const Po={sourceKind:"none",visualSources:[],criticalSources:[],samples:[]};function Ro(t,e){const i=t?.entities?.[e],s=t?.states?.[e];return String(i?.name||i?.original_name||s?.attributes?.friendly_name||e)}function Ao(t,e){const i=t?.states?.[e];if(!i)return"";if("function"==typeof t?.formatEntityState)try{const e=t.formatEntityState(i);if("string"==typeof e&&e)return e}catch{}return String(i.state??"")}function Eo(t,e,i,s){return{eid:e,role:i,name:Ro(t,e),state:String(t?.states?.[e]?.state??""),stateText:Ao(t,e),integrationDomain:t?.entities?.[e]?.platform||null,sample:s||Gs(t,e)}}function Io(t,e,i=[e],s){const n=e.hidden&&e.userHidden?{...e,hidden:!1}:e;let o="none",a=[];const r=(l=mo({hass:t,devices:i,device:n,lightSources:s}),"cover"!==l?.semantics?null:l.targets[0]?.entityId||l.skippedTargets[0]?.entityId||null);var l;const c=Rn(t,[n]),h=$n(n.marker?.binding,n.marker?.controls??n.controls,n.entities),d=new Set(h.filter(t=>t.startsWith("marker:"))),u=!0===n.marker?.is_light||d.size>0?s||Rn(t,i):c,p=u.filter(t=>t.device.id===n.id&&"controls"!==t.via),_=c.filter(t=>"controls"!==t.via),m=[...c.filter(t=>"controls"===t.via),...p.length?p:_];if(d.size)for(const t of u){if(!d.has(t.key))continue;t.stateEids.length>0&&m.some(e=>e.stateEids.some(e=>t.stateEids.includes(e)))||m.some(e=>e.key===t.key)||m.push({...t,via:"controls",castsGlow:!1})}if(r)o="cover",a=[Eo(t,r,"cover")];else if(m.length)o=m.some(t=>"controls"===t.via)?"controls":"light",a=m.map(i=>{const s="controls"===i.via?"control":"forced"===i.via?"forced_light":"light";if(!i.passive)return Eo(t,i.eid,s);const n=i.on?"on":"off";return{eid:i.key,role:s,name:e.name,state:n,stateText:n,integrationDomain:null,sample:{eid:i.key,state:n,availability:"available",status:i.on?"working":"neutral",activity:"none",edge:"none"}}});else{const e=bn(t,n.entities),i=e.length?e:n.entities.filter(e=>!!t?.states?.[e]);if(i.length){o="device_role";const e=function(t,e,i){const s=e.map(e=>Gs(t,e)),n=i.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category);return 1===e.length&&n.length>1&&Ws(t,e[0])?s.map(t=>"unavailable"===t.availability?t:"off"===t.state?{...t,availability:"unavailable",status:"neutral",activity:"none",edge:"none"}:{...t,status:"neutral",activity:"none",edge:"none"}):s}(t,i,n.entities);a=i.map((i,s)=>Eo(t,i,"device_role",e[s]))}else n.primary&&(o="primary",a=[Eo(t,n.primary,"primary")])}const g=[];for(const e of n.entities||[]){const i=Gs(t,e);"alarm"!==i.status||a.some(t=>t.eid===e)||g.push(Eo(t,e,"critical",i))}return{sourceKind:o,visualSources:a,criticalSources:g,samples:[...a,...g].map(t=>t.sample)}}function zo(t,e,i,s){if(e.virtual)return{source:null,text:null,fallback:"value_virtual"};if(s){const i=function(t,e){if(!0!==e.marker?.use_climate_temp)return null;for(const i of e.entities){if(!i.startsWith("climate."))continue;const e=Number(t?.states?.[i]?.attributes?.current_temperature);if(Number.isFinite(e))return{eid:i,text:Math.round(10*e)/10+"°"}}return null}(t,e);if(i)return{source:{kind:"temperature",eid:i.eid,attribute:"current_temperature",text:i.text},text:i.text,fallback:null};const s=function(t,e){if("mdi:thermometer"!==e.icon&&"mdi:air-filter"!==e.icon)return null;for(const i of e.entities){if(!fn(t,i))continue;const e=Number(t?.states?.[i]?.state);if(Number.isFinite(e))return{eid:i,text:Math.round(10*e)/10+"°"}}return null}(t,e);if(s)return{source:{kind:"temperature",eid:s.eid,text:s.text},text:s.text,fallback:null};const n=function(t,e){if(!e.primary||!Nn(t,e.primary))return null;const i=Number(t?.states?.[e.primary]?.state);return Number.isFinite(i)?{eid:e.primary,text:`${Math.round(i)}%`}:null}(t,e);if(n)return{source:{kind:"humidity",eid:n.eid,text:n.text},text:n.text,fallback:null}}if(1!==i.visualSources.length)return{source:null,text:null,fallback:i.visualSources.length?"value_ambiguous_sources":"value_no_state"};const n=i.visualSources[0].eid,o=function(t,e){const i=t?.states?.[e];if(!i||null==i.state||""===String(i.state).trim())return{text:"",fallback:"value_no_state"};const s=i.state;if(!["string","number","boolean"].includes(typeof s))return{text:"",fallback:"value_non_scalar"};const n=String(s).trim().toLowerCase();if("unknown"===n||"unavailable"===n)return{text:"",fallback:"value_no_state"};const o=qi(t,e);return o?{text:Bi(o,String(i.attributes?.unit_of_measurement||"")),fallback:null}:{text:"",fallback:"value_no_state"}}(t,n);return o.fallback?{source:null,text:null,fallback:o.fallback}:{source:{kind:"entity",eid:n,text:o.text},text:o.text,fallback:null}}function Fo(t,e,i){const s=t.marker?.binding||(t.bindingKind&&t.bindingRef?`${t.bindingKind}:${t.bindingRef}`:t.virtual?"virtual":""),n=[...e.visualSources.map(t=>`${t.role}:${t.eid}`),...e.criticalSources.map(t=>`critical:${t.eid}`)].sort(),o=i?`${i.kind}:${i.eid}:${i.attribute||""}`:"none";return[s,e.sourceKind,...n,`value:${o}`].join("\n")}function No(t,e,i=!0,s){const n=s||Io(t,e);return Fo(e,n,zo(t,e,n,i).source)}function Oo(t){if(t.effectiveHidden)return[];if("static_icon"===t.display)return["static-icon"];const e=[],{visual:i}=t;return"alarm"===i.status?e.push("alarm"):"unavailable"===i.availability?e.push("unavail"):"working"===i.status?e.push("on"):"open"===i.status&&e.push("open"),"icon_ripple"===t.display&&"alarm"!==i.status&&"none"!==t.activity&&(e.push("activity-"+t.activity),"event"===t.activity&&t.activityGeneration%2==0&&e.push("activity-gen2")),e}function Lo(t,e,i){const s=Ri(e.marker?.display),n="static_icon"===s,o=n&&!1===i.sourceDetails?Po:Io(t,e,i.lightDevices||[e],i.lightSources),a=e.bindingStatus,r="ha_disabled"===a?.kind,l="orphaned"===a?.kind,c=!0===e.userHidden||!0===e.marker?.hidden,h=r||c&&!i.designPreview,d=function(t){if(!t.length)return{availability:"available",status:"neutral",activity:"none"};const e=t.filter(t=>"available"===t.availability);if(!e.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(e.some(t=>"alarm"===t.status))return Es;const i=e.some(t=>"working"===t.status)?"working":e.some(t=>"open"===t.status)?"open":"neutral",s=e.some(t=>"transition"===t.activity)?"transition":e.some(t=>"presence"===t.activity)?"presence":e.some(t=>"running"===t.activity)?"running":"none";return{availability:"available",status:i,activity:s}}(o.samples);let u=d;h||n?u={availability:"available",status:"neutral",activity:"none"}:"alarm"===d.status||i.liveStates||(u={availability:"available",status:"neutral",activity:"none"});const p=n&&!1===i.sourceDetails?{source:null,text:null,fallback:null}:zo(t,e,o,i.showTemperature),_=Fo(e,o,p.source),m=i.activityRuntime,g=i.now??Date.now();!n&&!h&&i.liveStates&&"alarm"!==d.status&&m?.sources===_&&m.flashTs&&m.flashKind&&g-m.flashTs<3300&&(u={...u,activity:m.flashKind});const f="icon_ripple"===s&&!h&&i.liveStates&&"alarm"!==u.status?u.activity:"none",v=n||h||!i.showTemperature?null:!0===e.marker?.use_climate_temp?Fn(t,e.entities):"mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon?zn(t,e.entities):null,y=!n&&!h&&i.showTemperature&&e.primary&&Nn(t,e.primary)?On(t,e.entities):null,b=n||h||!i.showSignal||e.virtual?null:In(t,e.entities),w=function(t,e,i){const s=null!=e.marker?.value_badge;if(i.effectiveHidden||"static_icon"===i.display)return null;const n=e.marker?.value_badge;if(s)return n?.enabled&&n.source?{configured:!0,enabled:!0,position:n.position||"right",...xo(t,e,n.source,i.markerStates)}:null;if(!i.showTemperature||"value"===i.display)return null;const o=So(t,e);if(!o)return null;let a=null,r="default";if("entity_attribute"===o.kind&&"current_temperature"===o.attribute){const i=Fn(t,e.entities);null!=i&&(a=`${i}°`),r="temperature"}else if("mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon){const i=zn(t,e.entities);null!=i&&(a=`${i}°`),r="temperature"}else{const i=On(t,e.entities);null!=i&&(a=`${i}%`),r="humidity"}return null==a?null:{configured:!1,enabled:!0,source:o,position:"right",sourceLabel:"entity_attribute"===o.kind?`${wo(o.attribute)} · ${bo(t,o.entity_id)}`:"entity_state"===o.kind?bo(t,o.entity_id):"",text:a,fullText:a,availability:"available",isLqi:!1,tone:r}}(t,e,{showTemperature:i.showTemperature,showSignal:i.showSignal,display:s,effectiveHidden:h,markerStates:o.visualSources.filter(t=>t.eid.startsWith("marker:")).map(t=>({ref:t.eid,on:"on"===t.state,name:t.name}))}),k="cover"===o.sourceKind?o.visualSources[0]?.eid:e.primary||o.visualSources[0]?.eid,$=k?t?.states?.[k]:null,x=!i.liveStates||n||h?e.icon:ls(e.icon,k?.split(".")[0],$?.attributes?.device_class,$?.state,!!e.marker?.icon),S=!i.liveStates||n||h?null:Rn(t,[{...e,hidden:!1}]).filter(t=>!!t.eid).map(e=>function(t){return t&&"on"===t.state?Rt(t.attributes?.rgb_color):null}(t?.states?.[e.eid])).find(t=>!!t)||null,M=Number(e.marker?.size)>0?Number(e.marker.size):1,C=Number(e.marker?.angle)||0,D=Number(e.marker?.ripple_size)>0?Number(e.marker.ripple_size):3,T=Dt(e.marker?.ripple_color,null),P=n?null:T||S||null,R="value"!==s||h?null:p.text,A="ha_disabled"===a?.kind?a.reason:null,E=function(t,e,i,s,n,o,a,r){return o?"ha_disabled":a?"orphaned":"static_icon"===r?"static_icon":"alarm"===e.status?"alarm":n?"unavailable"===e.availability?"unavailable":"cover"===s?"cover_icon_state":"presence"===i?"presence":"event"===i?"event":"transition"===i?"transition":"working"===e.status?"icon_ripple"===r&&"none"!==i?"working_activity":"working":"open"===e.status?"open":(t.primary||"").startsWith("media_player.")?"media_neutral":"neutral":"live_states_disabled"}(e,u,f,o.sourceKind,i.liveStates,r,l,s),I=[];i.designPreview&&c&&I.push("hidden_design_preview"),n||!0!==e.marker?.vacuum?.live||I.push("vacuum_live_plan_only");const z=1===o.visualSources.length&&Ws(t,o.visualSources[0].eid),F=e.entities.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category).length;z&&F>1&&I.push("composite_power_source"),"icon_ripple"!==s&&"static_icon"!==s&&"alarm"!==d.status&&"none"!==d.activity&&I.push("activity_display_disabled");const N={binding:e.marker?.binding||(e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:e.virtual?"virtual":""),sourceKind:o.sourceKind,visualSources:o.visualSources,criticalSources:o.criticalSources,valueSource:p.source,sourceSignature:_,visual:u,display:s,icon:x,valueText:R,valueFullText:R,fallbackReason:"value"!==s||R?null:p.fallback,activity:f,activityGeneration:m?.gen||1,classes:[],tempText:null==v?null:String(v),humText:null==y?null:String(y),valueBadge:w,lqiText:null==b||w?.isLqi?null:String(b),lqiColor:null==b?null:Xe(b),lightColor:S,scale:M,angle:C,rippleScale:D,rippleColor:P,userHidden:c,effectiveHidden:h,haDisabled:r,disabledReason:A,orphaned:l,vacuumLive:!n&&!0===e.marker?.vacuum?.live,explanation:{reason:E,notices:I}};return{...N,classes:Oo(N)}}const Ho=new WeakMap;function qo(t){const e=function(t){const e=t?.connection||t;return!e||"object"!=typeof e&&"function"!=typeof e?null:e}(t);if(!e)return null;let i=Ho.get(e);return i||(i={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},Ho.set(e,i)),i}function Bo(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function Uo(t,e){const i=qo(t);if(!i)return()=>{};i.refs++,i.listeners.add(e),async function(t,e){e.loading||"function"!=typeof t?.callWS?e.loading:e.loaded&&e.loadedAt&&Date.now()-e.loadedAt<3e5||(e.loading=(async()=>{const[i,s]=await Promise.allSettled([t.callWS({type:"config_entries/get"}),t.callWS({type:"manifest/list"})]);"fulfilled"===i.status&&Array.isArray(i.value)&&(e.configEntries=Object.fromEntries(i.value.filter(t=>"string"==typeof t?.entry_id).map(t=>[t.entry_id,t]))),"fulfilled"===s.status&&Array.isArray(s.value)&&(e.manifests=Object.fromEntries(s.value.filter(t=>"string"==typeof t?.domain).map(t=>[t.domain,t]))),e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Bo(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Bo(e)}),e.loading)}(t,i),async function(t,e){if(e.unsubscribe||e.subscribing)return e.subscribing;const i=t?.connection?.subscribeMessage;"function"==typeof i&&(e.subscribing=(async()=>{try{e.unsubscribe=await i.call(t.connection,t=>{const i=Array.isArray(t)?t:Array.isArray(t?.entries)?t.entries:t?[t]:[];let s=!1;const n={...e.configEntries};for(const t of i){const e=t?.entry||t?.data?.entry||("string"==typeof t?.entry_id?t:null),i=e?.entry_id;if("string"!=typeof i)continue;const o=t?.type||t?.action||t?.data?.action;"removed"===o||"remove"===o?delete n[i]:n[i]=e,s=!0}s&&(e.configEntries=n,Bo(e))},{type:"config_entries/subscribe"})}catch{}finally{0===e.refs&&(e.unsubscribe?.(),e.unsubscribe=void 0),e.subscribing=void 0}})(),e.subscribing)}(t,i);let s=!1;return()=>{s||(s=!0,i.listeners.delete(e),i.refs=Math.max(0,i.refs-1),i.refs||(i.unsubscribe?.(),i.unsubscribe=void 0))}}function Wo(t){const e=qo(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function jo(t,e,i=Wo(t),s){const n=i.manifests[e];return function(t,e){if("function"!=typeof t?.localize)return"";try{const i=`component.${e}.title`,s=t.localize(i);return"string"==typeof s&&s!==i?s:""}catch{return""}}(t,e)||String(n?.name||"")||String(s||"")||e.replace(/_/g," ")}function Go(t,e,i,s,n){const o=String(e||"").trim();if(!o)return null;const a=n?s.configEntries[n]:null;return{domain:o,label:jo(t,o,s,a?.title),configEntryId:n,configEntryTitle:a?.title,confidence:i}}function Vo(t){const e={"registry-owner":0,"entity-platform":1,"identifier-fallback":2},i=new Set;return t.filter(t=>!!t).sort((t,i)=>e[t.confidence]-e[i.confidence]||t.label.localeCompare(i.label)||t.domain.localeCompare(i.domain)).filter(t=>{const e=`${t.domain}\n${t.configEntryId||""}`;return!i.has(e)&&(i.add(e),!0)})}function Ko(t,e,i,s=Wo(t)){const n=i.entities?.[e]||t?.entities?.[e];if(!n)return null;const o=n.config_entry_id||void 0,a=o?s.configEntries[o]?.domain:"";return Go(t,n.platform||a,"entity-platform",s,o)}var Yo={"editor.context_actions":"Actions: {object}","editor.tool_options":"Tool options: {tool}","editor.palette":"Palette: {tool}","editor.open_group":"Tool group: {group}","editor.group_active":"{group} — active: {item}","editor.disabled_action":"{action} is unavailable: {reason}","btn.properties":"Properties","btn.keep_as_walls":"Keep as closed walls","title.markup_partition":"Partition: draw one independent physical wall","title.markup_select":"Select and edit walls, columns and saved outlines","title.markup_column":"Column: click a grid point to place a square column","markup.partition":"Partition","markup.column":"Column","markup.hint_partition":"two clicks draw one partition · Shift locks to 45°","markup.hint_column":"click a grid point to place a square column","history.draft_segment":"Add room-draft segment","history.draft_merge":"Join unfinished room outlines","history.draft_segment_delete":"Remove room-draft segment","history.partition_add":"Add partition","history.column_add":"Add column","history.physical_edit":"Change physical object","history.physical_delete":"Delete physical object","history.physical_move":"Move physical object","history.contour_to_partitions":"Convert contour to closed walls","toast.column_duplicate":"A column with the same centre and outer size already exists","confirm.delete_draft":"Delete the whole unfinished room contour?","confirm.delete_draft_segment":"Delete this draft segment? The remaining contour may split in two.","physical.partition_properties":"Partition properties","physical.column_properties":"Column properties","physical.draft_properties":"Draft segment properties","physical.shape":"Shape","physical.square":"Square","physical.circle":"Circle","physical.diameter":"Diameter","physical.side":"Side","physical.rotation":"Rotation angle","physical.length":"Length","physical.allowed_range":"Allowed: 1–{max} {unit}","physical.delete_segment":"Delete segment","physical.delete_draft":"Delete entire outline","physical.partition_size_title":"Thickness of the partition created by the second click (1–100 cm).","physical.column_size_title":"Outer side of the square column placed by the click (1–150 cm).","card.title":"House plan","count.devices":"{n} dev.","empty.no_spaces":"No spaces yet.","empty.add_first":"Add the first space and upload a floor plan.","empty.install":'Install the House Plan integration and add it in "Devices & services".',"btn.add_space":"Add space","btn.cancel":"Cancel","btn.save":"Save","btn.close":"Close","btn.delete":"Delete","btn.edit":"Edit","btn.open_in_ha":"Open in HA","btn.reset":"Reset","btn.attach":"Attach…","btn.upload":"Upload…","btn.replace":"Replace…","title.zoom_in":"Zoom in","title.zoom_out":"Zoom out","title.zoom_fit":"Fit all","title.add_device":"Add a device to the plan","title.show_all":"Show hidden and disabled devices as service markers (this tab only)","title.markup":"Room markup: grid, lines, outlines","title.configure_space":"Configure space","title.add_space":"Add space","title.markup_add":"Room outline: connect grid dots until the room closes; Shift locks the direction in 45° steps","title.markup_merge":"Merge: click one room, then the neighbour it shares a wall with","title.markup_split":"Split a room: click the room, then two points on its walls","title.markup_delroom":"Delete room: click inside a room and confirm","title.markup_boundary":"Boundary: click two points on a solid shared wall to make that stretch virtual; click a dashed stretch to restore it","title.need_plan":"Upload a floor-plan image","markup.add":"Room outline","markup.merge":"Merge","markup.split":"Split","markup.resize":"Resize","title.markup_resize":"Resize rooms: drag a wall handle; click a room for a corner scale frame","markup.hint_resize":"drag a wall handle · click a room — corner frame · Esc cancels a drag · Ctrl+Z — undo a step","markup.opening":"Opening","markup.boundary":"Boundary","markup.delete_room":"Delete room","history.undo":"Undo","history.redo":"Redo","history.undo_named":"Undo: {name} (Ctrl+Z)","history.redo_named":"Redo: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Nothing to undo","history.redo_empty":"Nothing to redo","history.undone":"Undone: {name}","history.redone":"Redone: {name}","history.add_room":"Create room","history.split_room":"Split room","history.merge_rooms":"Merge rooms","history.resize_room":"Resize room","history.open_boundary":"Open boundary","history.close_boundary":"Restore wall","history.wall_thickness":"Change wall thickness","history.add_opening":"Add door or window opening","history.edit_opening":"Edit opening","history.move_opening":"Move opening","history.delete_opening":"Delete opening","history.delete_room":"Delete room","history.decor_add":"Add decor object","history.decor_edit":"Edit decor object","history.decor_move":"Move decor object","history.decor_transform":"Transform decor object","history.decor_delete":"Delete decor object","history.backdrop_transform":"Transform plan backdrop","title.markup_opening":"Openings: click a wall to place, click an opening to edit","opening.new":"New opening","opening.edit":"Opening","opening.door":"Door","opening.window":"Window","opening.gate":"Gate","opening.type_label":"Type","opening.length_label":"Length, cm","opening.contact_label":"Open/close sensor","opening.lock_label":"Lock","opening.none":"— none —","opening.invert":"Invert open/closed","opening.flip_h":"Hinge on the other jamb","opening.flip_v":"Opens to the other side","opening.open":"Open","opening.closed":"Closed","opening.locked":"Locked","opening.unlocked":"Unlocked","opening.state_unknown":"unavailable","opening.no_entities":"No sensors bound — a static symbol on the plan.","toast.opening_no_wall":"Click next to a room wall — openings sit on walls","markup.delete":"Delete","markup.hint_points":"points: {n} · Shift — 45° steps · Esc/Ctrl+Z — undo a dot · first point or Ctrl/Cmd+click — close","markup.hint_start":"click a grid dot to start the outline","tip.lqi":"average zigbee signal:","tip.area":"area: {value}","info.device_header":"Device on the plan","info.model":"Model","info.state":"State","info.link":"Link","info.manuals":"Manuals","info.none":"No additional information","marker.new_device":"New device","marker.name_label":"Name (shown on the plan)","marker.name_ph":"Name","marker.binding_label":"Bind to an HA device","marker.binding_disabled":"disabled in Home Assistant","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 + dynamic plate","display.icon_ripple":"Icon + activity","display.static_icon":"Always static icon","marker.display_hint":"The dynamic plate shows work, open, alarm and unavailable states. Icon + activity also shows events and motion. Value instead of an icon shows one unambiguous HA state. A static icon does not react to device states.","marker.static_alarm_warning":"This device can report alarms. Static display hides the marker's visual alarm indication.","marker.preview.title":"Display preview","marker.preview.actual":"Now","marker.preview.example":"Example","marker.preview.integration":"Provided by","marker.preview.source":"Display source","marker.preview.current_state":"Current state","marker.preview.result":"On the plan","marker.preview.details":"Source details","marker.preview.select_source":"Choose a Home Assistant device or entity to see its actual display.","marker.preview.unknown_provider":"Unknown integration","marker.preview.virtual_provider":"House Plan · virtual device","marker.preview.no_source":"No active source","marker.preview.no_state":"No current state","marker.preview.mixed_states":"Several different states","marker.preview.multiple_sources":"{n} sources","marker.preview.more_sources":"+{n} more","marker.preview.scaled":"The preview is scaled to {n}% to fit. The saved size is unchanged.","marker.preview.demo_activity":"Show activity example","marker.preview.demo_notice":"Temporary activity example; the actual device state is unchanged.","marker.preview.demo_already_visible":"The current state already shows a real activity or alarm effect.","marker.preview.reduced_motion":"System reduced-motion is enabled, so the effect is shown as a static ring.","marker.preview.reason.neutral":"Neutral dark plate","marker.preview.reason.working":"Yellow plate: the device is working now","marker.preview.reason.working_activity":"Yellow plate and activity effect: the device is working now","marker.preview.reason.open":"Orange plate: physically open or unlocked","marker.preview.reason.cover_icon_state":"Neutral plate; the cover entity controls the displayed state and icon","marker.preview.reason.presence":"Activity effect while presence is detected","marker.preview.reason.event":"Short activity effect after a detected event","marker.preview.reason.transition":"Activity effect while the device is moving or changing state","marker.preview.reason.media_neutral":"Media devices stay on a neutral dark plate while available","marker.preview.reason.unavailable":"Subdued neutral plate: unavailable or off","marker.preview.reason.alarm":"Red alarm plate; alarm indication is always shown","marker.preview.reason.live_states_disabled":"Live-state styling is disabled for this card","marker.preview.reason.value_no_state":"No usable state value; the icon is shown instead","marker.preview.reason.value_ambiguous_sources":"Several possible value sources; the icon is shown instead","marker.preview.reason.value_non_scalar":"The source did not return a simple value; the icon is shown instead","marker.preview.reason.value_virtual":"A virtual device has no Home Assistant value; the icon is shown instead","marker.preview.reason.vacuum_live_plan_only":"Live vacuum position and trail are available only on the full plan","marker.preview.reason.hidden_design_preview":"The device is hidden on the plan; this design preview remains visible","marker.preview.reason.composite_power_source":"State comes from the device Power entity; auxiliary switches are ignored","marker.preview.reason.activity_display_disabled":"The selected display mode does not show ordinary activity effects","marker.preview.reason.ha_disabled":"The Home Assistant binding is disabled and will be hidden on the plan","marker.preview.reason.orphaned":"The saved Home Assistant binding can no longer be found","marker.preview.reason.static_icon":"Static mode: device state does not change the icon","marker.activity_color":"Effect color","marker.ripple_size":"Effect size","marker.size_label":"Icon size / rotation","marker.angle_label":"Rotate","marker.model_label":"Model","marker.model_ph":"e.g. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Description","marker.desc_ph":"Notes, specs…","marker.manuals_label":"Manuals (PDF etc.)","marker.sub_device":"device","marker.sub_z2m_group":" · Z2M group","marker.sub_group":"group","marker.sub_helper":"helper","space.new":"New space","space.header":"Space","space.title_label":"Title","space.title_ph":"e.g. Garage","space.plan_label":"Floor plan (background)","space.no_plan":"no plan image","space.plan_alt":"plan","room.new":"New room","room.name_label":"Display name","room.name_ph":"e.g. Terrace","room.area_label":"Home Assistant area (unassigned)","room.no_area_option":"— no area —","room.default_name":"Room","device.unnamed":"unnamed","device.light_group":"light group","device.fallback":"device","device.virtual":"virtual device","confirm.delete_room":'Delete room "{name}"?',"confirm.merge_rooms":'Merge rooms "{a}" and "{b}"?',"confirm.remove_marker":'Delete "{name}" from the plan? The device will disappear completely and stop contributing to plan data. You can add it again later.',"confirm.erase_decor":"Erase the {kind} object? You can undo this action from the editor history.","confirm.delete_space":'Delete space "{title}" with all its rooms and markup?',"toast.pos_save_failed":"Failed to save position: {err}","toast.no_entity":"The device has no suitable entity","toast.ha_disabled_action":"A Home Assistant object that is disabled cannot be used on the plan.","toast.ha_disabled_show_device":"A device disabled in Home Assistant cannot be shown on the plan. Enable it in Home Assistant first.","toast.ha_disabled_show_entity":"An entity disabled in Home Assistant cannot be shown on the plan. Enable it in Home Assistant first.","toast.ha_disabled_add":"A disabled Home Assistant object cannot be added to the plan. Enable it in Home Assistant first.","toast.ha_binding_unverified":"The object status could not be verified through the Home Assistant registry. Display and actions are temporarily unavailable.","toast.markup_needs_server":"Markup is available after the config is moved to the server","toast.conflict":"Config was changed in another window — data refreshed, repeat your last action","toast.cfg_save_failed":"Failed to save config: {err}","toast.room_overlap":"The outline overlaps room “{name}” — rooms must not overlap","toast.contour_min_edges":"Draw at least two edges before closing the outline","toast.contour_cannot_close":"The outline cannot close because it is degenerate or intersects itself","toast.merge_not_adjacent":"Only rooms that share a wall can be merged","toast.rooms_merged":"Rooms merged into “{name}”","toast.split_pick_wall":"Start the cut on the room’s wall","toast.split_bad_cut":"The cut must run wall to wall inside the room, without crossing walls or itself","merge.header":"Merge rooms","merge.hint":"The merged room keeps one name and one area. The other area is released — its devices leave the plan until another room claims it.","merge.keep":"Keep","merge.no_area":"no area","room.split_header":"New room from the split","toast.room_saved":"Room saved ({n}). Devices added: {added}. Outline the next one or exit markup.","toast.room_saved_no_area":"Room saved ({n}, no area). Outline the next one or exit markup.","toast.marker_needs_server":"Device editing is available after the config is moved to the server","toast.virtual_name_required":"Enter a name for the virtual device","toast.marker_saved":"Device saved","toast.marker_removed":"Device deleted from the plan","toast.integration_missing":"The House Plan integration is not installed — management unavailable","toast.plan_formats":"Supported formats: SVG, PNG, JPG, WebP","toast.plan_required":"Upload a floor plan — it is required","toast.space_added_onboard":"Space added. Outline the rooms: click grid dots and close the contour.","toast.space_added":"Space added","toast.space_saved":"Space saved","toast.space_deleted":"Space deleted","toast.delete_failed":"Delete failed: {err}","toast.error":"Error: {err}","toast.file_failed":'File "{name}" was not uploaded: {err}',"toast.files_attached":"Files attached: {n}","err.unknown":"unknown error","err.code":"code {code}","err.too_large":"file larger than {mb} MB","err.bad_ext":"unsupported type (PDF/image expected)","err.unauthorized":"administrator rights required","editor.title":"Title","editor.default_floor":"Default space","editor.icon_size":"Icon size, % of plan width","editor.show_temperature":"Show temperature","editor.live_states":"Live states (on/off, open…)","editor.show_signal":"Show zigbee signal (LQI)","editor.language":"Interface language","editor.lang_auto":"Auto (HA profile)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Icon rules: which MDI icon devices get by name","rules.title":"Icon rules","rules.hint":"Rules are checked top-down against “device name + model” (case-insensitive regex); the first match wins. When nothing matches, the entity device class decides, then the generic chip icon.","rules.pattern_ph":"regex, e.g. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Add rule","rules.reset":"Reset to defaults","rules.test_ph":"Try a device name…","rules.invalid":"invalid regex","rules.saved":"Icon rules saved","btn.up":"Up","btn.down":"Down","tap.info":"Device card","tap.more_info":"HA more-info dialog","tap.toggle":"Toggle state","marker.tap_label":"Tap action for this device","tap.toggle_note":"The resolved state is shown below. Secure devices are never toggled from the plan.","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","fill.custom":"Custom color","space.custom_fill":"Fill color","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…","continuity.restore_plan":"Restoring floor plan…","continuity.restore_connection":"Restoring device connection…","continuity.retry":"Try again","editor.space":"Space","editor.show_button":"Show button","editor.button_label":"Button label","editor.button_target":"Target dashboard path","marker.sub_entity":"entity","title.general_settings":"General settings","gs.title":"General settings","gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.light_group":"Fill: lights","gs.light_on":"Lights on","gs.light_off":"All lights off","gs.temp_group":"Fill: temperature","gs.temp_cold":"Cold","gs.temp_ok":"Comfortable","gs.temp_hot":"Hot","gs.lqi_group":"Fill: zigbee signal","gs.lqi_low":"Weak signal","gs.lqi_high":"Strong signal","gs.reset":"Reset to defaults","gs.saved":"General settings saved","space.show_lqi":"Show zigbee signal (LQI) next to devices","space.hide_decor":"Hide the decorative layer","space.hide_decor_tip":"Lines, shapes, labels and furniture stay where they are — visible in the backdrop editor, not on the plan.","space.hide_openings":"Hide openings","space.hide_openings_tip":"Door, window and gate 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":"Hidden and disabled","devbar.rules":"Icon rules","space.roomcard_section":"Room card shows:","space.label_temp":"Temperature","space.label_hum":"Humidity","space.label_lqi":"Average Zigbee signal","space.label_light":"Lights on/off","roomcard.light_on":"On","roomcard.light_off":"Off","roomcard.light_partial":"{on} of {total}","toast.split_pick_inside":"Intermediate cut points must be inside the room","mode.decor":"Background editor","decor.select":"Select","decor.line":"Line","decor.rect":"Rectangle","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Erase","decor.erase_confirm_title":"Erase object?","decor.color":"Color","decor.width":"Line width","decor.line_style":"Line style","decor.line_style_solid":"Solid","decor.line_style_dashed":"Dashed","decor.fill":"Fill","decor.fill_color":"Fill color","decor.length":"Length","decor.size":"Size","decor.angle":"Rotation angle, °","decor.text_size":"Text size","decor.backdrop_properties":"Plan backdrop properties","decor.text_title":"Text label","decor.object_title":"Edit {kind}","decor.text_label":"Text","decor.live_group":"Insert HA variable","decor.live_entity":"Entity","decor.live_entity_ph":"choose an entity","decor.live_attr":"Value","decor.live_attr_ph":"choose state or attribute","decor.live_state":"State","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag to move; pull a corner to resize; use the top handle to rotate. Shift changes proportions or frees the angle.","decor.backdrop_reset":"Reset the picture","decor.backdrop_reset_done":"The picture is back at its original place and size","marker.icon_auto":"Auto: {icon} (by icon rules; pick one to override)","marker.icon_pin_auto":"Pin","mode.plan_tip":"Plan editor — the geometry of the home: draw and split/merge rooms, bind them to HA areas, place doors, windows and gates, 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","space.glow_enabled":"Light-source glow","gs.glow_group":"Light-source glow","gs.glow_base":"House darkness","gs.glow_light":"Default light color / intensity","gs.wall_group":"Walls","gs.wall_fill":"Wall fill","gs.glow_radius":"Glow radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Controls other light sources","marker.controls_hint":"With tap action “Toggle”, a click flips every source added here. The marker’s own bound entity is controlled directly and cannot be added; use the switch below to classify it as a light.","marker.controls_filter":"Search lights and switches…","info.controls":"Controls","marker.glow_radius_label":"Glow radius","marker.glow_radius.help":"Sets the glow radius in metres or feet; an empty value uses the radius from general settings.","marker.glow_radius.help.aria":"Help: glow radius","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set how thick it is. Empty or zero removes the thickness.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new room-wall segment (1–100 cm). Shared walls keep the neighbour's value.","toast.wallthick_pick":"Click a wall (not an open boundary)","toast.wallthick_open":"Open boundaries have no thickness","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from 1 to {max} {unit}","toast.physical_angle":"Enter a rotation angle from 0° up to, but not including, 90°","toast.physical_limit":"The space has reached the limit for this type of geometry","markup.boundary_hint":"Select a shared room boundary or a dashed stretch","markup.boundary_hint_open":"Click the start of the open stretch","markup.boundary_hint_second":"Choose the end on the selected boundary · Esc to cancel","markup.boundary_hint_retry":"Choose the end on the selected boundary","markup.boundary_hint_restore":"Click to restore the wall","markup.boundary_hint_outer":"Only a boundary shared by two rooms can be opened","markup.boundary_hint_blocked":"a partition, column or unfinished contour is above this boundary","markup.boundary_hint_ambiguous":"Choose a stretch farther from the junction","markup.boundary_cancel":"Cancel the unfinished Boundary action","toast.openwall_pick":"This tool works with room boundaries","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.boundary_opened":"Boundary stretch opened","toast.boundary_restored":"Wall restored on this stretch","toast.boundary_same_edge":"Choose the end point on the selected wall","toast.boundary_ambiguous":"Choose a stretch slightly farther from the junction","toast.boundary_blocked":"This boundary is covered by independent masonry. Move or delete it with Select first","toast.delete_room_pick":"Click inside the room you want to delete","toast.openwall_openings_removed":"Openings on the virtual stretch were removed","toast.openwall_opened":"Boundary “{a}” ↔ “{b}” is now open","toast.openwall_closed":"Boundary “{a}” ↔ “{b}” is closed again","toast.opening_on_virtual":"Openings cannot sit on a virtual wall","marker.from_ha_option":"Pick from the HA list","marker.show_entities":"Show entities","marker.show_entities_tip":"Adds not only devices to the list, but all their entities too","marker.pick_ph":"Choose a device…","room.open_area":"Open the HA area","kiosk.title":"This screen's sizes","kiosk.hint":"Stored on this device only — every wall tablet or TV can have its own comfortable sizes.","kiosk.icon_scale":"Device icon size","kiosk.font_scale":"Room card text size","editor.kiosk":"Wall device (kiosk) mode","editor.cycle":"Auto-switch spaces every N seconds (kiosk, 0 = off)","room.settings_title":"Room settings","room.settings_section":"Room settings (override the space)","room.fill_label":"Fill in THIS room","fill.inherit":"As the space","room.custom_fill_space":"Space color","room.custom_fill_own":"Room color","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":"Include the device temperature in the room","marker.use_climate_temp_tip":"Adds an air conditioner or thermostat current_temperature to the room average. Configure the external value badge separately below.","marker.value_badge_title":"Value badge","marker.value_badge.help":"Shows one selected value next to the icon. It does not affect room metrics, light, Glow or the tap action.","marker.value_badge.help.aria":"Help: device value badge","marker.value_badge_enabled":"Show a value badge","marker.value_badge_source":"Value","marker.value_badge_source.help":"Choose a specific state, supported attribute, or derived value of this device.","marker.value_badge_source.help.aria":"Help: value badge source","marker.value_badge_position":"Position","marker.value_badge_position.help":"The chosen side stays fixed and does not flip automatically at a plan edge.","marker.value_badge_position.help.aria":"Help: value badge position","marker.value_badge_right":"Right","marker.value_badge_bottom":"Below","marker.value_badge_left":"Left","marker.value_badge_top":"Above","marker.value_badge_empty":"This device has no available values","marker.value_badge_static":"A static icon does not show live values. The setting is preserved.","marker.value_badge_missing":"Source unavailable","marker.value_badge_missing_hint":"The saved source is currently unavailable. Keep it, disable the badge, or choose another value.","marker.value_badge_duplicate":"The same value is already shown inside the icon.","marker.value_badge_state":"State · {name}","marker.value_badge_marker_state":"Light-source state · {name}","marker.value_badge_lqi":"Average Zigbee signal quality","marker.value_badge_attr_current_temperature":"Current temperature · {name}","marker.value_badge_attr_temperature":"Target temperature · {name}","marker.value_badge_attr_current_humidity":"Current humidity · {name}","marker.value_badge_attr_humidity":"Humidity · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Speed · {name}","marker.value_badge_attr_brightness":"Brightness · {name}","marker.value_badge_attr_volume_level":"Volume · {name}","marker.value_badge_attr_battery_level":"Battery · {name}","marker.value_badge_attr_fan_speed":"Fan speed · {name}","marker.light_role_label":"Is this device a light source?","marker.light_role.help":"Controls whether this marker itself is a spatial light source. Linked lights above remain independent room-light controls.","marker.light_role.help.aria":"Help: whether this device is a light source","marker.light_role_auto_yes":"Auto (light source)","marker.light_role_auto_no":"Auto (not a light source)","marker.light_role_always":"Always a light source","marker.light_role_never":"Never a light source","marker.light_entity_label":"Leading light entity","marker.light_entity.help":"For a composite device, choose the entity whose state, colour and brightness represent this plan source. The automatic fallback remains compatible with older plans.","marker.light_entity.help.aria":"Help: leading light entity","marker.light_entity_auto":"Automatic ({entity})","marker.light_entity_none":"no controllable entity","marker.light_entity_missing":"The saved entity {entity} is unavailable. House Plan temporarily uses {fallback}; the saved choice will be restored if it returns.","marker.glow_color_label":"Glow colour and brightness","marker.glow_mode.help":"Use live source values, override only its colour, or fix both colour and brightness. The minimum is 1%; to disable the source, choose “Never”.","marker.glow_mode.help.aria":"Help: glow colour and brightness","marker.glow_mode_auto":"From source","marker.glow_mode_color":"Set colour","marker.glow_mode_fixed":"Set colour and brightness","marker.glow_color":"Glow colour","marker.glow_brightness":"Brightness","marker.glow_disabled_never":"Glow settings are unavailable because this marker is explicitly not a light source.","marker.glow_disabled_auto":"Auto mode found no spatial light source for this marker.","marker.glow_disabled_no_entity":"No active controllable Home Assistant entity is available for this spatial source.","marker.glow_passive_hint":"This source has no own Home Assistant data. Set its colour and brightness manually; its radius remains available.","marker.control_broken":"Saved source is missing or no longer marked as a light","marker.control_passive":"passive source","toast.marker_control_cycle":"This link would create a circular chain of light controls.","toast.marker_binding_required":"Choose a Home Assistant device before linking another light source.","confirm.unlock":"Unlock “{name}”?","toast.files_migrate_failed":"Attachments could not be moved to the new binding, links keep pointing at the old files: {err}","space.pick_saved":"Already uploaded","space.pick_saved_hint":"Plans stored on the server, including ones you detached earlier","space.no_saved":"No plans stored on the server yet.","space.loading":"Loading…","space.used_by":"in use: {list}","space.in_use":"A space still uses this plan — detach it first","btn.use":"Use","confirm.delete_plan":'Delete the plan file "{name}" from the server? This cannot be undone.',"toast.plans_list_failed":"Could not list the stored plans: {err}","toast.plan_delete_failed":"Could not delete the plan: {err}","marker.hide":"Hide","marker.hide_tip":'The device will disappear from the plan after saving but will still count toward the room signal. Restore it through "Hidden and disabled" in the device editor.',"marker.show":"Show","marker.show_tip":"The device will appear on the plan again after saving.","marker.hidden_ghost":"Device hidden by the user","marker.ha_disabled_device":"The device is disabled in Home Assistant and hidden from the plan.","marker.ha_disabled_entity":"The entity is disabled in Home Assistant and hidden from the plan.","marker.ha_disabled_all_entities":"The device has no active Home Assistant entities, so it is hidden from the plan.","marker.ha_registry_limited":"The full Home Assistant registry is unavailable to this user. The unverified object cannot be shown or used for now.","marker.delete_tip":"Completely delete the device from the plan and every aggregate. You can add it again later.","tap.run":"Run automation/script/scene","tap.cover":"Open/close (curtains/blinds)","marker.run_target_label":"What to run","marker.run_search_ph":"Search: automation, script or scene…","marker.run_target_gone":"Target {id} not found — pick again","marker.tap_confirm":"Ask for confirmation","marker.tap_confirm_tip":"Show a confirmation dialog before acting — a guard against accidental taps.","marker.toggle_hint_single":"Target: {name} ({id}).","marker.toggle_hint_group":"Will toggle {count} source(s): {names}.","marker.toggle_hint_current":"Now: {state} → {effect}.","marker.toggle_hint_group_current":"Currently on: {on} of {count} → {effect}.","marker.toggle_hint_skipped":"Will be skipped ({count}): {targets}.","marker.toggle_effect_turn_on":"will turn on","marker.toggle_effect_turn_off":"will turn off","marker.toggle_effect_open":"will open","marker.toggle_effect_close":"will close","marker.toggle_effect_stop":"will stop","marker.toggle_effect_toggle":"state will toggle","marker.toggle_skip_missing":"not found","marker.toggle_skip_ha_disabled":"disabled in HA","marker.toggle_skip_unavailable":"unavailable","marker.toggle_skip_unsupported":"toggle is unsupported","marker.toggle_skip_secure":"blocked for security","marker.toggle_none_no_binding":"This device has no binding, so there is no state to toggle. Tapping will do nothing.","marker.toggle_none_no_actionable_entity":"This device has no state that can be toggled. Tapping will do nothing.","marker.toggle_none_configured_targets_missing":"The configured targets are unavailable. The device's own entity will not be substituted for them.","marker.toggle_none_ha_disabled":"The target is disabled in Home Assistant and cannot be used on the plan.","marker.toggle_none_unavailable":"The target is currently unavailable. Tapping will do nothing.","marker.toggle_none_unsupported":"Home Assistant exposes no safe toggle operation for this entity. Tapping will do nothing.","marker.toggle_none_secure":"Toggling locks, alarms and secure gates from the plan is blocked for security.","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","toast.tap_target_changed":"The action target changed. Try again.","toast.value_badge_source_required":"Choose a value for the badge","btn.run":"Run","vac.section":"Robot vacuum: live position","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_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.about_group":"About","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · docs & issues","gs.about_telegram":"Telegram chat","space.bg_color":"Background around the plan","space.bg_inherit":"Inherit general","space.bg_inherited":"inherits general settings","space.bg_mode":"Plan background","space.north":"North on the plan (override)","space.north_inherited":"inherited: {v}","space.sun_rays":"Sunlight through windows","space.sun_inherit":"Inherit general","space.sun_on":"On","space.sun_off":"Off","canvas.far_objects":"{n} object(s) far from the plan","canvas.show_far":"Show","canvas.home_tip":"The plan is over there — click to fit it","gs.grid_group":"Plan maintenance","gs.grid_hint":"Updates data models, aligns plan elements to the grid and merges redundant wall fragments. An exact report is shown before anything is stored.","gs.align_all":"Optimize plans","gs.align_title":"Optimize plans","gs.align_none":"All plans already use the current optimized data model.","gs.align_count":"{n} of {total} elements will move, by at most {cm} cm.","gs.align_where":"The largest shift is in “{s}”.","gs.align_turned":"Openings whose angle is corrected: {n}.","gs.align_removed_drafts":"Invalid outlines collapsed by the grid and removed: {n}.","gs.optimize_changes":"Model migrations: {m}; plans canonicalized: {c}; merged real-wall fragments: {w}; virtual fragments: {s}.","gs.optimize_glow_migration":"Legacy Glow: {spaces} spaces → no data fill + independent Glow; {rooms} rooms → inherited data fill + independent Glow.","gs.align_warn":"Elements deliberately placed between grid nodes will move. One undo is available after the operation, only until the next plan edit.","gs.align_run":"Optimize","gs.align_done":"Plans optimized: {n} elements moved, {m} records maintained","gs.optimize_undo":"Undo last optimization","gs.optimize_undone":"The last optimization was undone","decor.furniture":"Furniture","furn.title":"Furniture library","furn.symbol":"Symbol","furn.group_furniture":"Furniture","furn.group_appliance":"Appliances","furn.group_sanitary":"Plumbing","furn.group_other":"Other","furn.width":"Width","furn.depth":"Depth","furn.pick_hint":"Pick a symbol, then click on the plan.","furn.place_hint":"Click on the plan — the piece lands against the nearest wall. Shift places it free.","furn.sym_sofa":"Sofa","furn.sym_armchair":"Armchair","furn.sym_coffee_table":"Coffee table","furn.sym_table_dining":"Dining table","furn.sym_table_round":"Round table","furn.sym_chair":"Chair","furn.sym_desk":"Desk","furn.sym_bed_double":"Double bed","furn.sym_bed_single":"Single bed","furn.sym_nightstand":"Nightstand","furn.sym_wardrobe":"Wardrobe","furn.sym_bookshelf":"Bookshelf","furn.sym_fridge":"Fridge","furn.sym_stove":"Cooker","furn.sym_dishwasher":"Dishwasher","furn.sym_washer":"Washing machine","furn.sym_dryer":"Tumble dryer","furn.sym_tv":"TV","furn.sym_ac":"Air conditioner","furn.sym_water_heater":"Water heater","furn.sym_toilet":"Toilet","furn.sym_bathtub":"Bathtub","furn.sym_shower":"Shower","furn.sym_sink":"Washbasin","furn.sym_kitchen_sink":"Kitchen sink","furn.sym_bidet":"Bidet","furn.sym_stairs":"Stairs","furn.sym_fireplace":"Fireplace","furn.sym_plant":"Plant","furn.sym_rug":"Rug","common.yes":"Yes","common.no":"No","vac.diag_source":"Source","vac.diag_platform":"Integration","vac.diag_status":"Status","vac.diag_position":"Position","vac.diag_rooms":"Rooms","vac.diag_rooms_value":"{total} · {matched} names match · {readiness}","vac.autocal_ready":"auto-calibration available","vac.autocal_not_ready":"need 3 matching names","vac.diag_path":"Integration path","vac.diag_map":"Map ID","vac.source_none":"not selected","vac.source_status_ok":"Ready","vac.source_status_missing":"Missing","vac.source_status_disabled":"Disabled in Home Assistant","vac.source_status_unavailable":"Unavailable","vac.source_status_unverified":"Cannot verify with current permissions","vac.source_status_unsupported":"No position data","vac.source_status_none":"No source","vac.source_banner_missing":"The saved source no longer exists. It was not replaced automatically; choose another source or restore it in Home Assistant.","vac.source_banner_disabled":"The saved source is disabled in Home Assistant. Enable it there or choose another source.","vac.source_banner_unverified":"Current Home Assistant permissions cannot verify this saved source. It remains pinned and will not be replaced automatically.","vac.choose_source":"Choose source","vac.source_auto":"Automatic","vac.source_auto_hint":"Use a compatible entity from this device","vac.all_cameras":"All cameras","vac.all_cameras_warn":"A camera may not provide robot data. Choose one only if it is your vacuum map.","vac.all_cameras_empty":"No other camera entities found.","vac.platform_unknown":"unknown integration","vac.cap_position":"position","vac.cap_rooms_short":"rooms","vac.cap_path":"path","vac.cap_map":"map ID","vac.cap_none":"no robot data detected","vac.xcme_hint":"Enable these Xiaomi Cloud Map Extractor attributes:","vac.documentation":"Documentation","vac.residual_title":"Check automatic calibration","vac.residual_message":"The matched rooms disagree by up to {error}. Apply this approximate calibration, refine it manually, or cancel without changing the saved setup.","vac.apply_proposal":"Apply","gs.backup_group":"Backup and transfer","gs.backup_hint":"Download a portable JSON backup or preview and import a backup made by House Plan.","backup.export_open":"Export","backup.import_open":"Import","backup.export_title":"Export House Plan","backup.import_title":"Import House Plan","backup.export_hint":"Choose whether the backup should contain the whole House Plan configuration or only the current space.","backup.full":"Full backup","backup.current_space":"Current space","backup.current_space_title":"Current space: {title}","backup.no_current_space":"No current space","backup.privacy_warning":"The archive keeps names, Home Assistant identifiers and exact coordinates. Internal plans and attachments are referenced, not embedded; runtime states and vacuum trails are not included.","backup.download":"Download JSON","backup.export_done":"Backup downloaded","backup.reading":"Checking the backup…","backup.revalidated":"The plan changed after this preview. The summary was refreshed; review it and confirm again.","backup.error.unauthorized":"You do not have permission to export or import this plan.","backup.error.not_ready":"House Plan is not ready yet. Try again in a moment.","backup.error.too_large":"The backup exceeds the 8 MiB limit.","backup.error.invalid_json":"The selected file is not valid JSON.","backup.error.invalid_format":"The selected file is not a House Plan backup.","backup.error.unsupported_export_version":"This backup format is not supported by the installed version.","backup.error.future_model":"The backup was created by a newer House Plan data model.","backup.error.invalid_config":"The backup contains an invalid House Plan configuration.","backup.error.invalid_layout":"The backup contains invalid object positions.","backup.error.invalid_content":"The backup contains invalid or inconsistent content references.","backup.error.space_not_found":"The selected space no longer exists.","backup.error.capacity_exceeded":"Adding this backup would exceed the plan limits.","backup.error.preview_expired":"The preview expired. Select the backup file again.","backup.error.preview_owner_mismatch":"This preview belongs to another Home Assistant user.","backup.error.conflict":"The plan changed after the preview. Review the refreshed summary.","backup.error.content_confirmation_required":"Confirm that unavailable local content may be detached.","backup.error.commit_failed":"The backup could not be applied safely. The previous plan was restored or is pending recovery.","backup.error.missing_plan":"A referenced plan file disappeared after the preview. Review the backup again.","backup.error.marker_control_missing":"A linked plan light is missing from the backup.","backup.error.marker_control_not_light":"A linked plan target is no longer marked as a light source.","backup.error.marker_control_self":"A light source cannot control itself.","backup.error.marker_control_cycle":"The backup contains a circular chain of light controls.","backup.error.duplicate_marker_control":"The backup contains a duplicate plan-light link.","backup.error.no_backup":"There is no import or optimization snapshot to restore.","backup.same_source":"Created on this Home Assistant instance","backup.foreign_source":"Created on another Home Assistant instance","backup.created":"Created: {value}","backup.versions":"Card {card}; integration {integration}; data model {model}","backup.count_spaces":"Spaces: {n}","backup.count_rooms":"Rooms: {n}","backup.count_walls":"Walls: {n}","backup.count_openings":"Openings: {n}","backup.count_decor":"Decor objects: {n}","backup.count_markers":"Devices: {n}","backup.count_layout":"Positions: {n}","backup.bindings":"Bindings — devices: {device}, entities: {entity}, virtual: {virtual}; positions without a space: {legacy}","backup.binding_status":"Target status — active: {active}, disabled: {disabled}, missing: {missing}","backup.missing_areas":"Areas missing on the target: {areas}","backup.dropped_marker_links":"Plan-light links outside this transferred space were omitted: {n}.","backup.replace_warning":"This replaces the current configuration and layout. Uploaded files are never deleted. One undo remains available until the next plan edit.","backup.foreign_bookkeeping":"Instance-specific known/new-device bookkeeping will not be imported.","backup.final_name":"New space name","backup.target_settings":"The target instance's global settings remain unchanged.","backup.duplicates":"Bindings already present: {n}","backup.skip":"Skip duplicate devices","backup.virtual_copy":"Add safe static virtual copies","backup.content":"Referenced content","backup.content_available":"available locally","backup.content_external":"external link","backup.content_detach_required":"will be detached","backup.confirm_detach":"I understand that unavailable internal plans and attachments will be detached from the imported configuration.","backup.replace":"Replace","backup.add":"Add space","backup.space_done":"Space imported: {rooms} rooms, {markers} devices","backup.full_done":"Backup restored: {spaces} spaces, {rooms} rooms, {markers} devices","backup.undo_import":"Undo last full import","backup.import_undone":"Full import undone"};const Jo={en:Yo,ru:{"editor.context_actions":"Действия: {object}","editor.tool_options":"Параметры инструмента: {tool}","editor.palette":"Палитра: {tool}","editor.open_group":"Группа инструментов: {group}","editor.group_active":"{group} — активно: {item}","editor.disabled_action":"{action} недоступно: {reason}","btn.properties":"Свойства","btn.keep_as_walls":"Оставить замкнутыми стенами","title.markup_partition":"Перегородка: нарисуйте одну независимую физическую стену","title.markup_select":"Выбор и редактирование стен, колонн и сохранённых контуров","title.markup_column":"Колонна: кликните точку сетки для квадратной колонны","markup.partition":"Перегородка","markup.column":"Колонна","markup.hint_partition":"два клика рисуют одну перегородку · Shift фиксирует шаг 45°","markup.hint_column":"кликните точку сетки, чтобы поставить квадратную колонну","history.draft_segment":"Добавление сегмента черновика комнаты","history.draft_merge":"Соединение незавершённых контуров комнаты","history.draft_segment_delete":"Удаление сегмента черновика комнаты","history.partition_add":"Добавление перегородки","history.column_add":"Добавление колонны","history.physical_edit":"Изменение физического объекта","history.physical_delete":"Удаление физического объекта","history.physical_move":"Перемещение физического объекта","history.contour_to_partitions":"Преобразование контура в замкнутые стены","toast.column_duplicate":"Колонна с тем же центром и внешним размером уже существует","confirm.delete_draft":"Удалить весь незавершённый контур комнаты?","confirm.delete_draft_segment":"Удалить этот сегмент черновика? Оставшийся контур может разделиться на два.","physical.partition_properties":"Свойства перегородки","physical.column_properties":"Свойства колонны","physical.draft_properties":"Свойства сегмента черновика","physical.shape":"Форма","physical.square":"Квадрат","physical.circle":"Круг","physical.diameter":"Диаметр","physical.side":"Сторона","physical.rotation":"Угол поворота","physical.length":"Длина","physical.allowed_range":"Допустимо: 1–{max} {unit}","physical.delete_segment":"Удалить сегмент","physical.delete_draft":"Удалить весь контур","physical.partition_size_title":"Толщина перегородки, создаваемой вторым кликом (1–100 см).","physical.column_size_title":"Внешняя сторона квадратной колонны, создаваемой кликом (1–150 см).","card.title":"План дома","count.devices":"{n} устр.","empty.no_spaces":"Пространств пока нет.","empty.add_first":"Добавьте первое пространство и загрузите план этажа.","empty.install":"Установите интеграцию House Plan и добавьте запись в «Устройства и службы».","btn.add_space":"Добавить пространство","btn.cancel":"Отмена","btn.save":"Сохранить","btn.close":"Закрыть","btn.delete":"Удалить","btn.edit":"Редактировать","btn.open_in_ha":"Открыть в HA","btn.reset":"Сброс","btn.attach":"Прикрепить…","btn.upload":"Загрузить…","btn.replace":"Заменить…","title.zoom_in":"Приблизить","title.zoom_out":"Отдалить","title.zoom_fit":"Вписать всё","title.add_device":"Добавить устройство на план","title.show_all":"Показать скрытые и деактивированные устройства служебными маркерами (только в этой вкладке)","title.markup":"Разметка комнат: сетка, линии, контуры","title.configure_space":"Настроить пространство","title.add_space":"Добавить пространство","title.markup_add":"Контур комнаты: соединяйте точки сетки линиями до замыкания комнаты; Shift фиксирует направление с шагом 45°","title.markup_merge":"Объединить: клик по одной комнате, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить комнату: кликните внутри комнаты и подтвердите удаление","title.markup_boundary":"Граница: две точки на сплошной общей стене делают участок виртуальным; клик по пунктиру восстанавливает стену","title.need_plan":"Загрузите подложку (план этажа)","markup.add":"Контур комнаты","markup.merge":"Объединить","markup.split":"Разделить","markup.resize":"Размер","title.markup_resize":"Изменение размера комнат: тяните ручку стены; клик по комнате — угловая рамка масштаба","markup.hint_resize":"тяните ручку стены · клик по комнате — угловая рамка · Esc отменяет перетаскивание · Ctrl+Z — отмена шага","markup.opening":"Проём","markup.boundary":"Граница","markup.delete_room":"Удалить комнату","history.undo":"Отменить","history.redo":"Повторить","history.undo_named":"Отменить: {name} (Ctrl+Z)","history.redo_named":"Повторить: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Нет операций для отмены","history.redo_empty":"Нет операций для повтора","history.undone":"Отменено: {name}","history.redone":"Повторено: {name}","history.add_room":"Создание комнаты","history.split_room":"Разделение комнаты","history.merge_rooms":"Объединение комнат","history.resize_room":"Изменение размера комнаты","history.open_boundary":"Открытие границы","history.close_boundary":"Восстановление стены","history.wall_thickness":"Изменение толщины стены","history.add_opening":"Добавление дверного или оконного проёма","history.edit_opening":"Изменение проёма","history.move_opening":"Перемещение проёма","history.delete_opening":"Удаление проёма","history.delete_room":"Удаление комнаты","history.decor_add":"Добавление объекта декора","history.decor_edit":"Изменение объекта декора","history.decor_move":"Перемещение объекта декора","history.decor_transform":"Трансформация объекта декора","history.decor_delete":"Удаление объекта декора","history.backdrop_transform":"Трансформация подложки плана","title.markup_opening":"Проёмы: клик по стене — добавить, клик по проёму — редактировать","opening.new":"Новый проём","opening.edit":"Проём","opening.door":"Дверь","opening.window":"Окно","opening.gate":"Ворота","opening.type_label":"Тип","opening.length_label":"Длина, см","opening.contact_label":"Датчик открытия","opening.lock_label":"Замок","opening.none":"— нет —","opening.invert":"Инвертировать открыто/закрыто","opening.flip_h":"Петли с другой стороны","opening.flip_v":"Открывается в другую сторону","opening.open":"Открыто","opening.closed":"Закрыто","opening.locked":"Заперто","opening.unlocked":"Не заперто","opening.state_unknown":"недоступно","opening.no_entities":"Датчики не привязаны — статичный символ на плане.","toast.opening_no_wall":"Кликните рядом со стеной комнаты — проёмы ставятся на стены","markup.delete":"Удалить","markup.hint_points":"точек: {n} · Shift — шаг 45° · Esc/Ctrl+Z — убрать точку · клик по первой или Ctrl/Cmd+клик — замкнуть","markup.hint_start":"кликните точку сетки, чтобы начать контур","tip.lqi":"средний сигнал zigbee:","tip.area":"площадь: {value}","info.device_header":"Устройство на плане","info.model":"Модель","info.state":"Состояние","info.link":"Ссылка","info.manuals":"Инструкции","info.none":"Нет дополнительной информации","marker.new_device":"Новое устройство","marker.name_label":"Имя (отображается на плане)","marker.name_ph":"Название","marker.binding_label":"Привязка к устройству HA","marker.binding_disabled":"деактивировано в Home Assistant","marker.virtual_option":"Виртуальное устройство (без привязки)","marker.search_ph":"Поиск устройства / группы…","marker.nothing_found":"ничего не найдено","marker.room_label":"Комната","marker.room_override":" (переопределить размещение)","marker.room_choose":"— выберите комнату —","marker.room_auto":"— по зоне устройства (авто) —","marker.icon_label":"Иконка","marker.icon_ph":"mdi:… (пусто = авто)","marker.display_label":"Отображение","display.badge":"Значок + динамическая подложка","display.icon_ripple":"Значок + активность","display.static_icon":"Всегда статичный значок","marker.display_hint":"Динамическая подложка показывает работу, открытие, тревогу и недоступность. Значок + активность также показывает события и движение. Значение вместо иконки выводит одно однозначное состояние HA. Статичный значок не реагирует на состояния устройства.","marker.static_alarm_warning":"Это устройство может сообщать о тревогах. Статичный режим скрывает визуальную тревогу маркера.","marker.preview.title":"Предпросмотр отображения","marker.preview.actual":"Сейчас","marker.preview.example":"Пример","marker.preview.integration":"Интеграция","marker.preview.source":"Источник отображения","marker.preview.current_state":"Текущее состояние","marker.preview.result":"На плане","marker.preview.details":"Подробнее об источниках","marker.preview.select_source":"Выберите устройство или сущность Home Assistant, чтобы увидеть фактическое отображение.","marker.preview.unknown_provider":"Интеграция неизвестна","marker.preview.virtual_provider":"House Plan · виртуальное устройство","marker.preview.no_source":"Нет активного источника","marker.preview.no_state":"Нет текущего состояния","marker.preview.mixed_states":"Несколько разных состояний","marker.preview.multiple_sources":"Источников: {n}","marker.preview.more_sources":"ещё {n}","marker.preview.scaled":"Предпросмотр уменьшен до {n}%, чтобы поместиться. Сохранённый размер не изменится.","marker.preview.demo_activity":"Показать пример активности","marker.preview.demo_notice":"Временный пример активности; фактическое состояние устройства не меняется.","marker.preview.demo_already_visible":"Текущее состояние уже показывает реальную активность или тревогу.","marker.preview.reduced_motion":"В системе включено уменьшение движения, поэтому эффект показан статичным кольцом.","marker.preview.reason.neutral":"Нейтральная тёмная подложка","marker.preview.reason.working":"Жёлтая подложка: устройство сейчас работает","marker.preview.reason.working_activity":"Жёлтая подложка и эффект активности: устройство сейчас работает","marker.preview.reason.open":"Оранжевая подложка: физически открыто или разблокировано","marker.preview.reason.cover_icon_state":"Нейтральная подложка; состояние и значок определяет сущность шторы или заслонки","marker.preview.reason.presence":"Эффект активности, пока обнаружено присутствие","marker.preview.reason.event":"Короткий эффект активности после события","marker.preview.reason.transition":"Эффект активности во время движения или смены состояния","marker.preview.reason.media_neutral":"Медиаустройство остаётся на нейтральной тёмной подложке, пока доступно","marker.preview.reason.unavailable":"Приглушённая нейтральная подложка: отключено или недоступно","marker.preview.reason.alarm":"Красная тревожная подложка; тревога отображается всегда","marker.preview.reason.live_states_disabled":"Оформление по живым состояниям отключено в настройках карточки","marker.preview.reason.value_no_state":"Нет подходящего значения состояния — вместо него показан значок","marker.preview.reason.value_ambiguous_sources":"Подходит несколько источников значения — вместо него показан значок","marker.preview.reason.value_non_scalar":"Источник вернул не простое значение — вместо него показан значок","marker.preview.reason.value_virtual":"У виртуального устройства нет значения HA — вместо него показан значок","marker.preview.reason.vacuum_live_plan_only":"Живая позиция и след пылесоса доступны только на полном плане","marker.preview.reason.hidden_design_preview":"Устройство скрыто на плане, но его оформление видно в предпросмотре","marker.preview.reason.composite_power_source":"Состояние берётся из сущности питания устройства; вспомогательные переключатели не учитываются","marker.preview.reason.activity_display_disabled":"Выбранный режим отображения не показывает обычные эффекты активности","marker.preview.reason.ha_disabled":"Привязка отключена в Home Assistant, поэтому устройство будет скрыто на плане","marker.preview.reason.orphaned":"Сохранённая привязка Home Assistant больше не найдена","marker.preview.reason.static_icon":"Статичный режим: состояние устройства не меняет значок","marker.activity_color":"Цвет эффекта","marker.ripple_size":"Размер эффекта","marker.size_label":"Размер / поворот значка","marker.angle_label":"Поворот","marker.model_label":"Модель","marker.model_ph":"напр. Aqara T&H","marker.link_label":"Ссылка","marker.desc_label":"Описание","marker.desc_ph":"Заметки, характеристики…","marker.manuals_label":"Инструкции (PDF и т.п.)","marker.sub_device":"устройство","marker.sub_z2m_group":" · Z2M-группа","marker.sub_group":"группа","marker.sub_helper":"хелпер","space.new":"Новое пространство","space.header":"Пространство","space.title_label":"Название","space.title_ph":"Например: Гараж","space.plan_label":"Подложка (план)","space.no_plan":"нет подложки","space.plan_alt":"план","room.new":"Новая комната","room.name_label":"Отображаемое имя","room.name_ph":"Например: Терраса","room.area_label":"Зона Home Assistant (свободные)","room.no_area_option":"— без зоны —","room.default_name":"Комната","device.unnamed":"без имени","device.light_group":"группа света","device.fallback":"устройство","device.virtual":"виртуальное устройство","confirm.delete_room":"Удалить комнату «{name}»?","confirm.merge_rooms":"Слить комнаты «{a}» и «{b}»?","confirm.remove_marker":"Удалить «{name}» с плана? Устройство исчезнет полностью и перестанет участвовать в данных плана. Позже его можно будет добавить заново.","confirm.erase_decor":"Стереть объект «{kind}»? Действие можно отменить из истории редактора.","confirm.delete_space":"Удалить пространство «{title}» со всеми комнатами и разметкой?","toast.pos_save_failed":"Не удалось сохранить позицию: {err}","toast.no_entity":"У устройства нет подходящей сущности","toast.ha_disabled_action":"Деактивированный объект Home Assistant нельзя использовать на плане.","toast.ha_disabled_show_device":"Деактивированное в Home Assistant устройство нельзя показать на плане. Сначала активируйте его в Home Assistant.","toast.ha_disabled_show_entity":"Деактивированную в Home Assistant сущность нельзя показать на плане. Сначала активируйте её в Home Assistant.","toast.ha_disabled_add":"Деактивированный объект Home Assistant нельзя добавить на план. Сначала активируйте его в Home Assistant.","toast.ha_binding_unverified":"Статус объекта не удалось подтвердить по реестру Home Assistant. Отображение и действия временно недоступны.","toast.markup_needs_server":"Разметка доступна после переноса конфига на сервер","toast.conflict":"Конфиг изменён в другом окне — данные обновлены, повторите последнее действие","toast.cfg_save_failed":"Не удалось сохранить конфиг: {err}","toast.room_overlap":"Контур накладывается на комнату «{name}» — комнаты не должны накладываться","toast.contour_min_edges":"Чтобы замкнуть контур, сначала нарисуйте минимум две грани","toast.contour_cannot_close":"Контур нельзя замкнуть: он вырожден или пересекает сам себя","toast.merge_not_adjacent":"Объединять можно только комнаты с общей стеной","toast.rooms_merged":"Комнаты объединены в «{name}»","toast.split_pick_wall":"Начните разрез на стене комнаты","toast.split_bad_cut":"Разрез — от стены до стены внутри комнаты, без пересечения стен и самого себя","merge.header":"Объединение комнат","merge.hint":"У объединённой комнаты одно имя и одна зона. Вторая зона освобождается — её устройства уйдут с плана, пока их не заберёт другая комната.","merge.keep":"Оставить","merge.no_area":"без зоны","room.split_header":"Новая комната после разделения","toast.room_saved":"Комната сохранена ({n}). Устройств добавлено: {added}. Обведите следующую или выйдите из разметки.","toast.room_saved_no_area":"Комната сохранена ({n}, без зоны). Обведите следующую или выйдите из разметки.","toast.marker_needs_server":"Редактирование устройств доступно после переноса конфига на сервер","toast.virtual_name_required":"Укажите имя виртуального устройства","toast.marker_saved":"Устройство сохранено","toast.marker_removed":"Устройство удалено с плана","toast.integration_missing":"Интеграция House Plan не установлена — управление недоступно","toast.plan_formats":"Поддерживаются SVG, PNG, JPG, WebP","toast.plan_required":"Загрузите подложку — план этажа обязателен","toast.space_added_onboard":"Пространство добавлено. Обведите комнаты: кликайте по точкам сетки и замкните контур.","toast.space_added":"Пространство добавлено","toast.space_saved":"Пространство сохранено","toast.space_deleted":"Пространство удалено","toast.delete_failed":"Ошибка удаления: {err}","toast.error":"Ошибка: {err}","toast.file_failed":"Файл «{name}» не загружен: {err}","toast.files_attached":"Прикреплено файлов: {n}","err.unknown":"неизвестная ошибка","err.code":"код {code}","err.too_large":"файл больше {mb} МБ","err.bad_ext":"недопустимый тип (нужен PDF/изображение)","err.unauthorized":"нужны права администратора","editor.title":"Заголовок","editor.default_floor":"Пространство по умолчанию","editor.icon_size":"Размер иконок, % ширины плана","editor.show_temperature":"Показывать температуру","editor.live_states":"Живые состояния (вкл/выкл, открыто…)","editor.show_signal":"Показывать сигнал zigbee (LQI)","editor.language":"Язык интерфейса","editor.lang_auto":"Авто (профиль HA)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Правила иконок: какая MDI-иконка достаётся устройству по имени","rules.title":"Правила иконок","rules.hint":"Правила проверяются сверху вниз по строке «имя устройства + модель» (regex без учёта регистра); срабатывает первое совпадение. Если ничего не подошло — решает device class сущности, затем — иконка-заглушка.","rules.pattern_ph":"regex, напр. розетк|plug","rules.icon_ph":"mdi:power-socket-de","rules.add":"Добавить правило","rules.reset":"Сбросить к умолчаниям","rules.test_ph":"Проверьте имя устройства…","rules.invalid":"некорректный regex","rules.saved":"Правила иконок сохранены","btn.up":"Вверх","btn.down":"Вниз","tap.info":"Карточка устройства","tap.more_info":"Диалог HA (more-info)","tap.toggle":"Переключить состояние","marker.tap_label":"Действие по нажатию для этого устройства","tap.toggle_note":"Под селектором показано, какое состояние изменится. Защищённые устройства не переключаются с плана.","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":"По освещению","fill.custom":"Свой цвет","space.custom_fill":"Цвет заливки","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":"Загрузка…","continuity.restore_plan":"Восстанавливаем план…","continuity.restore_connection":"Восстанавливаем подключение к устройствам…","continuity.retry":"Повторить","editor.space":"Пространство","editor.show_button":"Показывать кнопку","editor.button_label":"Текст кнопки","editor.button_target":"Путь дашборда (куда вести)","marker.sub_entity":"сущность","title.general_settings":"Общие настройки","gs.title":"Общие настройки","gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.light_group":"Заливка: освещение","gs.light_on":"Свет включён","gs.light_off":"Весь свет выключен","gs.temp_group":"Заливка: температура","gs.temp_cold":"Холодно","gs.temp_ok":"Комфорт","gs.temp_hot":"Жарко","gs.lqi_group":"Заливка: зигби-сигнал","gs.lqi_low":"Слабый сигнал","gs.lqi_high":"Сильный сигнал","gs.reset":"Сбросить к умолчаниям","gs.saved":"Общие настройки сохранены","space.show_lqi":"Показывать зигби-сигнал (LQI) у устройств","space.hide_decor":"Скрыть декоративный слой","space.hide_decor_tip":"Линии, фигуры, надписи и мебель остаются на месте — их видно в редакторе подложки, но не на плане.","space.hide_openings":"Скрыть проёмы","space.hide_openings_tip":"Двери, окна и ворота не рисуются, но продолжают работать: свет проходит, солнце светит в окна, датчики открытия срабатывают. В редакторе плана проёмы видно всегда.","gs.light_none":"Нет источников света","mode.plan":"Редактор плана","mode.devices":"Редактор устройств","display.value":"Значение вместо иконки","marker.subarea":"без зоны, вручную","device.new":"Новое устройство — откройте его редактор, чтобы снять отметку","opening.unlock_action":"Открыть замок","opening.lock_action":"Закрыть замок","opening.lock_pending":"Выполняется…","title.close_editor":"Закрыть редактор (вернуться к просмотру)","devbar.add":"Добавить","devbar.show_all":"Скрытые и деактивированные","devbar.rules":"Правила иконок","space.roomcard_section":"В карточке комнаты:","space.label_temp":"Температура","space.label_hum":"Влажность","space.label_lqi":"Средний Zigbee-сигнал","space.label_light":"Свет вкл/выкл","roomcard.light_on":"Вкл","roomcard.light_off":"Выкл","roomcard.light_partial":"{on} из {total}","toast.split_pick_inside":"Промежуточные точки разреза — внутри комнаты","mode.decor":"Редактор подложки","decor.select":"Выбрать","decor.line":"Линия","decor.rect":"Прямоугольник","decor.ellipse":"Овал","decor.text":"Надпись","decor.erase":"Стереть","decor.erase_confirm_title":"Стереть объект?","decor.color":"Цвет","decor.width":"Толщина линии","decor.line_style":"Стиль линии","decor.line_style_solid":"Сплошная","decor.line_style_dashed":"Пунктирная","decor.fill":"Заливка","decor.fill_color":"Цвет заливки","decor.length":"Длина","decor.size":"Размер","decor.angle":"Угол поворота, °","decor.text_size":"Размер текста","decor.backdrop_properties":"Свойства картинки-подложки","decor.text_title":"Надпись","decor.object_title":"Редактирование: {kind}","decor.text_label":"Текст","decor.live_group":"Вставить переменную HA","decor.live_entity":"Сущность","decor.live_entity_ph":"выберите сущность","decor.live_attr":"Значение","decor.live_attr_ph":"выберите состояние или атрибут","decor.live_state":"Состояние","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — перемещение; угол — размер; верхнюю ручку — поворот. Shift меняет пропорции или снимает шаг угла.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","marker.icon_pin_auto":"Закрепить","mode.plan_tip":"Редактор плана — геометрия дома: рисование и объединение/разделение комнат, привязка к зонам HA, двери, окна и ворота, карточки комнат, масштаб","mode.devices_tip":"Редактор устройств — всё про значки: перетаскивание, клик — настройка привязки/иконки/отображения, виртуальные устройства, правила иконок","mode.decor_tip":"Редактор подложки — чисто визуальный декор под планом: линии, прямоугольники, овалы и надписи, не реагирующие на клики","space.glow_enabled":"Свечение источников света","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.help":"Задаёт радиус свечения в метрах или футах; пустое значение использует радиус из общих настроек.","marker.glow_radius.help.aria":"Подсказка: радиус свечения","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены комнаты (1–100 см). Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от 1 до {max} {unit}","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","markup.boundary_hint":"Выберите общую границу комнат или пунктирный участок","markup.boundary_hint_open":"Кликните начало открытого участка","markup.boundary_hint_second":"Укажите конец на выбранной границе · Esc — отмена","markup.boundary_hint_retry":"Укажите конец на выбранной границе","markup.boundary_hint_restore":"Кликните, чтобы восстановить стену","markup.boundary_hint_outer":"Открыть можно только общую границу двух комнат","markup.boundary_hint_blocked":"над границей находится перегородка, колонна или незавершённый контур","markup.boundary_hint_ambiguous":"Выберите участок чуть дальше от стыка","markup.boundary_cancel":"Отменить незавершённое действие «Граница»","toast.openwall_pick":"Инструмент работает с границами комнат","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.boundary_opened":"Участок границы открыт","toast.boundary_restored":"Стена на участке восстановлена","toast.boundary_same_edge":"Выберите конечную точку на выбранной стене","toast.boundary_ambiguous":"Выберите участок чуть дальше от стыка","toast.boundary_blocked":"Граница перекрыта независимой стеной. Сначала переместите или удалите её инструментом «Выбрать»","toast.delete_room_pick":"Кликните внутри комнаты, которую нужно удалить","toast.openwall_openings_removed":"Проёмы на виртуальном отрезке удалены","toast.openwall_opened":"Граница «{a}» ↔ «{b}» теперь открыта","toast.openwall_closed":"Граница «{a}» ↔ «{b}» снова закрыта","toast.opening_on_virtual":"Проёмы на виртуальной стене запрещены","marker.from_ha_option":"Выбрать из списка HA","marker.show_entities":"Отображать сущности","marker.show_entities_tip":"Добавляет в список не только устройства, но и все их сущности","marker.pick_ph":"Выберите устройство…","room.open_area":"Открыть зону в HA","kiosk.title":"Размеры на этом экране","kiosk.hint":"Хранится только на этом устройстве — у каждого настенного планшета или ТВ свои удобные размеры.","kiosk.icon_scale":"Размер значков устройств","kiosk.font_scale":"Размер текста карточек комнат","editor.kiosk":"Режим настенного устройства (киоск)","editor.cycle":"Автосмена пространств каждые N секунд (киоск, 0 = выкл)","room.settings_title":"Настройки комнаты","room.settings_section":"Настройки комнаты (переопределяют пространство)","room.fill_label":"Заливка в ЭТОЙ комнате","fill.inherit":"Как у пространства","room.custom_fill_space":"Цвет пространства","room.custom_fill_own":"Цвет комнаты","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.value_badge_title":"Бейдж со значением","marker.value_badge.help":"Показывает одно выбранное значение рядом со значком. Не влияет на комнатные показатели, свет, Glow и действие по нажатию.","marker.value_badge.help.aria":"Подсказка: бейдж со значением устройства","marker.value_badge_enabled":"Отображать бейдж со значением","marker.value_badge_source":"Значение","marker.value_badge_source.help":"Выберите конкретное состояние, поддерживаемый атрибут или производное значение этого устройства.","marker.value_badge_source.help.aria":"Подсказка: источник значения бейджа","marker.value_badge_position":"Расположение","marker.value_badge_position.help":"Выбранная сторона сохраняется постоянно и не переворачивается автоматически у края плана.","marker.value_badge_position.help.aria":"Подсказка: расположение бейджа","marker.value_badge_right":"Справа","marker.value_badge_bottom":"Снизу","marker.value_badge_left":"Слева","marker.value_badge_top":"Сверху","marker.value_badge_empty":"У этого устройства нет доступных значений","marker.value_badge_static":"Статичный значок не показывает живые значения. Настройка сохранится.","marker.value_badge_missing":"Источник недоступен","marker.value_badge_missing_hint":"Сохранённый источник сейчас недоступен. Можно сохранить его, отключить бейдж или выбрать другой.","marker.value_badge_duplicate":"То же значение уже показано внутри значка.","marker.value_badge_state":"Состояние · {name}","marker.value_badge_marker_state":"Состояние источника света · {name}","marker.value_badge_lqi":"Качество Zigbee-сигнала (среднее)","marker.value_badge_attr_current_temperature":"Текущая температура · {name}","marker.value_badge_attr_temperature":"Целевая температура · {name}","marker.value_badge_attr_current_humidity":"Текущая влажность · {name}","marker.value_badge_attr_humidity":"Влажность · {name}","marker.value_badge_attr_current_position":"Положение · {name}","marker.value_badge_attr_percentage":"Скорость · {name}","marker.value_badge_attr_brightness":"Яркость · {name}","marker.value_badge_attr_volume_level":"Громкость · {name}","marker.value_badge_attr_battery_level":"Заряд батареи · {name}","marker.value_badge_attr_fan_speed":"Скорость вентилятора · {name}","marker.light_role_label":"Является источником света","marker.light_role.help":"Определяет, является ли сам маркер пространственным источником света. Привязанные выше лампы остаются независимыми источниками для комнаты.","marker.light_role.help.aria":"Подсказка: является ли устройство источником света","marker.light_role_auto_yes":"Автоматически (источник света)","marker.light_role_auto_no":"Автоматически (не источник света)","marker.light_role_always":"Всегда источник света","marker.light_role_never":"Никогда не источник света","marker.light_entity_label":"Ведущая сущность света","marker.light_entity.help":"Для составного устройства выберите сущность, состояние, цвет и яркость которой представляют этот источник на плане. Автоматический вариант сохраняет совместимость со старыми планами.","marker.light_entity.help.aria":"Подсказка: ведущая сущность света","marker.light_entity_auto":"Автоматически ({entity})","marker.light_entity_none":"нет управляемой сущности","marker.light_entity_missing":"Сохранённая сущность {entity} недоступна. Временно используется {fallback}; выбор восстановится, если сущность вернётся.","marker.glow_color_label":"Цвет и яркость свечения","marker.glow_mode.help":"Используйте данные источника, задайте только цвет или зафиксируйте цвет и яркость. Минимум — 1%; чтобы отключить источник, выберите «Никогда».","marker.glow_mode.help.aria":"Подсказка: цвет и яркость свечения","marker.glow_mode_auto":"Из источника","marker.glow_mode_color":"Задать цвет","marker.glow_mode_fixed":"Задать цвет и яркость","marker.glow_color":"Цвет свечения","marker.glow_brightness":"Яркость","marker.glow_disabled_never":"Настройки свечения недоступны: маркер явно исключён из источников света.","marker.glow_disabled_auto":"В автоматическом режиме у этого маркера не найден пространственный источник света.","marker.glow_disabled_no_entity":"Для пространственного источника нет активной управляемой сущности Home Assistant.","marker.glow_passive_hint":"У источника нет собственных данных Home Assistant. Задайте цвет и яркость вручную; радиус остаётся доступным.","marker.control_broken":"Сохранённый источник отсутствует или больше не отмечен как источник света","marker.control_passive":"пассивный источник","toast.marker_control_cycle":"Такая связь создаст циклическую цепочку управления светом.","toast.marker_binding_required":"Сначала выберите устройство Home Assistant, затем добавьте управляемый источник света.","confirm.unlock":"Открыть замок «{name}»?","toast.files_migrate_failed":"Не удалось перенести вложения к новой привязке, ссылки остались на старые файлы: {err}","space.pick_saved":"Уже загруженные","space.pick_saved_hint":"Планы, сохранённые на сервере, включая отцеплённые ранее","space.no_saved":"На сервере пока нет сохранённых планов.","space.loading":"Загрузка…","space.used_by":"используется: {list}","space.in_use":"План используется пространством — сначала отцепите его","btn.use":"Выбрать","confirm.delete_plan":"Удалить файл плана «{name}» с сервера? Действие необратимо.","toast.plans_list_failed":"Не удалось получить список планов: {err}","toast.plan_delete_failed":"Не удалось удалить план: {err}","marker.hide":"Скрыть","marker.hide_tip":"Устройство исчезнет с плана после сохранения, но продолжит участвовать в расчёте сигнала комнаты. Вернуть его можно через кнопку «Скрытые и деактивированные» в редакторе устройств.","marker.show":"Показать","marker.show_tip":"Устройство снова появится на плане после сохранения.","marker.hidden_ghost":"Устройство скрыто пользователем","marker.ha_disabled_device":"Устройство деактивировано в Home Assistant и скрыто с плана.","marker.ha_disabled_entity":"Сущность деактивирована в Home Assistant и скрыта с плана.","marker.ha_disabled_all_entities":"У устройства нет активных сущностей Home Assistant, поэтому оно скрыто с плана.","marker.ha_registry_limited":"Полный реестр Home Assistant недоступен этому пользователю. Неподтверждённый объект временно нельзя показывать или использовать.","marker.delete_tip":"Полностью удалить устройство с плана и из всех расчётов. Позже его можно добавить заново.","tap.run":"Запустить автоматизацию/скрипт/сцену","tap.cover":"Открыть/закрыть (шторы/жалюзи)","marker.run_target_label":"Что запускать","marker.run_search_ph":"Поиск: автоматизация, скрипт или сцена…","marker.run_target_gone":"Цель {id} не найдена — выберите заново","marker.tap_confirm":"Спрашивать подтверждение","marker.tap_confirm_tip":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","marker.toggle_hint_single":"Цель: {name} ({id}).","marker.toggle_hint_group":"Переключатся источники ({count}): {names}.","marker.toggle_hint_current":"Сейчас: {state} → {effect}.","marker.toggle_hint_group_current":"Сейчас включено {on} из {count} → {effect}.","marker.toggle_hint_skipped":"Не будут затронуты ({count}): {targets}.","marker.toggle_effect_turn_on":"включится","marker.toggle_effect_turn_off":"выключится","marker.toggle_effect_open":"откроется","marker.toggle_effect_close":"закроется","marker.toggle_effect_stop":"остановится","marker.toggle_effect_toggle":"состояние переключится","marker.toggle_skip_missing":"не найдено","marker.toggle_skip_ha_disabled":"деактивировано в HA","marker.toggle_skip_unavailable":"недоступно","marker.toggle_skip_unsupported":"переключение не поддерживается","marker.toggle_skip_secure":"заблокировано из соображений безопасности","marker.toggle_none_no_binding":"У устройства нет привязки, состояние переключать нечем. По нажатию ничего не произойдёт.","marker.toggle_none_no_actionable_entity":"У устройства нет состояния, которое можно переключить. По нажатию ничего не произойдёт.","marker.toggle_none_configured_targets_missing":"Настроенные цели сейчас недоступны. Собственная сущность устройства не будет подставлена вместо них.","marker.toggle_none_ha_disabled":"Цель деактивирована в Home Assistant и не может использоваться на плане.","marker.toggle_none_unavailable":"Цель сейчас недоступна. По нажатию ничего не произойдёт.","marker.toggle_none_unsupported":"Для этой сущности Home Assistant не предоставляет безопасного переключения. По нажатию ничего не произойдёт.","marker.toggle_none_secure":"Переключение замков, сигнализации и защитных ворот с плана заблокировано из соображений безопасности.","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":"Выберите автоматизацию, скрипт или сцену","toast.tap_target_changed":"Цель действия изменилась. Повторите попытку.","toast.value_badge_source_required":"Выберите значение для бейджа","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.autocal":"Настроить автоматически","vac.live":"Живая позиция на плане","vac.trail":"Показывать путь робота","vac.cal_maps":"Откалиброваны карты: {maps}","vac.autocal_no_rooms":"Интеграция не отдаёт список комнат — откройте «Подогнать вручную»","vac.autocal_no_match":"Не совпали имена комнат (нужно ≥3 общих) — откройте «Подогнать вручную»","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.about_group":"О карточке","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · документация и issues","gs.about_telegram":"Чат в Telegram","space.bg_color":"Цвет фона вокруг плана","space.bg_inherit":"Наследовать общий","space.bg_inherited":"наследуется из общих настроек","space.bg_mode":"Фон плана","space.north":"Север на плане (переопределение)","space.north_inherited":"наследуется: {v}","space.sun_rays":"Солнце в окнах","space.sun_inherit":"Наследовать общий","space.sun_on":"Вкл","space.sun_off":"Выкл","canvas.far_objects":"Объектов далеко от плана: {n}","canvas.show_far":"Показать","canvas.home_tip":"План там — нажмите, чтобы вписать","gs.grid_group":"Обслуживание планов","gs.grid_hint":"Обновляет модели данных, выравнивает элементы по сетке и объединяет лишние фрагменты стен. Перед записью будет показан точный отчёт.","gs.align_all":"Оптимизировать планы","gs.align_title":"Оптимизировать планы","gs.align_none":"Все планы уже используют актуальную и оптимальную модель данных.","gs.align_count":"Сдвинется элементов: {n} из {total}, максимум на {cm} см.","gs.align_where":"Наибольший сдвиг — в пространстве «{s}».","gs.align_turned":"Проёмов с исправлением угла: {n}.","gs.align_removed_drafts":"Схлопнувшиеся на сетке некорректные контуры удалены: {n}.","gs.optimize_changes":"Миграций модели: {m}; канонизировано планов: {c}; объединено отрезков реальных стен: {w}; виртуальных: {s}.","gs.optimize_glow_migration":"Старый Glow: пространств — {spaces} → без заливки данных + независимый Glow; комнат — {rooms} → наследуемая заливка + независимый Glow.","gs.align_warn":"Элементы, намеренно поставленные между узлами, будут сдвинуты. После операции доступна одна отмена — только до следующего изменения плана.","gs.align_run":"Оптимизировать","gs.align_done":"Планы оптимизированы: сдвинуто элементов — {n}, обслужено записей — {m}","gs.optimize_undo":"Отменить последнюю оптимизацию","gs.optimize_undone":"Последняя оптимизация отменена","decor.furniture":"Мебель","furn.title":"Библиотека мебели","furn.symbol":"Символ","furn.group_furniture":"Мебель","furn.group_appliance":"Техника","furn.group_sanitary":"Сантехника","furn.group_other":"Прочее","furn.width":"Ширина","furn.depth":"Глубина","furn.pick_hint":"Выберите символ и кликните по плану.","furn.place_hint":"Кликните по плану — предмет встанет к ближайшей стене. Shift — свободно.","furn.sym_sofa":"Диван","furn.sym_armchair":"Кресло","furn.sym_coffee_table":"Журнальный столик","furn.sym_table_dining":"Обеденный стол","furn.sym_table_round":"Круглый стол","furn.sym_chair":"Стул","furn.sym_desk":"Письменный стол","furn.sym_bed_double":"Двуспальная кровать","furn.sym_bed_single":"Односпальная кровать","furn.sym_nightstand":"Тумбочка","furn.sym_wardrobe":"Шкаф","furn.sym_bookshelf":"Стеллаж","furn.sym_fridge":"Холодильник","furn.sym_stove":"Плита","furn.sym_dishwasher":"Посудомоечная машина","furn.sym_washer":"Стиральная машина","furn.sym_dryer":"Сушильная машина","furn.sym_tv":"Телевизор","furn.sym_ac":"Кондиционер","furn.sym_water_heater":"Бойлер","furn.sym_toilet":"Унитаз","furn.sym_bathtub":"Ванна","furn.sym_shower":"Душ","furn.sym_sink":"Раковина","furn.sym_kitchen_sink":"Кухонная мойка","furn.sym_bidet":"Биде","furn.sym_stairs":"Лестница","furn.sym_fireplace":"Камин","furn.sym_plant":"Растение","furn.sym_rug":"Ковёр","common.yes":"Да","common.no":"Нет","vac.diag_source":"Источник","vac.diag_platform":"Интеграция","vac.diag_status":"Статус","vac.diag_position":"Позиция","vac.diag_rooms":"Комнаты","vac.diag_rooms_value":"{total} · совпало имён: {matched} · {readiness}","vac.autocal_ready":"автокалибровка доступна","vac.autocal_not_ready":"нужно 3 совпавших имени","vac.diag_path":"Путь интеграции","vac.diag_map":"ID карты","vac.source_none":"не выбран","vac.source_status_ok":"Готов","vac.source_status_missing":"Не найден","vac.source_status_disabled":"Деактивирован в Home Assistant","vac.source_status_unavailable":"Недоступен","vac.source_status_unverified":"Нельзя проверить с текущими правами","vac.source_status_unsupported":"Нет данных позиции","vac.source_status_none":"Нет источника","vac.source_banner_missing":"Сохранённый источник больше не существует. Он не заменён автоматически: выберите другой или восстановите его в Home Assistant.","vac.source_banner_disabled":"Сохранённый источник деактивирован в Home Assistant. Активируйте его там или выберите другой.","vac.source_banner_unverified":"Текущих прав Home Assistant недостаточно для проверки источника. Привязка сохранена и не будет заменена автоматически.","vac.choose_source":"Выбрать источник","vac.source_auto":"Автоматически","vac.source_auto_hint":"Использовать совместимую сущность этого устройства","vac.all_cameras":"Все камеры","vac.all_cameras_warn":"Камера может не отдавать данные робота. Выбирайте её только если это карта вашего пылесоса.","vac.all_cameras_empty":"Других сущностей камеры не найдено.","vac.platform_unknown":"интеграция неизвестна","vac.cap_position":"позиция","vac.cap_rooms_short":"комнаты","vac.cap_path":"путь","vac.cap_map":"ID карты","vac.cap_none":"данные робота не обнаружены","vac.xcme_hint":"Включите атрибуты Xiaomi Cloud Map Extractor:","vac.documentation":"Документация","vac.residual_title":"Проверьте автокалибровку","vac.residual_message":"Совпавшие комнаты расходятся максимум на {error}. Примените приблизительную калибровку, подгоните её вручную либо отмените без изменения сохранённых настроек.","vac.apply_proposal":"Применить","gs.backup_group":"Резервная копия и перенос","gs.backup_hint":"Скачайте переносимую JSON-копию или проверьте и импортируйте копию, созданную House Plan.","backup.export_open":"Экспорт","backup.import_open":"Импорт","backup.export_title":"Экспорт House Plan","backup.import_title":"Импорт House Plan","backup.export_hint":"Выберите, должна ли копия содержать всю конфигурацию House Plan или только текущее пространство.","backup.full":"Полная копия","backup.current_space":"Текущее пространство","backup.current_space_title":"Текущее пространство: {title}","backup.no_current_space":"Нет текущего пространства","backup.privacy_warning":"Архив сохраняет названия, идентификаторы Home Assistant и точные координаты. Внутренние планы и вложения указываются ссылками, но не вкладываются; текущие состояния и маршруты пылесосов не включаются.","backup.download":"Скачать JSON","backup.export_done":"Резервная копия скачана","backup.reading":"Проверяем резервную копию…","backup.revalidated":"После предпросмотра план изменился. Сводка обновлена — проверьте её и подтвердите ещё раз.","backup.error.unauthorized":"У вас нет прав на экспорт или импорт этого плана.","backup.error.not_ready":"House Plan ещё не готов. Повторите попытку через несколько секунд.","backup.error.too_large":"Размер резервной копии превышает 8 МиБ.","backup.error.invalid_json":"Выбранный файл не является корректным JSON.","backup.error.invalid_format":"Выбранный файл не является резервной копией House Plan.","backup.error.unsupported_export_version":"Эта версия формата резервной копии не поддерживается установленной версией.","backup.error.future_model":"Резервная копия создана в более новой версии модели данных House Plan.","backup.error.invalid_config":"Резервная копия содержит некорректную конфигурацию House Plan.","backup.error.invalid_layout":"Резервная копия содержит некорректные позиции объектов.","backup.error.invalid_content":"Резервная копия содержит некорректные или несогласованные ссылки на файлы.","backup.error.space_not_found":"Выбранное пространство больше не существует.","backup.error.capacity_exceeded":"Добавление этой копии превысит ограничения размера плана.","backup.error.preview_expired":"Время предпросмотра истекло. Выберите файл резервной копии заново.","backup.error.preview_owner_mismatch":"Этот предпросмотр принадлежит другому пользователю Home Assistant.","backup.error.conflict":"После предпросмотра план изменился. Проверьте обновлённую сводку.","backup.error.content_confirmation_required":"Подтвердите отсоединение недоступных локальных файлов.","backup.error.commit_failed":"Не удалось безопасно применить копию. Предыдущий план восстановлен или ожидает восстановления.","backup.error.missing_plan":"После предпросмотра исчез связанный файл плана. Проверьте копию ещё раз.","backup.error.marker_control_missing":"В резервной копии отсутствует связанный источник света на плане.","backup.error.marker_control_not_light":"Связанная цель больше не отмечена как источник света.","backup.error.marker_control_self":"Источник света не может управлять самим собой.","backup.error.marker_control_cycle":"Резервная копия содержит циклическую цепочку управления светом.","backup.error.duplicate_marker_control":"Резервная копия содержит повторяющуюся связь с источником света.","backup.error.no_backup":"Нет снимка импорта или оптимизации, который можно восстановить.","backup.same_source":"Создано на этом экземпляре Home Assistant","backup.foreign_source":"Создано на другом экземпляре Home Assistant","backup.created":"Создано: {value}","backup.versions":"Карточка {card}; интеграция {integration}; модель данных {model}","backup.count_spaces":"Пространств: {n}","backup.count_rooms":"Комнат: {n}","backup.count_walls":"Стен: {n}","backup.count_openings":"Проёмов: {n}","backup.count_decor":"Объектов декора: {n}","backup.count_markers":"Устройств: {n}","backup.count_layout":"Позиций: {n}","backup.bindings":"Привязки — устройства: {device}, сущности: {entity}, виртуальные: {virtual}; позиций без пространства: {legacy}","backup.binding_status":"Статус на целевом экземпляре — активных: {active}, деактивированных: {disabled}, отсутствующих: {missing}","backup.missing_areas":"На целевом экземпляре отсутствуют зоны: {areas}","backup.dropped_marker_links":"Связи с источниками света вне переносимого пространства пропущены: {n}.","backup.replace_warning":"Текущая конфигурация и расположение будут заменены. Загруженные файлы не удаляются. Одну отмену можно выполнить до следующего изменения плана.","backup.foreign_bookkeeping":"Служебные списки известных и новых устройств другого экземпляра импортированы не будут.","backup.final_name":"Название нового пространства","backup.target_settings":"Глобальные настройки целевого экземпляра не изменятся.","backup.duplicates":"Уже существующих привязок: {n}","backup.skip":"Пропустить повторяющиеся устройства","backup.virtual_copy":"Добавить безопасные статичные виртуальные копии","backup.content":"Связанное содержимое","backup.content_available":"доступно локально","backup.content_external":"внешняя ссылка","backup.content_detach_required":"будет отвязано","backup.confirm_detach":"Я понимаю, что недоступные внутренние планы и вложения будут отвязаны от импортируемой конфигурации.","backup.replace":"Заменить","backup.add":"Добавить пространство","backup.space_done":"Пространство импортировано: комнат — {rooms}, устройств — {markers}","backup.full_done":"Копия восстановлена: пространств — {spaces}, комнат — {rooms}, устройств — {markers}","backup.undo_import":"Отменить последний полный импорт","backup.import_undone":"Полный импорт отменён"}};function Zo(t,e){if(e&&e in Jo)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function Xo(t,e,i){return Vi(Jo[t][e]??Yo[e]??e,i)}function Qo(t,e){const i=Jo[t][e]??Jo.en[e];return"string"==typeof i&&i.trim().length>0}class ta extends ht{constructor(){super(...arguments),this.deviceName="",this._metadataConnection=null,this._demoUntil=0,this._demoTimer=0,this._demoGeneration=1,this._reducedMotion=!1,this._onMotionChange=t=>{this._reducedMotion=t.matches,this.requestUpdate()},this._startDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"alarm"!==t.visual.status&&"none"===t.activity&&(this._demoGeneration++,this._demoUntil=Date.now()+3300,window.clearTimeout(this._demoTimer),this._demoTimer=window.setTimeout(()=>{this._demoUntil=0,this._demoTimer=0,this.requestUpdate()},3330),this.requestUpdate())}}connectedCallback(){super.connectedCallback(),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._ensureMetadata()}disconnectedCallback(){this._metadataRelease?.(),this._metadataRelease=void 0,this._metadataConnection=null,this._clearDemo(),this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,super.disconnectedCallback()}willUpdate(t){t.has("hass")&&this._ensureMetadata();const e=this.presentation,i=t.get("presentation");t.has("presentation")&&i?.binding!==e?.binding&&this._clearDemo(),e&&this._demoUntil>Date.now()&&("icon_ripple"!==e.display||"alarm"===e.visual.status||"none"!==e.activity)&&this._clearDemo()}_ensureMetadata(){const t=this.hass?.connection||null;t&&t!==this._metadataConnection&&(this._metadataRelease?.(),this._metadataConnection=t,this._metadataRelease=Uo(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0}get _lang(){return Zo(this.hass)}_t(t,e){return Xo(this._lang,t,e)}_providerText(t){return"houseplan"===t.domain?this._t("marker.preview.virtual_provider"):t.label.toLowerCase()===t.domain.toLowerCase()?t.domain:`${t.label} (${t.domain})`}_providers(){return this.presentation&&this.registry?function(t,e,i,s=Wo(t)){if("virtual"===e)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const n=e.indexOf(":");if(n<1)return[];const o=e.slice(0,n),a=e.slice(n+1);if("entity"===o){const e=Ko(t,a,i,s);return e?[e]:[]}if("device"!==o)return[];const r=i.devices?.[a]||t?.devices?.[a];if(!r)return[];const l=[r.config_entry_id,...Array.isArray(r.config_entries)?r.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const i=s.configEntries[e];return Go(t,i?.domain,"registry-owner",s,e)});if(l.some(Boolean))return Vo(l);const c=[];for(const[e,n]of Object.entries(i.entities||{}))n?.device_id===a&&null==n?.disabled_by&&c.push(Ko(t,e,i,s));return c.some(Boolean)?Vo(c):Vo((Array.isArray(r.identifiers)?r.identifiers:[]).map(e=>{const i=Array.isArray(e)?e[0]:null;return Go(t,i,"identifier-fallback",s)}))}(this.hass,this.presentation.binding,this.registry,Wo(this.hass)):[]}_providerSummary(t){if(!t.length)return this._t("marker.preview.unknown_provider");const e=t.slice(0,2).map(t=>this._providerText(t)),i=t.length-e.length;return e.join(", ")+(i>0?` · ${this._t("marker.preview.more_sources",{n:i})}`:"")}_sourceSummary(t){const e=t.visualSources;return e.length?1===e.length?`${e[0].name} · ${e[0].eid} · ${this._sourceProvider(e[0].eid)}`:2===e.length?e.map(t=>t.name).join(" · "):this._t("marker.preview.multiple_sources",{n:e.length}):this._t("marker.preview.no_source")}_stateSummary(t){if(!t.visualSources.length)return this._t("marker.preview.no_state");if(1===t.visualSources.length)return t.visualSources[0].stateText||this._t("marker.preview.no_state");const e=[...new Set(t.visualSources.map(t=>t.stateText).filter(Boolean))];return 1===e.length?e[0]:this._t("marker.preview.mixed_states")}_reason(t){return this._t(`marker.preview.reason.${t}`)}_sourceProvider(t){if(!this.registry)return this._t("marker.preview.unknown_provider");const e=Ko(this.hass,t,this.registry,Wo(this.hass));return e?this._providerText(e):this._t("marker.preview.unknown_provider")}render(){const t=this.presentation;if(!t)return G;const e=this._demoUntil>Date.now()&&"icon_ripple"===t.display&&"alarm"!==t.visual.status&&"none"===t.activity,i=e?function(t,e){if("icon_ripple"!==t.display)return t;const i={...t,activity:"event",activityGeneration:e,classes:[]};return{...i,classes:Oo(i)}}(t,this._demoGeneration):t,s=this._providers(),n="alarm"===t.visual.status||"none"!==t.activity,o=i.scale*("none"!==i.activity?i.rippleScale:1);let a=-o/2,r=o/2,l=-o/2,c=o/2;if(i.valueBadge){const t=i.scale*Math.min(4,Math.max(.9,.29*i.valueBadge.text.length+.28)),e=.7*i.scale,s=.6*i.scale;"right"===i.valueBadge.position&&(r=Math.max(r,s+t)),"left"===i.valueBadge.position&&(a=Math.min(a,-s-t)),"top"===i.valueBadge.position&&(l=Math.min(l,-s-e)),"bottom"===i.valueBadge.position&&(c=Math.max(c,s+e))}if(null!=i.lqiText){const t=i.scale*("bottom"===i.valueBadge?.position?1.55:.95);c=Math.max(c,t)}const h=Math.min(1,2.35/Math.max(1,r-a),2.35/Math.max(1,c-l)),d=Math.round(100*h),u=[`left:calc(50% - ${54*((a+r)/2)*h}px)`,`top:calc(50% - ${54*((l+c)/2)*h}px)`,h<1?`transform:scale(${h})`:""].filter(Boolean).join(";"),p=["dev",...i.classes,"virtual"===i.binding?"virtual":"",i.haDisabled?"ghost ha-disabled":"",null!=i.valueText?"valonly":""].filter(Boolean).join(" "),_=Lt(i).join(";"),m=e?this._t("marker.preview.demo_notice"):this._reason(t.explanation.reason),g=[this.deviceName,this._providerSummary(s),this._stateSummary(t),m,t.valueFullText||"",t.valueBadge?`${t.valueBadge.sourceLabel}: ${t.valueBadge.fullText}`:""].filter(Boolean).join(". ");return U`
${this._t("marker.preview.title")} @@ -3596,7 +3608,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="653755f6bd2ba4e51138f9c02b343a4b1ca2 ${this._t("marker.preview.demo_activity")} `:G} -
`}}Qo.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},Qo.styles=[Ot,o` +
`}}ta.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},ta.styles=[Ot,o` :host { display: block; min-width: 0; } .devicepreview { margin: 4px 0 14px; @@ -3662,13 +3674,13 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="653755f6bd2ba4e51138f9c02b343a4b1ca2 .previewstage { min-height: 142px; } .previewfacts > div { grid-template-columns: minmax(0, 1fr); gap: 2px; } } - `],customElements.get("hp-device-preview")||customElements.define("hp-device-preview",Qo);const ta=(t,e)=>[t[0]-e[0],t[1]-e[1]],ea=(t,e)=>[t[0]+e[0],t[1]+e[1]],ia=(t,e)=>t[0]*e[0]+t[1]*e[1],sa=t=>Math.hypot(t[0],t[1]);function na(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(r-o)*(t[1]-a)/(l-a)+o&&(i=!i)}return i}([r[0]+a[0]*l,r[1]+a[1]*l],t)&&(a=[-a[0],-a[1]]),a}function ra(t,e,i,s){const n=s||aa(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 la(t,e,i,s){const n=[],o=ta(i,e),a=sa(o);if(as||h>s)continue;const d=ia(ta(o,e),r),u=ia(ta(l,e),r),p=Math.max(0,Math.min(d,u)),_=Math.min(a,Math.max(d,u));_-p>s&&n.push([[e[0]+r[0]*p,e[1]+r[1]*p],[e[0]+r[0]*_,e[1]+r[1]*_]])}return n}function ca(t,e,i,s,n){const o=la(t,e,i,n);if(!o.length)return null;const a=t=>o.some(([e,i])=>oa(t,e,i)<=n),r=t.length,l=[];for(let o=0;on||f>n)continue;const v=ia(ta(e,c),p),y=ia(ta(i,c),p),b=Math.max(0,Math.min(v,y)),w=Math.min(u,Math.max(v,y));if(!(w-b<=n)){if(b>n&&bn&&wsa(ta(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],a=(t[0]-n[0])*(o[1]-n[1])-(t[1]-n[1])*(o[0]-n[0]),r=sa(ta(o,n))||1;return Math.abs(a)/r>e});return i.length>=3?i:t}function da(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-n[0])*r[1]-(t[1]-n[1])*r[0],c=t=>(t[0]-n[0])*r[0]+(t[1]-n[1])*r[1],h=i,d=a-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(a+(t-u)/g*(r-a));s=Math.min(s,f(_),f(m))}}return s}function pa(t,e,i){if(yi(t,e,i))return!0;if(vi(t,e,i)||vi(e,t,i))return!1;let s=0;try{const i=Je([[...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+=wi(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function _a(t,e,i,s,n){const o=[i.n[0]*s,i.n[1]*s],a={polys:{},openings:{},movedSpans:{}};if(Math.abs(s)<1e-9)return a;for(const e of t){if(e.id===i.roomId){a.polys[e.id]=ra(e.poly,i.edge,s,i.n),a.movedSpans[e.id]=[[ea(i.a,o),ea(i.b,o)]];continue}const t=la(e.poly,i.a,i.b,n);if(!t.length)continue;const r=ca(e.poly,i.a,i.b,o,n);r&&(a.polys[e.id]=r,a.movedSpans[e.id]=t.map(([t,e])=>[ea(t,o),ea(e,o)]))}for(const t of e)oa([t.x,t.y],i.a,i.b)<=n&&(a.openings[t.id]=[t.x+o[0],t.y+o[1]]);return a}function ma(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<=r+i)return!0}return!1}function ga(t,e,i){return t.filter(t=>e.some(e=>{for(let s=0;sr.polys[t.id]||t.poly;for(const e of l){const s=t.find(t=>t.id===e),n=r.polys[e];if(!da(n))return!1;const l=na(s.poly),h=na(n);if(Math.abs(h)t.find(t=>t.id===e).poly),d=t.map(c);for(const t of ga(e,h,2*a)){const e=r.openings[t.id];if(!ma(e?{...t,x:e[0],y:e[1]}:t,d,2*a))return!1}return!0}function va(t,e,i,s,n,o){const a=t=>[s[0]+(t[0]-s[0])*n,s[1]+(t[1]-s[1])*n],r={poly:t.poly.map(a),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=va(l,e,c,s,n,2*r),d=h.poly,u=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 ga(e,[l.poly],2*r)){const e=h.openings[t.id];if(!ma(e?{...t,x:e[0],y:e[1]}:t,p,2*r))return!1}return!0}function ba(t,e,i){const s=i/e;return wi(t)*s*s/1e4}function wa(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const ka=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],$a=t=>Math.min(1,Math.max(0,t));function xa(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=ka[ka.length-1][1];for(let t=1;t{const o=[t.x+n[0]*i*s,t.y+n[1]*i*s];return e.find(t=>t.poly.length>=3&&ci(o,t.poly))||null},a=o(1),r=o(-1);return a&&r?null:a||r?a?{normal:[-n[0],-n[1]],roomId:a.id}:{normal:n,roomId:r.id}:null}function Ma(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function Ca(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 Da(t,e){try{const i=Je([[...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 Ta(t,e,i,s,n,o,a){if(!(s>0))return[];const r=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=[-r[0],-r[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=Sa(i,t);if(!e||!Ma(e.normal,r,s))continue;const n=t.find(t=>t.id===e.roomId);if(!n)continue;const d=o&&o[e.roomId]||n.poly,u=i.angle*Math.PI/180,p=i.length/2,_=[-e.normal[0],-e.normal[1]],m=Math.max(0,a?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(u)*p,y=Math.sin(u)*p,b=[g-v,f-y],w=[g+v,f+y],k=c*i.length,$=Da(Ca(b,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:b,b:w,dir:l,len:k,normal:_,depth:k*x})}return h}function Pa(t){return Math.round(10*(Number(t)||0))/10}const Ra=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Aa(t,e){const i=Ra(e?.north_deg);return null!==i?i:Ra(t?.north_deg)}function Ea(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function Ia(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function za(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 Fa={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},Na=(t,e=1)=>{const i=Number(t);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):e},Oa=t=>{let e=Number(t);return Number.isFinite(e)?(e=(e%360+360)%360,e>180?e-360:e):0};function La(t,e,i){return Oa(e)?[t.x,t.y]:i([t.x,t.y])}const Ha=(t,e,i)=>{const s=Number(t),n=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&s>0?s/n*i:0},qa=(t,e,i)=>{const s=Number(t),n=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&i>0?s/i*n:0},Ba=(t,e,i,s=Fa.widthCm)=>{const n=Number(t?.width_cm);if(Number.isFinite(n)&&n>0)return n;const o=Number(t?.width);return Number.isFinite(o)&&o>0?qa(o,e,i):s};function Ua(t,e){return{color:t.color,opacity:Na(t.opacity),width_cm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),...e?{fill:t.fill,fill_color:t.fillColor,fill_opacity:Na(t.fillOpacity,.25)}:{}}}function Wa(t){const e=t.x+t.w/2,i=t.y+t.h/2,s=Oa(t.angle)*Math.PI/180,n=Math.cos(s),o=Math.sin(s),a=(t,s)=>{const a=t-e,r=s-i;return[e+a*n-r*o,i+a*o+r*n]};return[a(t.x,t.y),a(t.x+t.w,t.y),a(t.x+t.w,t.y+t.h),a(t.x,t.y+t.h)]}function ja(t){const e=Wa(t),i=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],s=[(e[0][0]+e[2][0])/2,(e[0][1]+e[2][1])/2];return{points:[...e,i(e[0],e[1]),i(e[1],e[2]),i(e[2],e[3]),i(e[3],e[0]),s],segments:e.map((t,i)=>({a:t,b:e[(i+1)%4]}))}}function Ga(t,e,i,s,n,o,a,r){const l=Oa(t.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l),d=-Math.sin(l),u=Math.cos(l),p=t.x+t.w/2,_=t.y+t.h/2,m=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,f=p+m*c+g*d,v=_+m*h+g*u,y=s-f,b=n-v;let w=(y*c+b*h)*(e>0?1:-1),k=(y*d+b*u)*(i>0?1:-1),$=null;if(o){const e=w/Math.max(t.w,r),i=k/Math.max(t.h,r);$=Math.max(r/Math.max(t.w,r),r/Math.max(t.h,r),e,i),w=t.w*$,k=t.h*$}if(a>0)if(o){const e=Math.max(1,Math.round(t.w/a)),i=Math.max(1,Math.round(t.h/a));if(Math.abs(t.w-e*a)<1e-6&&Math.abs(t.h-i*a)<1e-6){const s=(t,e)=>{let i=Math.abs(t),s=Math.abs(e);for(;s;)[i,s]=[s,i%s];return Math.max(1,i)},n=1/s(e,i),o=Math.max(r/t.w,r/t.h),a=Math.max(o,Math.round(($??1)/n)*n);w=t.w*a,k=t.h*a}else{t.w>=t.h?(w=Math.round(w/a)*a,k=w*(t.h/Math.max(t.w,r))):(k=Math.round(k/a)*a,w=k*(t.w/Math.max(t.h,r)))}}else w=Math.round(w/a)*a,k=Math.round(k/a)*a;w=Math.max(r,w),k=Math.max(r,k);return{x:f+(e>0?w/2:-w/2)*c+(i>0?k/2:-k/2)*d-w/2,y:v+(e>0?w/2:-w/2)*h+(i>0?k/2:-k/2)*u-k/2,w:w,h:k,angle:Oa(t.angle)||void 0}}const Va=(t,e,i)=>{const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;if(o<1e-12)return[...e];const a=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*n)/o));return[e[0]+s*a,e[1]+n*a]};const Ka=15;function Ya(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function Ja(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function Za(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function Xa(t,e,i){const s=Number(e)>0?Number(e):5;return Ja(t)/s*i}function Qa(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function tr(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 er(t,e,i){const s=Qa((t[0]+e[0])/2,i),n=Qa((t[1]+e[1])/2,i),[o,a]=tr(t,e);let r=Math.atan2(a,o);r<0&&(r+=Math.PI);const l=Math.round(1800*r)/1800,c=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(c)},${n.toFixed(c)}@${l.toFixed(4)}`}function ir(t,e,i,s){return 1===s?er(t,e,i):er([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function sr(t,e){if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return null;const i=[Number(t.a[0]),Number(t.a[1]),Number(t.b[0]),Number(t.b[1])];if(!i.every(Number.isFinite))return null;const s=e>0?e:1;return[[i[0]*s,i[1]*s],[i[2]*s,i[3]*s]]}function nr(t,e,i,s,n){const o=n>0?n:1;return{key:ir(t,e,s,o),cm:Ja(i),a:[t[0]/o,t[1]/o],b:[e[0]/o,e[1]/o]}}function or(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),a=Number(e.key.slice(t+1));[n,o,a].every(Number.isFinite)&&s.push({w:e,x:n*i,y:o*i,ang:a})}return s}function ar(t,e){const[i,s]=tr(t,e);let n=Math.atan2(s,i);return n<0&&(n+=Math.PI),n}function rr(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function lr(t,e,i,s,n=1){if(!t?.length)return null;const o=ir(e,i,s,n),a=t.find(t=>t.key===o);if(a)return a;const r=n>0?n:1,l=(e[0]+i[0])/2,c=(e[1]+i[1])/2,h=ar(e,i),d=Math.max(.5*s,1e-9)*r;for(const e of or(t,r))if(rr(e.ang,h)&&Math.hypot(e.x-l,e.y-c)<=d)return e.w;return null}function cr(t,e,i,s,n=1){const o=lr(t,e,i,s,n);return o&&o.cm>0?Ja(o.cm):0}function hr(t,e,i,s=1,n=[]){if(!t?.length)return[];const o=new Set,a=ni(e);for(const t of a)o.add(ir([t[0],t[1]],[t[2],t[3]],i,s));const r=e||[],l=Math.max(i*s*.02,1e-9);for(let t=0;t(o.has(t.key)||(t=>{const e=sr(t,s);if(!e)return!1;const[i,o]=e,r=o[0]-i[0],c=o[1]-i[1],h=Math.hypot(r,c);if(h<=l)return!1;if(!a.some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];return rr(ar(i,o),ar(e,s))&&Pr(i[0],i[1],e[0],e[1],s[0],s[1])<=l&&Pr(o[0],o[1],e[0],e[1],s[0],s[1])<=l}))return!1;const d=(n||[]).some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];if(!rr(ar(i,o),ar(e,s)))return!1;const n=t=>Math.abs((t[0]-i[0])*c-(t[1]-i[1])*r)/h;if(n(e)>l||n(s)>l)return!1;const a=h*h,d=((e[0]-i[0])*r+(e[1]-i[1])*c)/a,u=((s[0]-i[0])*r+(s[1]-i[1])*c)/a;return Math.min(1,Math.max(d,u))-Math.max(0,Math.min(d,u))>l/h});return!d})(t))&&t.cm>=1&&t.cm<=100)}function dr(t,e,i,s=8){const[n,o]=tr(t,e);let a=Math.atan2(o,n);a<0&&(a+=Math.PI);let r=i*Math.PI/180%Math.PI;r<0&&(r+=Math.PI);let l=Math.abs(a-r);return l>Math.PI/2&&(l=Math.PI-l),l<=s*Math.PI/180}function ur(t,e,i,s,n=1){if(!t?.length)return[];if(e.length!==i.length)return t.slice();const o=new Map;for(let t=0;t0?n:1,r=Math.max(.5*s,1e-9)*a,l=new Set,c=[];for(const n of t){let t="",h=null;const d=sr(n,a);if(d)for(let n=0;nr||Pr(d[1][0],d[1][1],o[0],o[1],l[0],l[1])>r)continue;const p=l[0]-o[0],_=l[1]-o[1],m=p*p+_*_;if(m<1e-18)continue;const g=t=>{const e=Math.max(0,Math.min(1,((t[0]-o[0])*p+(t[1]-o[1])*_)/m));return[c[0]+(u[0]-c[0])*e,c[1]+(u[1]-c[1])*e]};h=[g(d[0]),g(d[1])],t=ir(h[0],h[1],s,a);break}if(d||(t=o.get(n.key)||""),!t){const o=or([n],a)[0];if(o)for(let n=0;n1.000001)continue;if(Pr(o.x,o.y,l[0],l[1],c[0],c[1])>r)continue;const g=h[0]+(d[0]-h[0])*Math.max(0,Math.min(1,m)),f=h[1]+(d[1]-h[1])*Math.max(0,Math.min(1,m)),[v,y]=tr(h,d),b=Math.max(s*a,1e-6);t=ir([g-v*b,f-y*b],[g+v*b,f+y*b],s,a);break}}t||(t=n.key),l.has(t)||(l.add(t),c.push(h?nr(h[0],h[1],n.cm,s,a):{...n,key:t,cm:Ja(n.cm)}))}return c}function pr(t,e,i,s,n,o=1){const a=ir(e,i,n,o),r=(t||[]).filter(t=>t.key!==a);return null==s||s<1?r:[...r,nr(e,i,s,n,o)]}function _r(t,e,i,s,n,o=[],a=1){let r=t?t.slice():[];for(const t of function(t,e,i,s,n=1,o=[]){const a=xr(t,e,i,s,n,o);if(!a)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=zr(i,t),n=wr(i,t);if(s&&n)return`${kr(s)} ${kr($r(n))}`}}let s="";for(let i=0;i.05)&&Pr(a,r,t[0],t[1],t[2],t[3])<=o)return!0}return!1}function fr(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function vr(t,e){const i=t[e],s=t[(e+1)%t.length],n=s[0]-i[0],o=s[1]-i[1],a=Math.hypot(n,o)||1;let r=-o/a,l=n/a;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]<(r-o)*(t[1]-a)/(l-a+0)+o&&(i=!i)}return i}([c[0]+.001*r,c[1]+.001*l],t)||(r=-r,l=-l),[r,l]}function yr(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 br(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]],a=(o[0]*s[1]-o[1]*s[0])/n;return[t[0]+a*e[0],t[1]+a*e[1]]}function wr(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(yr(b,w)){const e=t[n],i=[e[0]+u*h,e[1]+p*h],o=[e[0]+_*d,e[1]+m*d];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=br(k,b,$,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]+u*h,t[n][1]+p*h]),d>0&&s.push([t[n][0]+_*d,t[n][1]+m*d]),h>0||d>0||s.push([t[n][0],t[n][1]])}return s.length>=3?s:null}function kr(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),r=ii(a);if(!r||r.length<3)return null;const l=fr(s,n),c=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=ii(i);if(t)for(const e of fs(r,t,l))c.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])c.push([t[0],t[1]],[t[2],t[3]]);for(const t of o||[]){const e=sr(t,n);e&&c.push(e[0],e[1])}const h=[],d=[];for(let t=0;tl)continue;const a=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);a<=n||a>=1-n||(o.some(t=>Math.abs(t-a)*s<=2*l)||o.push(a))}o.sort((t,e)=>t-e);for(const s of o)h.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),d.push(t)}return{poly:h,parent:d,orig:r}}function Sr(t,e,i,s){const n=e.poly.length,o=new Array(n).fill(0);if(!t?.length)return o;const a=new Set,r=[];for(let l=0;l0?(o[l]=Ja(c.cm),a.add(c.key)):r.push(l)}if(!r.length)return o;const l=s>0?s:1,c=Math.max(.5*i,1e-9)*l,h=or(t,l).filter(t=>t.w.cm>0),d=new Map;for(const t of r){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],r=ar(s,n),d=(s[0]+n[0])/2,u=(s[1]+n[1])/2;let p=null;const _=Math.hypot(n[0]-s[0],n[1]-s[1]);for(const t of h){if(a.has(t.w.key))continue;if(!rr(t.ang,r))continue;const e=sr(t.w,l);let i=!1,o=0;if(e){if(!rr(ar(e[0],e[1]),r))continue;if(Pr(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>c||Pr(n[0],n[1],e[0][0],e[0][1],e[1][0],e[1][1])>c)continue;i=!0,o=Math.max(0,Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1])-_)}else{if(Pr(t.x,t.y,s[0],s[1],n[0],n[1])>c)continue;o=Math.hypot(t.x-d,t.y-u)}(!p||i&&!p.exact||i===p.exact&&ot?.id===e),n=ii(s);if(!n)return[];const o=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=ii(s);if(t)for(const e of fs(n,t,i))o.push(e)}return o}(t,e,fr(n,r)),h=function(t,e,i,s,n){const o=fr(s,n),a=[];for(let r=0;rPr(h,d,t[0],t[1],t[2],t[3])<=o);a.push(u?"shared":"outer")}return a}(l.poly,c,s,n,r),d=Sr(i,l,n,r),u=d.map((t,e)=>h[e]&&t>0?Xa(t,o,a)/2:0);return{...l,kinds:h,cms:d,offsets:u}}function Cr(t,e,i,s,n,o,a=1){const r=[];for(const l of t||[]){if(!l?.id)continue;const c=Mr(t,l.id,e,i,s,n,o,a);if(c)for(let t=0;t0&&!l.has(c.key)&&(l.add(c.key),r.push(c));const c=[];for(const r of t||[]){if(!r?.id)continue;const l=Mr(t,r.id,e,i,s,n,o,a);if(l)for(let t=0;t0)||null===l.kinds[i]){t++;continue}let o=t;for(;o+10&&c.push({a:[h[0],h[1]],b:[d[0],d[1]],key:ir(h,d,s,a),cm:n,len:u}),t=o+1}}}c.sort((t,e)=>e.len-t.len||t.key.localeCompare(e.key));const h=[],d=new Set,u=new Set,p=4*fr(s,a);for(const t of c){const e=r.filter(e=>!u.has(e.key)&&e.cm===t.cm&&rr(ar(e.a,e.b),ar(t.a,t.b))&&Pr(e.a[0],e.a[1],t.a[0],t.a[1],t.b[0],t.b[1])<=p&&Pr(e.b[0],e.b[1],t.a[0],t.a[1],t.b[0],t.b[1])<=p);if(e.length){for(const t of e)u.add(t.key);d.has(t.key)||(d.add(t.key),h.push(nr(t.a,t.b,t.cm,s,a)))}}for(const t of r)u.has(t.key)||d.has(t.key)||(d.add(t.key),h.push(nr(t.a,t.b,t.cm,s,a)));return h}function Tr(t,e,i,s,n,o,a,r=1){const l=fr(n,r),c=(s[0]+s[2])/2,h=(s[1]+s[3])/2,d=ar([s[0],s[1]],[s[2],s[3]]);let u=null;for(const s of Cr(t,e,i,n,o,a,r)){if(!rr(ar(s.a,s.b),d))continue;const t=Pr(c,h,s.a[0],s.a[1],s.b[0],s.b[1]);t>4*l||(!u||tt?.id===e),c=ii(l);if(!c||c.length<3)return null;if(!i?.length)return c.map(t=>[t[0],t[1]]);const h=Mr(t,e,i,s,n,o,a,r);return h&&h.offsets.some(t=>t>0)&&wr(h.poly,h.offsets)||c.map(t=>[t[0],t[1]])}function Ar(t){const e=t.map(t=>[t[0],t[1]]);return e.push([t[0][0],t[0][1]]),[e]}function Er(t,e,i,s=[],n,o,a,r=1,l=[]){if(!e?.length&&!l.length)return null;const c=[];let h=0;for(const s of t||[]){if(!s?.id)continue;const l=Mr(t,s.id,e,i,n,o,a,r);if(!l||l.poly.length<3||!l.offsets.some(t=>t>0))continue;for(const t of l.offsets)t>0&&(h=Math.max(h,2*t));const d=zr(l.poly,l.offsets),u=wr(l.poly,l.offsets);d&&c.push({outset:d,inset:u})}for(const t of l){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);if(e.length){const s=Math.min(Math.max(...e)-Math.min(...e),Math.max(...i)-Math.min(...i)),n=Math.min(...t.map((e,i)=>{const s=t[(i+1)%t.length];return Math.hypot(s[0]-e[0],s[1]-e[1])}));h=Math.max(h,t.length>16?s:n)}}const d=function(t,e,i,s,n,o,a){if(!e?.length||!i?.length)return[];const r=4*fr(s,a),l=new Map;for(const r of Cr(t,e,i,s,n,o,a))!r.open&&r.half>0&&!l.has(r.key)&&l.set(r.key,r);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])<=r)||h.push(e);const d=[],u=(t,e)=>{let i=0,s=0;if(Math.hypot(t.a[0]-e[0],t.a[1]-e[1])<=r)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])<=r))return null;i=t.a[0]-t.b[0],s=t.a[1]-t.b[1]}const n=Math.hypot(i,s);return n>r?[i/n,s/n]:null};for(const t of h){const e=c.map(e=>({iv:e,u:u(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(a>0?[t.slice(),h,p,u]:[t.slice(),u,p,h])}}return d}(t,e,i,n,o,a,r),u=s.length?Nr(t,e,i,n,o,a,r):null;try{const t=t=>{const e=Ar(t.outset);return t.inset?Ze(e,Ar(t.inset)):e};let e=c.length?t(c[0]):null;for(let i=1;i0))continue;const i=Hr(u,t,!0);if(!i.negative&&!i.positive)continue;const s=t.angle*Math.PI/180,o=Math.cos(s),a=Math.sin(s),l=-a,c=o,d=t.length/2,p=1.25*Math.max(h,n*r),_=[[t.x-o*d-l*p,t.y-a*d-c*p],[t.x+o*d-l*p,t.y+a*d-c*p],[t.x+o*d+l*p,t.y+a*d+c*p],[t.x-o*d+l*p,t.y-a*d+c*p]];e&&(e=Ze(e,Ar(_)))}for(const t of l)t.length<3||(e=e?Ye(e,Ar(t)):[Ar(t)]);return{geom:e||[],depthUnits:h}}catch{return null}}function Ir(t,e,i,s=[],n,o,a,r=1,l=[]){if(!e?.length&&!l.length)return null;const c=Er(t,e,i,s,n,o,a,r,l),h=c?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?" ":"")+kr(i.map(t=>[t[0],t[1]])))}return e}(c.geom):"";if(c&&h)return{d:h,depthUnits:c.depthUnits,fillRule:"evenodd"};if(c)return null;const d=function(t,e,i,s,n,o,a=1){if(!e?.length)return[];const r=[];for(const l of t||[]){if(!l?.id)continue;const c=Mr(t,l.id,e,i,s,n,o,a);if(!c||c.poly.length<3||!c.offsets.some(t=>t>0))continue;const h=zr(c.poly,c.offsets),d=wr(c.poly,c.offsets);if(!h||!d)continue;const u=`${kr(h)} ${kr($r(d))}`;let p="",_="outer",m=0,g=0;for(let t=0;t0){p=ir(c.poly[t],c.poly[(t+1)%c.poly.length],s,a),_=c.kinds[t]||"outer",m=c.cms[t],g=Xa(m,n,o);break}r.push({d:u,key:p,kind:_,cm:m,depthUnits:g})}return r}(t,e,i,n,o,a,r),u=l.map(t=>kr(t)).join(" ");if(!d.length&&!u)return null;let p=0;for(const t of d)p=Math.max(p,t.depthUnits);return{d:[d.map(t=>t.d).join(" "),u].filter(Boolean).join(" "),depthUnits:p,fillRule:"nonzero"}}function zr(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]]);$r(t),e.slice().reverse();const s=[];for(let n=0;n0||r>0)){s.push([t[n][0],t[n][1]]);continue}if(yr(b,w)){const e=t[n],i=[e[0]-l*a,e[1]-c*a],o=[e[0]-h*r,e[1]-d*r];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=br(k,b,$,w),S=Math.max(a,r,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}a>0&&s.push([t[n][0]-l*a,t[n][1]-c*a]),r>0&&s.push([t[n][0]-h*r,t[n][1]-d*r])}return s.length>=3?s:null}function Fr(t,e,i,s,n,o,a=1){const r=[];for(const l of t||[]){const c=ii(l);if(c&&c.length>=3){const h=Mr(t,l.id,e,i,s,n,o,a),d=h&&h.offsets.some(t=>t>0)?zr(h.poly,h.offsets):null,u=d||c;r.push({poly:u.map(t=>t.join(",")).join(" ")})}else l&&null!=l.x&&null!=l.y&&null!=l.w&&null!=l.h&&r.push({rect:{x:l.x,y:l.y,w:l.w,h:l.h,rx:.03*Math.min(l.w,l.h)}})}return r}function Nr(t,e,i,s,n,o,a=1){const r=[];for(const l of t||[]){if(!l?.id)continue;const c=Mr(t,l.id,e,i,s,n,o,a);if(!c)continue;const h=Math.abs(wi(c.poly));for(let t=0;t[Math.max(e,t.x0),Math.min(i,t.x1)]).filter(t=>t[1]-t[0]>s).sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(!n.length)return{coverage:0,full:!1};let o=n[0][0],a=n[0][1],r=0,l=o<=e+s;for(let t=1;t=i-s,{coverage:r,full:l}}function Lr(t,e){return Number(e.full)-Number(t.full)||t.faceDistance-e.faceDistance||t.area-e.area||t.roomId.localeCompare(e.roomId)}function Hr(t,e,i=!1){const s=Number(e?.x),n=Number(e?.y),o=Number(e?.angle),a=Number(e?.length);if(!([s,n,o,a].every(Number.isFinite)&&a>0))return{negative:null,positive:null};const r=o*Math.PI/180,l=Math.cos(r),c=Math.sin(r),h=-c,d=l,u=a/2,p=Math.max(1e-9,t.adjacencyEps),_=new Map;let m=0;for(const e of t.edges){if(i&&!(e.half>0))continue;if(!dr(e.a,e.b,o))continue;const[t,a]=tr(e.a,e.b);if(Math.abs((s-e.a[0])*a-(n-e.a[1])*t)>p)continue;const r=(e.a[0]-s)*l+(e.a[1]-n)*c,g=(e.b[0]-s)*l+(e.b[1]-n)*c,f=Math.max(-u,Math.min(r,g)),v=Math.min(u,Math.max(r,g));if(v-f<=p)continue;const y=e.inward[0]*h+e.inward[1]*d>=0?1:-1,b=((e.a[0]+e.b[0])/2-s)*h+((e.a[1]+e.b[1])/2-n)*d,w=Math.abs(b+y*e.half),k=`${y}|${e.roomId}`,$={x0:f,x1:v,half:e.half,cm:e.cm,key:e.key,axis:[t,a]},x=_.get(k);x?(x.pieces.push($),x.faceDistance=Math.min(x.faceDistance,w)):_.set(k,{roomId:e.roomId,side:y,order:m++,pieces:[$],faceDistance:w,area:e.area,coverage:0,full:!1})}for(const t of _.values()){const e=Or(t.pieces,-u,u,p);t.coverage=e.coverage,t.full=e.full}const g=t=>{const e=[..._.values()].filter(e=>e.side===t&&e.coverage>p);return e.sort(Lr),e[0]||null};return{negative:g(-1),positive:g(1)}}function qr(t,e){const i=Hr(t,e),s=[i.negative,i.positive].filter(t=>!!t).sort((t,e)=>t.order-e.order);if(!s.length)return{ox:0,oy:0,cm:0,side:-1};const n=s[0],o=e.flip_v?-n.side:n.side,a=(-1===o?i.negative:i.positive)||n,r=[...a.pieces].sort((t,e)=>(t.x0<=0&&t.x1>=0?0:Math.min(Math.abs(t.x0),Math.abs(t.x1)))-(e.x0<=0&&e.x1>=0?0:Math.min(Math.abs(e.x0),Math.abs(e.x1)))||e.x1-e.x0-(t.x1-t.x0)||t.key.localeCompare(e.key))[0];if(!(r.half>0&&r.cm>0))return{ox:0,oy:0,cm:0,side:o};const l=e.angle*Math.PI/180,c=-Math.sin(l),h=Math.cos(l);return{ox:c*o*r.half,oy:h*o*r.half,cm:r.cm,side:o}}function Br(t,e){const i=1e-9,s=e.filter(t=>Number.isFinite(t.x0)&&Number.isFinite(t.x1)&&Number.isFinite(t.half)&&t.x1>t.x0&&t.half>0);if(!s.length)return"";const n=s.flatMap(t=>[t.x0,t.x1]).sort((t,e)=>t-e),o=[];for(const t of n){const e=o[o.length-1];(void 0===e||t>e+i)&&o.push(t)}const a=[];for(let t=0;t+1e+i))continue;const r=(e+n)/2,l=s.reduce((t,e)=>r>=e.x0-i&&r<=e.x1+i?Math.max(t,e.half):t,0);if(!(l>0))continue;const c=a[a.length-1];c&&e<=c.x1+i&&Math.abs(l-c.half)<=i?c.x1=n:a.push({x0:e,x1:n,half:l})}const r=[];for(const t of a){const e=r[r.length-1],s=e?.[e.length-1];s&&t.x0<=s.x1+i?(t.x0=s.x1,e.push(t)):r.push([t])}return r.map(e=>{const i=e[0],s=e[e.length-1],n=Math.min(.02*Math.min(...e.map(t=>t.half)),.05),o=-t*n,a=[];if(1===t){a.push(`M ${i.x0} ${o} L ${s.x1} ${o}`);for(let t=e.length-1;t>=0;t--){const i=e[t];a.push(`L ${i.x1} ${i.half} L ${i.x0} ${i.half}`)}}else{a.push(`M ${s.x1} ${o} L ${i.x0} ${o}`);for(const t of e)a.push(`L ${t.x0} ${-t.half} L ${t.x1} ${-t.half}`)}return a.push("Z"),a.join(" ")}).join(" ")}function Ur(t,e,i,s){if(!s)return i;const n=t.angle*Math.PI/180,o=Math.cos(n),a=Math.sin(n),r=[],l=1e-9;for(const n of i){const[i,c]=n.axis,h=o*i+a*c;if(Math.abs(h)<=l)continue;const d=t.x*i+t.y*c,u=d+h*n.x0,p=d+h*n.x1,_=Math.min(u,p),m=Math.max(u,p),g=`${n.key}|${e}`,f=s.get(g)||[];let v=[[_,m]];for(const[t,e]of f){const i=[];for(const[s,n]of v)e<=s+l||t>=n-l?i.push([s,n]):(t>s+l&&i.push([s,Math.min(n,t)]),et[0]-e[0]||t[1]-e[1]),b=[];for(const t of y){const e=b[b.length-1];e&&t[0]<=e[1]+l?e[1]=Math.max(e[1],t[1]):b.push([t[0],t[1]])}s.set(g,b)}return r}function Wr(t,e){const i=new Map;return e.map(e=>function(t,e,i){const s=Hr(t,e,!0),n=s.negative,o=s.positive;if(!n&&!o)return null;let a;if(n&&o)a=[{candidate:n,side:-1},{candidate:o,side:1}];else{const t=n||o;a=[{candidate:t,side:-1},{candidate:t,side:1}]}const r=a.map(({candidate:t,side:s})=>({candidate:t,side:s,pieces:Ur(e,s,t.pieces,i)})),l=r.map(({candidate:t,side:e,pieces:i})=>({side:e,roomId:t.roomId,d:Br(e,i)})),c=r.flatMap(({pieces:t})=>t);if(!c.length)return null;const h=Math.max(...c.map(t=>t.half));return{faces:l,minY:-h,maxY:h,wallKey:[...new Set(c.map(t=>t.key))].sort().join("|")}}(t,e,i))}const jr=150;function Gr(t){return Number.isFinite(t)?Math.max(1,Math.min(jr,t)):1}function Vr(t){return((Number.isFinite(Number(t))?Number(t):0)%90+90)%90}const Kr=t=>{const e=t.map(t=>[t[0],t[1]]);return!e.length||e[0][0]===e[e.length-1][0]&&e[0][1]===e[e.length-1][1]||e.push([...e[0]]),[e]};function Yr(t){const e=[];for(const i of t||[])for(const t of i||[]){const i=(t||[]).filter(t=>Array.isArray(t)&&t.length>=2);i.length<4||e.push(`M ${i.slice(0,-1).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`)}return e.join(" ")}function Jr(t,e,i,s,n){const o=e[0]-t[0],a=e[1]-t[1],r=Math.hypot(o,a);if(!(r>1e-9))return null;const l=Xa(i,s,n)/2,c=-a/r*l,h=o/r*l;return[[t[0]+c,t[1]+h],[e[0]+c,e[1]+h],[e[0]-c,e[1]-h],[t[0]-c,t[1]-h]]}function Zr(t,e,i){const s=Number(e)>0?Number(e):5,n=Gr(t.cm)/s*i,o=t.center[0],a=t.center[1];if("circle"===t.shape){const t=n/2;return Array.from({length:96},(e,i)=>{const s=i/96*Math.PI*2;return[o+Math.cos(s)*t,a+Math.sin(s)*t]})}const r=n/2,l=Vr(t.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l);return[[-r,-r],[r,-r],[r,r],[-r,r]].map(([t,e])=>[o+t*c-e*h,a+t*h+e*c])}function Xr(t,e,i){const s=[];for(let n=0;n+1t.length>=3).map(t=>Kr(t));return e.length?Ye(e[0],...e.slice(1)):null}catch{return null}}const tl=t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`;function el(t,e){const i=Qr(t.filter(t=>t.length>=3)),s=Qr(e.filter(t=>t.length>=3));if(!i)return[];if(!s)return[];try{return function(t){const e=[];for(const i of t||[]){if(sl([i])<=1e-6)continue;const t=[];for(const e of i||[]){const i=(e||[]).filter(t=>Array.isArray(t)&&t.length>=2);i.length<4||t.push(tl(i.slice(0,-1)))}t.length&&e.push(t.join(" "))}return e}(Je(i,s))}catch{return[]}}function il(t,e){if(!e.length)return[Kr(t)];try{const i=Qr(e);if(i)return Ze(Kr(t),i)}catch{}let i=[Kr(t)];for(const t of e)if(!(t.length<3))try{i=Ze(i,Kr(t))}catch{}return i}function sl(t){let e=0;for(const i of t||[])if(i?.length){e+=wi(i[0]||[]);for(let t=1;t=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]))}return e}function ol(t,e){let i=!1;for(let s=0,n=e.length-1;st[1]!=l>t[1]&&t[0]<(r-o)*(t[1]-a)/(l-a||1e-12)+o&&(i=!i)}return i}function al(t,e){for(const i of e||[]){const e=i?.[0];if(!e?.length||!ol(t,e))continue;let s=!1;for(let e=1;ei)return!1;if(Math.abs(Gr(t.cm)-Gr(e.cm))>1e-6)return!1;if(t.shape!==e.shape)return!0;if("circle"===t.shape||"circle"===e.shape)return!0;const s=Math.abs(Vr(t.angle)-Vr(e.angle));return Math.min(s,90-s)<=1e-6}const ll=1e3;function cl(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 hl=.01,dl=100;function ul(t,e=1e3){const i=cl(t?.plan_aspect,e),s=Number(t?.plan_scale),n=Number.isFinite(s)&&s>0?Math.min(dl,Math.max(hl,s)):1,o=Number(t?.plan_scale_x),a=Number(t?.plan_scale_y),r=Number.isFinite(o)&&o>0?Math.min(dl,Math.max(hl,o)):n,l=Number.isFinite(a)&&a>0?Math.min(dl,Math.max(hl,a)):n,c=Number(t?.plan_x),h=Number(t?.plan_y),d=Oa(t?.plan_angle);return{x:i.x+(Number.isFinite(c)?$l(c):0)*e,y:i.y+(Number.isFinite(h)?$l(h):0)*e,w:i.w*r,h:i.h*l,...d?{angle:d}:{}}}function pl(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 _l(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=ll,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]*ll,i[1]*e,i[2]*ll,i[3]*e],bg:t.plan_url?{href:$s(t.plan_url),...ul(t,ll)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...pl(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*ll:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*ll:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*ll,t[1]*e]):void 0}}),room_drafts:(t.room_drafts||[]).map(t=>({id:t.id,points:(t.points||[]).map(t=>[t[0]*ll,t[1]*e]),segments:(t.segments||[]).map(t=>({cm:Number(t.cm)}))})),partitions:(t.partitions||[]).map(t=>({id:t.id,a:[t.a[0]*ll,t.a[1]*e],b:[t.b[0]*ll,t.b[1]*e],cm:Number(t.cm)})),wall_columns:(t.wall_columns||[]).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[t.center[0]*ll,t.center[1]*e],cm:Number(t.cm),..."circle"===t.shape?{}:{angle:Vr(t.angle)}}))}}):[]}const ml=5e3,gl=ml*ll,fl=240,vl=ll/fl,yl=1/fl;function bl(t){if(!Number.isFinite(t))return t;const e=Math.round(t*fl/ll)*ll/fl;return Math.abs(e-t)<=1e-9*vl?t:e}function wl(t){return{x:bl(t.x),y:bl(t.y)}}function kl(t){return Number.isFinite(t)?Math.min(gl,Math.max(-gl,t)):0}function $l(t){return Number.isFinite(t)?Math.min(ml,Math.max(-ml,t)):0}const xl=1/3,Sl=200;function Ml(t){let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t){const t=Number(o[0]),a=Number(o[1]);Number.isFinite(t)&&Number.isFinite(a)&&(ts&&(s=t),a>n&&(n=a))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:n}}function Cl(t){return t.poly&&t.poly.length?Ml(t.poly):null==t.x||null==t.y?null:Ml([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function Dl(t,e){const i=[];for(const e of t.rooms||[]){const t=Cl(e);t&&i.push(t)}if(t.bg){const e=Ml(Wa(t.bg));e&&i.push(e)}for(const t of e||[])if(Array.isArray(t)){const e=Ml([t]);e&&i.push(e)}else i.push(t);return i}const Tl=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},Pl=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 Rl(t,e){let{x:i,y:s,w:n,h:o}=t;n<30&&(i=i+n/2-100,n=Sl),o<30&&(s=s+o/2-100,o=Sl);const a=Math.max(n,o)*e;return{x:i-a,y:s-a,w:n+2*a,h:o+2*a}}function Al(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)<=gl&&Math.abs(t.maxX)<=gl&&Math.abs(t.minY)<=gl&&Math.abs(t.maxY)<=gl);if(!o.length)return{core:null,all:null,outliers:0};const a=Pl(o);if(o.length<4){const t=Rl(a,i);return{core:t,all:t,outliers:0}}const r=o.map(t=>(t.minX+t.maxX)/2),l=o.map(t=>(t.minY+t.maxY)/2),c=Tl(r),h=Tl(l),d=o.map((t,e)=>Math.max(Math.abs(r[e]-c),Math.abs(l[e]-h))),u=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),p=d.map(t=>t>s*u),_=p.filter(Boolean).length,m=_&&_<=o.length*xl?o.filter((t,e)=>!p[e]):o;return{core:Rl(Pl(m)||a,i),all:Rl(a,i),outliers:m===o?0:_}}function El(t,e,i=.05){const s=Al(Dl(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,ll,ll];return{x:n[0],y:n[1],w:n[2],h:n[3]}}function Il(t){const e=El(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function zl(t){const e=[];for(const i of t.rooms||[]){const t=Cl(i);t&&e.push(t)}const i=Al(e,{pad:0}).core;return i?Math.max(ll,Math.min(gl,Math.max(i.w,i.h))):ll}function Fl(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*zl(e)*o/n}const Nl=[1,2,5,10,20,50,100,200,500,1e3];function Ol(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 Ll(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 Hl=["furniture","appliance","sanitary","other"],ql=[{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]]}],Bl=new Map(ql.map(t=>[t.id,t]));function Ul(t){return t&&Bl.get(t)||null}function Wl(t){return ql.filter(e=>e.group===t)}function jl(t,e,i=vl,s=1e3){const n=Number(e)>0?Number(e):5;return(Number(t)||0)/n*i/s}const Gl=5e-4,Vl=ml;function Kl(t){return Number.isFinite(t)?Math.max(Gl,Math.min(Vl,t)):Gl}const Yl=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function Jl(t,e,i){const s=Ul(t);if(!(s&&e>0&&i>0))return"";const n=t=>Yl(t*e),o=t=>Yl(t*i),a=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,r]=t;a.push(`M${n(e)} ${o(i)}H${n(e+s)}V${o(i+r)}H${n(e)}Z`)}else if("l"===t[0]){const[,e,i,s,r]=t;a.push(`M${n(e)} ${o(i)}L${n(s)} ${o(r)}`)}else if("e"===t[0]){const[,s,r,l,c]=t;a.push(`M${n(s-l)} ${o(r)}A${Yl(l*e)} ${Yl(c*i)} 0 0 1 ${n(s+l)} ${o(r)}A${Yl(l*e)} ${Yl(c*i)} 0 0 1 ${n(s-l)} ${o(r)}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 Xl(t,e,i,s,n,o=0){let a=null,r=n;for(const n of s){const[s,l,c,h]=n,d=c-s,u=h-l,p=d*d+u*u;if(!p)continue;const _=Math.sqrt(p);let m=((t-s)*d+(e-l)*u)/p;m=Math.max(0,Math.min(1,m));let g=s+m*d,f=l+m*u;const v=Math.hypot(t-g,e-f);if(!(v0){const t=Math.round(m*_/o)*o;g=s+t/_*d,f=l+t/_*u}a={cx:g+y*(i/2),cy:f+b*(i/2),angle:Zl(180*Math.atan2(-y,b)/Math.PI),dist:v}}return a}function Ql(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function tc(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function ec(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function ic(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!ec(t.a)||!ec(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 sc(t,e){const i=e[0],s=e[1],n=e[2]-i,o=e[3]-s,a=n*n+o*o;if(a<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let r=((t[0]-i)*n+(t[1]-s)*o)/a;r=Math.max(0,Math.min(1,r));const l=[i+r*n,s+r*o];return{q:l,t:r,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function nc(t,e){return sc(t,e).q}function oc(t,e,i,s,n){const o=sc(t,e).q;let a=o,r=1/0;for(const t of i){const e=Math.hypot(o[0]-t[0],o[1]-t[1]);e<=n&&e0?s:1,m=Math.round(p/_)*_,g=Math.max(0,Math.min(u,m))/u;return[l+h*g,c+d*g]}function ac(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt?.id),a=ic(e);if(a.length)return mc(a,t,i,s).map(t=>tc(t,i));if(!n)return[];const r=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;tQl([t[0],t[1]],[t[2],t[3]],e))}function cc(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])),a=(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;tTs(i,t)<4*s)){a(n[t].id,n[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}function hc(t,e,i,s=0){const n=e[2]-e[0],o=e[3]-e[1],a=Math.hypot(n,o);if(!(a>1e-9))return null;const r=n/a,l=o/a,c=(t[0]-e[0])*r+(t[1]-e[1])*l;if(c<-s||c>a+s)return null;return Math.abs((t[0]-e[0])*l-(t[1]-e[1])*r)>i?null:Ts(t,e)}function dc(t,e,i){const s=t[2]-t[0],n=t[3]-t[1],o=e[2]-e[0],a=e[3]-e[1],r=Math.hypot(s,n),l=Math.hypot(o,a);return r>1e-9&&l>1e-9&&(!(Math.abs(s/r*(a/l)-n/r*(o/l))>1e-6)&&(Ts([e[0],e[1]],t)<=4*i||Ts([t[0],t[1]],e)<=4*i))}function uc(t,e,i){if(!t.length)return null;t.sort((t,e)=>t.distance-e.distance);const s=t[0];for(let n=1;ne)break;if(o.semantic!==s.semantic||!dc(s.seg,o.seg,i))return"ambiguous"}return s}function pc(t,e,i,s,n=1,o=15){const a=function(t,e,i,s,n=1,o=15){const[a,r]=tr([e[0],e[1]],[e[2],e[3]]);let l=0,c=1/0;const h=[(e[0]+e[2])/2,(e[1]+e[3])/2];for(const e of i){const[i,o]=tr([e[0],e[1]],[e[2],e[3]]);if(Math.abs(a*o-r*i)>.05)continue;const d=cr(t,[e[0],e[1]],[e[2],e[3]],s,n);if(!(d>0))continue;const u=[(e[0]+e[2])/2,(e[1]+e[3])/2],p=Math.hypot(u[0]-h[0],u[1]-h[1]);p0?l:o}(t,e,i,s,n,o);return pr(t,[e[0],e[1]],[e[2],e[3]],a,s,n)}function _c(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(!(Ts(n,o)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Ts([o[0],o[1]],t)>i))for(const e of[[o[0],o[1]],[o[2],o[3]]])Ts(e,t)<=2*i&&s.push(e);return s}function mc(t,e,i,s){const n=ic(t);if(!n.length)return[];const o=ac(e,s);if(!o.length)return[];const a=[],r=Math.max(4*s,1e-6);for(const t of n){const e=tc(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 a=(t[0]-n)*u+(t[1]-l)*p,c=(t[2]-n)*u+(t[3]-l)*p,h=Math.max(0,Math.min(a,c)),m=Math.min(d,Math.max(a,c));if(m-ht.lo-e.lo||t.hi-e.hi);const i=[];for(const t of e){const e=i[i.length-1];e&&t.lo<=e.hi+r?e.hi=Math.max(e.hi,t.hi):i.push({...t})}for(const e of i){const i=[n+u*e.lo,l+p*e.lo],s=[n+u*e.hi,l+p*e.hi];Math.hypot(s[0]-i[0],s[1]-i[1])i.pair===t&&Math.abs(i.ux*a-i.uy*o)<=1e-6&&Math.abs((e[0]-i.origin[0])*i.uy-(e[1]-i.origin[1])*i.ux)<=c&&Math.abs((e[2]-i.origin[0])*i.uy-(e[3]-i.origin[1])*i.ux)<=c);h||(h={pair:t,origin:[e[0],e[1]],ux:o,uy:a,ranges:[]},l.push(h));const d=(e[0]-h.origin[0])*h.ux+(e[1]-h.origin[1])*h.uy,u=(e[2]-h.origin[0])*h.ux+(e[3]-h.origin[1])*h.uy;h.ranges.push({lo:Math.min(d,u),hi:Math.max(d,u)})}const h=[];for(const t of l){t.ranges.sort((t,e)=>t.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t.ranges){const t=e[e.length-1];t&&i.lo<=t.hi+r?t.hi=Math.max(t.hi,i.hi):e.push({...i})}for(const s of e){const e=[t.origin[0]+t.ux*s.lo,t.origin[1]+t.uy*s.lo],n=[t.origin[0]+t.ux*s.hi,t.origin[1]+t.uy*s.hi];Math.hypot(n[0]-e[0],n[1]-e[1])>=r&&h.push(Ql(e,n,i))}}return h}function gc(t,e,i,s){const n=ic(t);if(!n.length)return[];if(e.length!==i.length)return n;const o=[];for(const t of n){const n=tc(t,s),a=[n[0],n[1]],r=[n[2],n[3]];let l=a,c=r;for(let t=0;t{const e=sc(t,[s[0],s[1],n[0],n[1]]);if(e.d>.001)return t;const i=h[0]-o[0],a=h[1]-o[1];return[o[0]+i*e.t,o[1]+a*e.t]};if(sc([(a[0]+r[0])/2,(a[1]+r[1])/2],[s[0],s[1],n[0],n[1]]).d<.01){l=d(a),c=d(r);break}}o.push(Ql(l,c,s))}return o}const fc=15e3,vc=2e3,yc=new WeakMap;function bc(t,e){let i=yc.get(t);if(!i){const e=new Set;i={hiddenAt:"hidden"===t.visibilityState?Date.now():0,token:0,subscribers:e,onVisibility:()=>{const e=yc.get(t);if(!e)return;const i=Date.now();if("hidden"===t.visibilityState){e.hiddenAt||(e.hiddenAt=i);const t={kind:"hidden",token:e.token,at:i,hiddenFor:0,long:!1};for(const i of[...e.subscribers])i(t);return}const s=e.hiddenAt?Math.max(0,i-e.hiddenAt):0;e.hiddenAt=0,e.token++;const n={kind:"visible",token:e.token,at:i,hiddenFor:s,long:s>=fc};for(const t of[...e.subscribers])t(n)},onPageShow:e=>{const i=yc.get(t);if(!i)return;const s=Date.now();i.token++;const n={kind:"pageshow",token:i.token,at:s,hiddenFor:0,long:!!e.persisted,persisted:!!e.persisted};for(const t of[...i.subscribers])t(n)}},yc.set(t,i),t.addEventListener("visibilitychange",i.onVisibility),t.defaultView?.addEventListener("pageshow",i.onPageShow)}return i.subscribers.add(e),()=>{const i=yc.get(t);i&&(i.subscribers.delete(e),i.subscribers.size||(t.removeEventListener("visibilitychange",i.onVisibility),t.defaultView?.removeEventListener("pageshow",i.onPageShow),yc.delete(t)))}}const wc={now:()=>Date.now(),setTimeout:(t,e)=>window.setTimeout(t,e),clearTimeout:t=>window.clearTimeout(t),requestAnimationFrame:t=>window.requestAnimationFrame(t),cancelAnimationFrame:t=>window.cancelAnimationFrame(t)};class kc{constructor(t,e=wc){this.onChange=t,this.clock=e,this._state="steady",this._token=0,this._frameFingerprint="",this._hasCompleteFrame=!1,this._overlayPhase="none",this._recoveryReason=null,this._overlayTimer=0,this._overlayRaf=0,this._overlayOpaqueAt=0,this._barrierRafs=new Set,this._trace=[],this._disposed=!1}get state(){return this._state}get token(){return this._token}get frameFingerprint(){return this._frameFingerprint}get hasCompleteFrame(){return this._hasCompleteFrame}get overlayPhase(){return this._overlayPhase}get overlayVisible(){return"none"!==this._overlayPhase}get overlayBlocksInteraction(){return this.overlayVisible}get recoveryReason(){return this._recoveryReason}get trace(){return this._trace.map(t=>({...t,...t.stage?{stage:[...t.stage]}:{}}))}note(t,e={}){this.record(t,void 0,e)}record(t,e,i={}){this._trace.push({at:this.clock.now(),token:this._token,event:t,state:this._state,...e?{reason:e}:{},...i}),this._trace.length>80&&this._trace.splice(0,this._trace.length-80)}changed(){this._disposed||this.onChange()}adoptCompleteFrame(t){t&&(this._hasCompleteFrame=!0,this._frameFingerprint=t,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-adopted"),this.changed())}markCompleteFrame(t){t&&(this._hasCompleteFrame=!0,this._frameFingerprint=t,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-complete"),this.changed())}refreshCompleteFrame(t){!this._disposed&&this._hasCompleteFrame&&"steady"===this._state&&t&&t!==this._frameFingerprint&&(this._frameFingerprint=t,this.record("frame-refreshed"))}visibility(t){return"hidden"===t.kind?(this.record("visibility-hidden"),this._token):t.long||"visible"!==t.kind&&"pageshow"!==t.kind?this.beginCandidate("pageshow"===t.kind?"pageshow":"long-resume"):(this.record("pageshow"===t.kind?"pageshow-noop":"visibility-visible-quick"),this._token)}beginCandidate(t,e="plan"){return this._token++,this._recoveryReason=e,this._hasCompleteFrame?(this._state="connection"===e?"offline-stale":"holding",this.clearOverlay()):this.overlayVisible?this._state="overlay-visible":(this._state="overlay-pending",this.scheduleOverlay(e)),this.record("candidate-start",t),this.changed(),this._token}connectionLost(){return this.beginCandidate("connection-lost","connection")}candidateReady(t){return!this._disposed&&t===this._token&&(this._state="candidate-ready",this.record("candidate-ready"),this.changed(),!0)}async commitAfterPaint(t,e){if(this._disposed||t!==this._token)return!1;let i=!0;const s=(async()=>{for(await e.updateComplete();i&&!this._disposed&&t===this._token;)if(e.stageValid()&&e.assetsReady()){if(await this.nextFrame(),t!==this._token||!e.stageValid()||!e.assetsReady())continue;if(await this.nextFrame(),t===this._token&&e.stageValid()&&e.assetsReady())return!0}else await this.nextFrame();return!1})();let n=0;const o=new Promise(t=>{n=this.clock.setTimeout(()=>t({ready:!1,timedOut:!0}),vc)}),a=await Promise.race([s.then(t=>({ready:t,timedOut:!1})),o]);return i=!1,this.clock.clearTimeout(n),!this._disposed&&t===this._token&&(!(!a.ready&&!a.timedOut)&&(a.ready?(this._hasCompleteFrame=!0,this._frameFingerprint=e.frameFingerprint(),this.record("paint-barrier"),this.finishOverlayAfterCommit(),!0):(this.record("paint-barrier-timeout"),this._hasCompleteFrame?(this._state="connection"===this._recoveryReason?"offline-stale":"steady",this.clearOverlay()):(this._state="recovery-error","opaque"!==this._overlayPhase&&this.showOverlayNow()),this.changed(),!1)))}retry(t=this._recoveryReason||"plan"){return this.beginCandidate("retry",t)}nextFrame(){return new Promise(t=>{const e=this.clock.requestAnimationFrame(()=>{this._barrierRafs.delete(e),t()});this._barrierRafs.add(e)})}scheduleOverlay(t){this.clearOverlayTimer(),this._recoveryReason=t,this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._hasCompleteFrame||"overlay-pending"!==this._state&&"candidate-ready"!==this._state||(this._state="overlay-visible",this._overlayPhase="entering",this.record("overlay-enter"),this.changed(),this._overlayRaf=this.clock.requestAnimationFrame(()=>{this._overlayRaf=0,"entering"===this._overlayPhase&&(this._overlayPhase="fading-in",this.changed(),this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,"fading-in"===this._overlayPhase&&(this._overlayPhase="opaque",this._overlayOpaqueAt=this.clock.now(),this.record("overlay-opaque"),this.changed())},150))}))},150)}showOverlayNow(){this.clearOverlayTimer(),this._overlayPhase="opaque",this._overlayOpaqueAt=this.clock.now(),this.record("overlay-error")}finishOverlayAfterCommit(){if(this.clearOverlayTimer(),"none"===this._overlayPhase||"entering"===this._overlayPhase||"fading-in"===this._overlayPhase)return this.clearOverlay(),this._state="steady",this._recoveryReason=null,this.record("candidate-committed"),void this.changed();const t=Math.max(0,this._overlayOpaqueAt+250-this.clock.now());this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._overlayPhase="leaving",this.record("overlay-leave"),this.changed(),this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._overlayPhase="none",this._state="steady",this._recoveryReason=null,this.record("candidate-committed"),this.changed()},180)},t)}clearOverlayTimer(){this._overlayTimer&&this.clock.clearTimeout(this._overlayTimer),this._overlayTimer=0}clearOverlay(){this.clearOverlayTimer(),this._overlayRaf&&this.clock.cancelAnimationFrame(this._overlayRaf),this._overlayRaf=0,this._overlayPhase="none",this._overlayOpaqueAt=0}dispose(){this._disposed=!0,this._token++,this.clearOverlay();for(const t of this._barrierRafs)this.clock.cancelAnimationFrame(t);this._barrierRafs.clear()}}function $c(t){let e=2166136261,i=2654435769;const s=new WeakSet,n=t=>{for(let s=0;s>>0)+(i>>>2)}},o=t=>{if(null===t)return void n("n");const e=typeof t;if("string"===e)return void n(`s${t.length}:${t}`);if("number"===e)return void n(`d${Number.isNaN(t)?"NaN":String(t)}`);if("boolean"===e)return void n(t?"t":"f");if("undefined"===e)return void n("u");if("object"!==e)return void n(`${e}:${String(t)}`);const i=t;if(s.has(i))n("[cycle]");else{if(s.add(i),Array.isArray(t)){n("[");for(const e of t)o(e);return n("]"),void s.delete(i)}n("{");for(const e of Object.keys(t).sort())n(e),o(t[e]);n("}"),s.delete(i)}};return o(t),`${(e>>>0).toString(16).padStart(8,"0")}${(i>>>0).toString(16).padStart(8,"0")}`}function xc(t){return $c(t)}const Sc=new WeakMap;class Mc{constructor(t,e=()=>Date.now()){this.onUpdate=t,this.now=e,this.fallbackAuthority={},this.referenceOwner={},this.referenced=new Set,this.queued=new Set,this.disposed=!1,this.sharedUpdate=()=>{this.disposed||this.onUpdate()}}bind(t){const e=((t,e)=>{const i=t?.connection||t?.callWS||e;return!i||"object"!=typeof i&&"function"!=typeof i?e:i})(t,this.fallbackAuthority);return this.shared&&this.authority===e||(this.shared&&(this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner)),this.authority=e,this.shared=(t=>{let e=Sc.get(t);return e||(e={signed:{},queued:new Set,inFlight:new Map,retry:new Map,listeners:new Set,settlers:new Set,references:new Map},Sc.set(t,e)),e})(e),this.disposed||(this.shared.listeners.add(this.sharedUpdate),this.shared.references.set(this.referenceOwner,new Set(this.referenced)))),this.shared}start(t,e){this.disposed=!1,this.referenced=new Set(e());const i=this.bind(t());i.listeners.add(this.sharedUpdate),i.references.set(this.referenceOwner,new Set(this.referenced)),this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(t(),e()),288e5)}dispose(){if(this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.shared){this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner);for(const t of this.queued)this.shared.queued.delete(t)}this.queued.clear()}invalidate(t){const e=this.bind(t);clearTimeout(this.batchTimer),this.queued.clear(),e.queued.clear(),e.inFlight.clear(),e.retry.clear(),e.signed={}}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(t,e){const i=$s(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.bind(t);this.referenced.add(i),s.references.set(this.referenceOwner,new Set(this.referenced));const n=s.signed[i],o=n?this.now()-n.at:1/0;return o{const e=[...this.queued];this.queued.clear();for(const t of e)i.queued.delete(t);this.sign(t,e)},30))}sign(t,e){if(!e.length||!t?.callWS)return;const i=this.bind(t);for(const s of function(t,e){const i=Math.max(1,Math.floor(e)),s=[];for(let e=0;e{const s=i.inFlight.get(t);return!(void 0!==s&&e-s<15e3)&&(i.inFlight.set(t,e),!0)});n.length&&t.callWS({type:"houseplan/content/sign",paths:n}).then(t=>{const e=this.now();let s=0,o=!1;for(const a of n){const n=t?.urls?.[a];if("string"==typeof n&&n){const t=i.signed[a];t?.loaded&&t.url!==n?(t.pending={url:n,at:e},this.preloadReplacement(i,a,n,e)):(i.signed[a]={url:n,at:e,loaded:t?.url===n&&!!t.loaded},o=!0),i.retry.delete(a),s++}else this.backOff(i,a)}if(s&&(this.trimShared(i),o))for(const t of[...i.listeners])t()}).catch(()=>{for(const t of n)this.backOff(i,t)}).finally(()=>{for(const t of n)i.inFlight.get(t)===e&&i.inFlight.delete(t);for(const t of[...i.settlers])t()})}}preloadReplacement(t,e,i,s){const n=()=>{const n=t.signed[e];if(n?.pending?.url===i){t.signed[e]={url:i,at:s,loaded:!0},t.retry.delete(e),this.trimShared(t);for(const e of[...t.listeners])e()}},o=()=>{const s=t.signed[e];s?.pending?.url===i&&(delete s.pending,this.backOff(t,e))};if("undefined"==typeof Image)return void n();const a=new Image;a.onload=()=>{("function"==typeof a.decode?a.decode():Promise.resolve()).then(n).catch(o)},a.onerror=o,a.src=i}preloadCurrentImage(t,e,i){if(i.loaded)return Promise.resolve(!0);if(i.preload)return i.preload;if("undefined"==typeof Image)return i.loaded=!0,Promise.resolve(!0);const s=i.url;return i.preload=new Promise(n=>{const o=new Image;let a=!1;const r=o=>{if(a)return;a=!0,clearTimeout(l);const r=t.signed[e];if(r===i&&delete r.preload,o&&r===i&&r.url===s){r.loaded=!0;for(const e of[...t.listeners])e();n(!0)}else o||r!==i||this.backOff(t,e),n(!1)};o.onload=()=>{("function"==typeof o.decode?o.decode():Promise.resolve()).then(()=>r(!0)).catch(()=>r(!1))},o.onerror=()=>r(!1);const l=setTimeout(()=>r(!1),vc);o.src=s}),i.preload}prepareImage(t,e){const i=$s(e);if(!i.startsWith("/api/houseplan/content/"))return Promise.resolve(!0);const s=this.bind(t),n=()=>{const e=s.signed[i];if(e&&this.now()-e.atthis.now()&&!s.inFlight.has(i)&&!s.queued.has(i)?Promise.resolve(!1):null},o=n();return o?(a=o,new Promise(t=>{let e=!1;const i=i=>{e||(e=!0,clearTimeout(s),t(i))},s=setTimeout(()=>i(!1),vc);a.then(i).catch(()=>i(!1))})):new Promise(t=>{let e=!1,i=null;const o=i=>{e||(e=!0,clearTimeout(r),s.settlers.delete(a),t(i))},a=()=>{e||i||(i=n(),i&&i.then(o).catch(()=>o(!1)))},r=setTimeout(()=>o(!1),vc);s.settlers.add(a),a()});var a}backOff(t,e){const i=t.retry.get(e)?.delay||0,s=Math.min(6e4,i?2*i:2e3);t.retry.set(e,{notBefore:this.now()+s,delay:s})}trimShared(t){const e=Object.entries(t.signed);if(e.length<=512)return;const i=new Set(t.inFlight.keys());for(const e of t.references.values())for(const t of e)i.add(t);for(const[t,s]of e)(s.pending||s.preload)&&i.add(t);e.sort((t,e)=>e[1].at-t[1].at);const s=e.filter(([t])=>i.has(t));for(const t of e){if(s.length>=512)break;i.has(t[0])||s.push(t)}t.signed=Object.fromEntries(s)}resign(t,e){const i=this.bind(t);this.referenced=new Set(e),i.references.set(this.referenceOwner,new Set(this.referenced));const s=this.now(),n=new Set;for(const t of i.references.values())for(const e of t)n.add(e);const o={};for(const[t,e]of Object.entries(i.signed))n.has(t)&&s-e.at!!o[t]&&!o[t].pending))}markLoaded(t,e,i){const s=$s(e);if(!s.startsWith("/api/houseplan/content/"))return;const n=this.bind(t).signed[s];if(n&&(!i||n.url===i)&&!n.loaded){n.loaded=!0;for(const e of[...this.bind(t).listeners])e()}}isReady(t,e){const i=$s(e);if(!i.startsWith("/api/houseplan/content/"))return!0;const s=this.bind(t).signed[i];return!!s&&this.now()-s.at{if(null==t||""===t)return null;const e=Number(t);return Number.isFinite(e)?e:null},Pc=t=>{const e=t,i=Tc(Array.isArray(e)?e[0]:e?.x),s=Tc(Array.isArray(e)?e[1]:e?.y);return null==i||null==s?null:[i,s]};function Rc(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=Pc(i);if(!t)return null;e.push(t)}if(e.length>1){const t=e[0],i=e[e.length-1];t[0]===i[0]&&t[1]===i[1]&&e.pop()}if(e.length<3)return null;let i=0,s=0,n=0;for(let t=0;tt+e[0],0)/e.length,e.reduce((t,e)=>t+e[1],0)/e.length]:[s/(3*i),n/(3*i)]}function Ac(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=Pc(i);if(!t)return null;e.push(t)}if(e.length>1){const t=e[0],i=e[e.length-1];t[0]===i[0]&&t[1]===i[1]&&e.pop()}const i=Rc(e);if(!i)return null;const s=e.map(t=>t[0]),n=e.map(t=>t[1]);return{centroid:i,bbox:[Math.min(...s),Math.min(...n),Math.max(...s),Math.max(...n)]}}function Ec(t){let e=t;if(e&&"object"==typeof e&&!Array.isArray(e)){const t=e;e=t.path??t.points??e}if(!Array.isArray(e)||!e.length)return[];let i=(e.some(t=>(t=>Array.isArray(t)?t.length>=2&&!Array.isArray(t[0])&&("object"!=typeof t[0]||null==t[0]):!!t&&"object"==typeof t&&("x"in t||"y"in t))(t))?[e]:e.filter(Array.isArray)).map(t=>t.map(Pc).filter(t=>null!=t)).filter(t=>t.length>=2);i.length>64&&(i=i.slice(-64));if(i.reduce((t,e)=>t+e.length,0)<=4e3)return i.map(t=>t.slice());const s=4e3-2*i.length,n=i.map(t=>t.length-2),o=n.reduce((t,e)=>t+e,0),a=n.map(t=>s*t/o),r=a.map(Math.floor);let l=s-r.reduce((t,e)=>t+e,0);const c=a.map((t,e)=>({index:e,fraction:t-Math.floor(t)})).sort((t,e)=>e.fraction-t.fraction||t.index-e.index);for(let t=0;tfunction(t,e){if(e>=t.length)return t.slice();if(e<=2)return[t[0],t[t.length-1]];const i=[];for(let s=0;st.slice()),i=e[e.length-1];return i?(i.length>2?i.pop():e.pop(),e):[]}function Fc(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=Tc(e.x)&&null!=Tc(e.y)?{x:Tc(e.x),y:Tc(e.y),a:Tc(e.a??e.angle??e.theta)}:null,s=Ec(t.path?.path??t.path?.points??t.path),n=[],o=t.rooms,a=Array.isArray(o)?o.map((t,e)=>[String(t?.id??e),t]):o&&"object"==typeof o?Object.entries(o):[];for(const[t,e]of a){if(!e||"object"!=typeof e)continue;const i=String(e.name??e.label??"").trim(),s=[[Tc(e.cx),Tc(e.cy)],[Tc(e.center?.x),Tc(e.center?.y)],[Tc(e.x),Tc(e.y)]].find(([t,e])=>null!=t&&null!=e);let o=s?.[0]??null,a=s?.[1]??null;const r=Ac(e.outline),l=Tc(e.x0),c=Tc(e.y0),h=Tc(e.x1),d=Tc(e.y1),u=(null!=l&&null!=c&&null!=h&&null!=d?[Math.min(l,h),Math.min(c,d),Math.max(l,h),Math.max(c,d)]:null)||r?.bbox||null;if(null!=o&&null!=a||!r||([o,a]=r.centroid),null!=o&&null!=a||!u||(o=(u[0]+u[2])/2,a=(u[1]+u[3])/2),i&&null!=o&&null!=a){const e={id:t,name:i,cx:o,cy:a};u&&([e.x0,e.y0,e.x1,e.y1]=u),n.push(e)}}const r=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||n.length||s.length?{pos:i,path:s,rooms:n,mapId:r}:null}function Nc(t,e,i){if(!t||!e)return null;const s=e.attributes||{},n=t.split(".")[0]||"",o=null!=i?.platform?String(i.platform):null,a=!!Pc(s.vacuum_position??s.robot_position),r=!(!s.rooms||"object"!=typeof s.rooms),l=Ec(s.path?.path??s.path?.points??s.path).length>0,c=[s.map_name,s.current_map,s.map_index,s.selected_map].some(t=>null!=t),h="xiaomi_cloud_map_extractor"===o,d=r||l||c;let u=null;if(a?u="compatible":h?u="known_xcme_incomplete":d?u="partial":"camera"===n&&(u="camera"),!u)return null;const p=a?"camera"===n?300:200:h?100:d?50:0;return{entityId:t,name:String(s.friendly_name||t),platform:o,category:u,hasPosition:a,hasRooms:r,hasPath:l,hasMapId:c,score:p}}const Oc=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function Lc(t,e){const i=new Map(e.map(t=>[Oc(t.name),t])),s=[],n=[];for(const e of t){const t=i.get(Oc(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,a=0,r=0,l=0,c=0,h=0,d=0,u=0;for(const[[p,_],[m,g]]of t){if(![p,_,m,g].every(Number.isFinite))return null;e+=p*p,i+=p*_,s+=p,n+=_*_,o+=_,a+=1,r+=p*m,l+=_*m,c+=m,h+=p*g,d+=_*g,u+=g}const p=[e,i,s,i,n,o,s,o,a],_=t=>{const[e,i,s,n,o,a,r,l,c]=p,h=e*(o*c-a*l)-i*(n*c-a*r)+s*(n*l-o*r);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-a*l)/h,(s*l-i*c)/h,(i*a-s*o)/h,(a*r-n*c)/h,(e*c-s*r)/h,(s*n-e*a)/h,(n*l-o*r)/h,(i*r-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]]},m=_([r,l,c]),g=_([h,d,u]);if(!m||!g)return null;const f=[m[0],m[1],m[2],g[0],g[1],g[2]];return f.every(Number.isFinite)?f:null}(s);return o?{matrix:o,matched:n,residual:Dc(o,s)}:null}function Hc(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(),[a,r]=t[n],[l,c]=t[o],h=l-a,d=c-r,u=Math.hypot(h,d)||1e-9;let p=0,_=-1;for(let e=n+1;ep&&(p=i,_=e)}_>0&&p>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 qc(t){return"cleaning"===t||"returning"===t||"on"===t}const Bc={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function Uc(t){const[e,i]=Bc[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 Wc(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}}function jc(t,e,i,s){const[n,o]=Cc(Uc(e),i,s),a=Uc({...t,ox:0,oy:0}),[r,l]=Cc(a,i,s);return{...t,ox:n-r,oy:o-l}}function Gc(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}const Vc=1e-5;function Kc(t){const e=t.map(()=>[]);for(let i=0;i=1-1e-9||u<=1e-9||u>=1-1e-9||(e[i].push(d),e[a].push(u))}}const i=[];for(let s=0;st-e),1];for(let t=1;t{const i=e[2]-e[0],s=e[3]-e[1],n=i*i+s*s;if(!(n>0))return Math.hypot(t[0]-e[0],t[1]-e[1]);const o=Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*s)/n));return Math.hypot(t[0]-(e[0]+o*i),t[1]-(e[1]+o*s))},Zc=(t,e,i,s)=>{const n=s[2]-s[0],o=s[3]-s[1],a=e*o-i*n;if(Math.abs(a)<1e-12)return 1/0;const r=s[0]-t[0],l=s[1]-t[1],c=(r*o-l*n)/a;if(!(c>1e-9))return 1/0;const h=(r*i-l*e)/a;return h<-1e-9||h>1+1e-9?1/0:c};function Xc(t,e,i,s=96){if(!(e>0&&Number.isFinite(t[0])&&Number.isFinite(t[1])))return[];const n=[];for(const s of i){if(!s||s.length<4)continue;if(![s[0],s[1],s[2],s[3]].every(Number.isFinite))continue;const i=Jc(t,s);if(i<1e-7)return[];i>e||n.push(s)}const o=[],a=Math.max(12,Math.round(s));for(let t=0;tt-e);const l=[];let c=Number.NEGATIVE_INFINITY;for(const i of o){if(i-c<1e-9)continue;c=i;const s=Math.cos(i),o=Math.sin(i);let a=e;for(const e of n){const i=Zc(t,s,o,e);i=3?l:[]}class Qc extends ht{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 Zo(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:Xo(e,"editor.lang_auto")},{value:"en",label:Xo(e,"editor.lang_en")},{value:"ru",label:Xo(e,"editor.lang_ru")}]}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const t=this._lang,e={title:Xo(t,"editor.title"),default_floor:Xo(t,"editor.default_floor"),language:Xo(t,"editor.language"),icon_size:Xo(t,"editor.icon_size"),show_temperature:Xo(t,"editor.show_temperature"),live_states:Xo(t,"editor.live_states"),show_signal:Xo(t,"editor.show_signal"),kiosk:Xo(t,"editor.kiosk"),cycle:Xo(t,"editor.cycle")};return U`[t[0]-e[0],t[1]-e[1]],ia=(t,e)=>[t[0]+e[0],t[1]+e[1]],sa=(t,e)=>t[0]*e[0]+t[1]*e[1],na=t=>Math.hypot(t[0],t[1]);function oa(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(r-o)*(t[1]-a)/(l-a)+o&&(i=!i)}return i}([r[0]+a[0]*l,r[1]+a[1]*l],t)&&(a=[-a[0],-a[1]]),a}function la(t,e,i,s){const n=s||ra(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 ca(t,e,i,s){const n=[],o=ea(i,e),a=na(o);if(as||h>s)continue;const d=sa(ea(o,e),r),u=sa(ea(l,e),r),p=Math.max(0,Math.min(d,u)),_=Math.min(a,Math.max(d,u));_-p>s&&n.push([[e[0]+r[0]*p,e[1]+r[1]*p],[e[0]+r[0]*_,e[1]+r[1]*_]])}return n}function ha(t,e,i,s,n){const o=ca(t,e,i,n);if(!o.length)return null;const a=t=>o.some(([e,i])=>aa(t,e,i)<=n),r=t.length,l=[];for(let o=0;on||f>n)continue;const v=sa(ea(e,c),p),y=sa(ea(i,c),p),b=Math.max(0,Math.min(v,y)),w=Math.min(u,Math.max(v,y));if(!(w-b<=n)){if(b>n&&bn&&wna(ea(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],a=(t[0]-n[0])*(o[1]-n[1])-(t[1]-n[1])*(o[0]-n[0]),r=na(ea(o,n))||1;return Math.abs(a)/r>e});return i.length>=3?i:t}function ua(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-n[0])*r[1]-(t[1]-n[1])*r[0],c=t=>(t[0]-n[0])*r[0]+(t[1]-n[1])*r[1],h=i,d=a-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(a+(t-u)/g*(r-a));s=Math.min(s,f(_),f(m))}}return s}function _a(t,e,i){if(yi(t,e,i))return!0;if(vi(t,e,i)||vi(e,t,i))return!1;let s=0;try{const i=Je([[...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+=wi(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function ma(t,e,i,s,n){const o=[i.n[0]*s,i.n[1]*s],a={polys:{},openings:{},movedSpans:{}};if(Math.abs(s)<1e-9)return a;for(const e of t){if(e.id===i.roomId){a.polys[e.id]=la(e.poly,i.edge,s,i.n),a.movedSpans[e.id]=[[ia(i.a,o),ia(i.b,o)]];continue}const t=ca(e.poly,i.a,i.b,n);if(!t.length)continue;const r=ha(e.poly,i.a,i.b,o,n);r&&(a.polys[e.id]=r,a.movedSpans[e.id]=t.map(([t,e])=>[ia(t,o),ia(e,o)]))}for(const t of e)aa([t.x,t.y],i.a,i.b)<=n&&(a.openings[t.id]=[t.x+o[0],t.y+o[1]]);return a}function ga(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<=r+i)return!0}return!1}function fa(t,e,i){return t.filter(t=>e.some(e=>{for(let s=0;sr.polys[t.id]||t.poly;for(const e of l){const s=t.find(t=>t.id===e),n=r.polys[e];if(!ua(n))return!1;const l=oa(s.poly),h=oa(n);if(Math.abs(h)t.find(t=>t.id===e).poly),d=t.map(c);for(const t of fa(e,h,2*a)){const e=r.openings[t.id];if(!ga(e?{...t,x:e[0],y:e[1]}:t,d,2*a))return!1}return!0}function ya(t,e,i,s,n,o){const a=t=>[s[0]+(t[0]-s[0])*n,s[1]+(t[1]-s[1])*n],r={poly:t.poly.map(a),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=ya(l,e,c,s,n,2*r),d=h.poly,u=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 fa(e,[l.poly],2*r)){const e=h.openings[t.id];if(!ga(e?{...t,x:e[0],y:e[1]}:t,p,2*r))return!1}return!0}function wa(t,e,i){const s=i/e;return wi(t)*s*s/1e4}function ka(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const $a=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],xa=t=>Math.min(1,Math.max(0,t));function Sa(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=$a[$a.length-1][1];for(let t=1;t<$a.length;t++){const[s,n]=$a[t-1],[o,a]=$a[t];if(e<=o){i=ss(n,a,(e-s)/(o-s));break}}return{bg:i,planDim:.1*xa((10-e)/16),warmth:e<0?1:xa(1-e/10)}}function Ma(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&&ci(o,t.poly))||null},a=o(1),r=o(-1);return a&&r?null:a||r?a?{normal:[-n[0],-n[1]],roomId:a.id}:{normal:n,roomId:r.id}:null}function Ca(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function Da(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 Ta(t,e){try{const i=Je([[...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 Pa(t,e,i,s,n,o,a){if(!(s>0))return[];const r=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=[-r[0],-r[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=Ma(i,t);if(!e||!Ca(e.normal,r,s))continue;const n=t.find(t=>t.id===e.roomId);if(!n)continue;const d=o&&o[e.roomId]||n.poly,u=i.angle*Math.PI/180,p=i.length/2,_=[-e.normal[0],-e.normal[1]],m=Math.max(0,a?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(u)*p,y=Math.sin(u)*p,b=[g-v,f-y],w=[g+v,f+y],k=c*i.length,$=Ta(Da(b,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:b,b:w,dir:l,len:k,normal:_,depth:k*x})}return h}function Ra(t){return Math.round(10*(Number(t)||0))/10}const Aa=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Ea(t,e){const i=Aa(e?.north_deg);return null!==i?i:Aa(t?.north_deg)}function Ia(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function za(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Fa(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 Na={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},Oa=(t,e=1)=>{const i=Number(t);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):e},La=t=>{let e=Number(t);return Number.isFinite(e)?(e=(e%360+360)%360,e>180?e-360:e):0};function Ha(t,e,i){return La(e)?[t.x,t.y]:i([t.x,t.y])}const qa=(t,e,i)=>{const s=Number(t),n=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&s>0?s/n*i:0},Ba=(t,e,i)=>{const s=Number(t),n=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&i>0?s/i*n:0},Ua=(t,e,i,s=Na.widthCm)=>{const n=Number(t?.width_cm);if(Number.isFinite(n)&&n>0)return n;const o=Number(t?.width);return Number.isFinite(o)&&o>0?Ba(o,e,i):s};function Wa(t,e){return{color:t.color,opacity:Oa(t.opacity),width_cm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),...e?{fill:t.fill,fill_color:t.fillColor,fill_opacity:Oa(t.fillOpacity,.25)}:{}}}function ja(t){const e=t.x+t.w/2,i=t.y+t.h/2,s=La(t.angle)*Math.PI/180,n=Math.cos(s),o=Math.sin(s),a=(t,s)=>{const a=t-e,r=s-i;return[e+a*n-r*o,i+a*o+r*n]};return[a(t.x,t.y),a(t.x+t.w,t.y),a(t.x+t.w,t.y+t.h),a(t.x,t.y+t.h)]}function Ga(t){const e=ja(t),i=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],s=[(e[0][0]+e[2][0])/2,(e[0][1]+e[2][1])/2];return{points:[...e,i(e[0],e[1]),i(e[1],e[2]),i(e[2],e[3]),i(e[3],e[0]),s],segments:e.map((t,i)=>({a:t,b:e[(i+1)%4]}))}}function Va(t,e,i,s,n,o,a,r){const l=La(t.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l),d=-Math.sin(l),u=Math.cos(l),p=t.x+t.w/2,_=t.y+t.h/2,m=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,f=p+m*c+g*d,v=_+m*h+g*u,y=s-f,b=n-v;let w=(y*c+b*h)*(e>0?1:-1),k=(y*d+b*u)*(i>0?1:-1),$=null;if(o){const e=w/Math.max(t.w,r),i=k/Math.max(t.h,r);$=Math.max(r/Math.max(t.w,r),r/Math.max(t.h,r),e,i),w=t.w*$,k=t.h*$}if(a>0)if(o){const e=Math.max(1,Math.round(t.w/a)),i=Math.max(1,Math.round(t.h/a));if(Math.abs(t.w-e*a)<1e-6&&Math.abs(t.h-i*a)<1e-6){const s=(t,e)=>{let i=Math.abs(t),s=Math.abs(e);for(;s;)[i,s]=[s,i%s];return Math.max(1,i)},n=1/s(e,i),o=Math.max(r/t.w,r/t.h),a=Math.max(o,Math.round(($??1)/n)*n);w=t.w*a,k=t.h*a}else{t.w>=t.h?(w=Math.round(w/a)*a,k=w*(t.h/Math.max(t.w,r))):(k=Math.round(k/a)*a,w=k*(t.w/Math.max(t.h,r)))}}else w=Math.round(w/a)*a,k=Math.round(k/a)*a;w=Math.max(r,w),k=Math.max(r,k);return{x:f+(e>0?w/2:-w/2)*c+(i>0?k/2:-k/2)*d-w/2,y:v+(e>0?w/2:-w/2)*h+(i>0?k/2:-k/2)*u-k/2,w:w,h:k,angle:La(t.angle)||void 0}}const Ka=(t,e,i)=>{const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;if(o<1e-12)return[...e];const a=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*n)/o));return[e[0]+s*a,e[1]+n*a]};const Ya=15;function Ja(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function Za(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function Xa(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function Qa(t,e,i){const s=Number(e)>0?Number(e):5;return Za(t)/s*i}function tr(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function er(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 ir(t,e,i){const s=tr((t[0]+e[0])/2,i),n=tr((t[1]+e[1])/2,i),[o,a]=er(t,e);let r=Math.atan2(a,o);r<0&&(r+=Math.PI);const l=Math.round(1800*r)/1800,c=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(c)},${n.toFixed(c)}@${l.toFixed(4)}`}function sr(t,e,i,s){return 1===s?ir(t,e,i):ir([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function nr(t,e){if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return null;const i=[Number(t.a[0]),Number(t.a[1]),Number(t.b[0]),Number(t.b[1])];if(!i.every(Number.isFinite))return null;const s=e>0?e:1;return[[i[0]*s,i[1]*s],[i[2]*s,i[3]*s]]}function or(t,e,i,s,n){const o=n>0?n:1;return{key:sr(t,e,s,o),cm:Za(i),a:[t[0]/o,t[1]/o],b:[e[0]/o,e[1]/o]}}function ar(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),a=Number(e.key.slice(t+1));[n,o,a].every(Number.isFinite)&&s.push({w:e,x:n*i,y:o*i,ang:a})}return s}function rr(t,e){const[i,s]=er(t,e);let n=Math.atan2(s,i);return n<0&&(n+=Math.PI),n}function lr(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function cr(t,e,i,s,n=1){if(!t?.length)return null;const o=sr(e,i,s,n),a=t.find(t=>t.key===o);if(a)return a;const r=n>0?n:1,l=(e[0]+i[0])/2,c=(e[1]+i[1])/2,h=rr(e,i),d=Math.max(.5*s,1e-9)*r;for(const e of ar(t,r))if(lr(e.ang,h)&&Math.hypot(e.x-l,e.y-c)<=d)return e.w;return null}function hr(t,e,i,s,n=1){const o=cr(t,e,i,s,n);return o&&o.cm>0?Za(o.cm):0}function dr(t,e,i,s=1,n=[]){if(!t?.length)return[];const o=new Set,a=ni(e);for(const t of a)o.add(sr([t[0],t[1]],[t[2],t[3]],i,s));const r=e||[],l=Math.max(i*s*.02,1e-9);for(let t=0;t(o.has(t.key)||(t=>{const e=nr(t,s);if(!e)return!1;const[i,o]=e,r=o[0]-i[0],c=o[1]-i[1],h=Math.hypot(r,c);if(h<=l)return!1;if(!a.some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];return lr(rr(i,o),rr(e,s))&&Rr(i[0],i[1],e[0],e[1],s[0],s[1])<=l&&Rr(o[0],o[1],e[0],e[1],s[0],s[1])<=l}))return!1;const d=(n||[]).some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];if(!lr(rr(i,o),rr(e,s)))return!1;const n=t=>Math.abs((t[0]-i[0])*c-(t[1]-i[1])*r)/h;if(n(e)>l||n(s)>l)return!1;const a=h*h,d=((e[0]-i[0])*r+(e[1]-i[1])*c)/a,u=((s[0]-i[0])*r+(s[1]-i[1])*c)/a;return Math.min(1,Math.max(d,u))-Math.max(0,Math.min(d,u))>l/h});return!d})(t))&&t.cm>=1&&t.cm<=100)}function ur(t,e,i,s=8){const[n,o]=er(t,e);let a=Math.atan2(o,n);a<0&&(a+=Math.PI);let r=i*Math.PI/180%Math.PI;r<0&&(r+=Math.PI);let l=Math.abs(a-r);return l>Math.PI/2&&(l=Math.PI-l),l<=s*Math.PI/180}function pr(t,e,i,s,n=1){if(!t?.length)return[];if(e.length!==i.length)return t.slice();const o=new Map;for(let t=0;t0?n:1,r=Math.max(.5*s,1e-9)*a,l=new Set,c=[];for(const n of t){let t="",h=null;const d=nr(n,a);if(d)for(let n=0;nr||Rr(d[1][0],d[1][1],o[0],o[1],l[0],l[1])>r)continue;const p=l[0]-o[0],_=l[1]-o[1],m=p*p+_*_;if(m<1e-18)continue;const g=t=>{const e=Math.max(0,Math.min(1,((t[0]-o[0])*p+(t[1]-o[1])*_)/m));return[c[0]+(u[0]-c[0])*e,c[1]+(u[1]-c[1])*e]};h=[g(d[0]),g(d[1])],t=sr(h[0],h[1],s,a);break}if(d||(t=o.get(n.key)||""),!t){const o=ar([n],a)[0];if(o)for(let n=0;n1.000001)continue;if(Rr(o.x,o.y,l[0],l[1],c[0],c[1])>r)continue;const g=h[0]+(d[0]-h[0])*Math.max(0,Math.min(1,m)),f=h[1]+(d[1]-h[1])*Math.max(0,Math.min(1,m)),[v,y]=er(h,d),b=Math.max(s*a,1e-6);t=sr([g-v*b,f-y*b],[g+v*b,f+y*b],s,a);break}}t||(t=n.key),l.has(t)||(l.add(t),c.push(h?or(h[0],h[1],n.cm,s,a):{...n,key:t,cm:Za(n.cm)}))}return c}function _r(t,e,i,s,n,o=1){const a=sr(e,i,n,o),r=(t||[]).filter(t=>t.key!==a);return null==s||s<1?r:[...r,or(e,i,s,n,o)]}function mr(t,e,i,s,n,o=[],a=1){let r=t?t.slice():[];for(const t of function(t,e,i,s,n=1,o=[]){const a=Sr(t,e,i,s,n,o);if(!a)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=Fr(i,t),n=kr(i,t);if(s&&n)return`${$r(s)} ${$r(xr(n))}`}}let s="";for(let i=0;i.05)&&Rr(a,r,t[0],t[1],t[2],t[3])<=o)return!0}return!1}function vr(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function yr(t,e){const i=t[e],s=t[(e+1)%t.length],n=s[0]-i[0],o=s[1]-i[1],a=Math.hypot(n,o)||1;let r=-o/a,l=n/a;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]<(r-o)*(t[1]-a)/(l-a+0)+o&&(i=!i)}return i}([c[0]+.001*r,c[1]+.001*l],t)||(r=-r,l=-l),[r,l]}function br(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 wr(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]],a=(o[0]*s[1]-o[1]*s[0])/n;return[t[0]+a*e[0],t[1]+a*e[1]]}function kr(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(br(b,w)){const e=t[n],i=[e[0]+u*h,e[1]+p*h],o=[e[0]+_*d,e[1]+m*d];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=wr(k,b,$,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]+u*h,t[n][1]+p*h]),d>0&&s.push([t[n][0]+_*d,t[n][1]+m*d]),h>0||d>0||s.push([t[n][0],t[n][1]])}return s.length>=3?s:null}function $r(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),r=ii(a);if(!r||r.length<3)return null;const l=vr(s,n),c=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=ii(i);if(t)for(const e of fs(r,t,l))c.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])c.push([t[0],t[1]],[t[2],t[3]]);for(const t of o||[]){const e=nr(t,n);e&&c.push(e[0],e[1])}const h=[],d=[];for(let t=0;tl)continue;const a=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);a<=n||a>=1-n||(o.some(t=>Math.abs(t-a)*s<=2*l)||o.push(a))}o.sort((t,e)=>t-e);for(const s of o)h.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),d.push(t)}return{poly:h,parent:d,orig:r}}function Mr(t,e,i,s){const n=e.poly.length,o=new Array(n).fill(0);if(!t?.length)return o;const a=new Set,r=[];for(let l=0;l0?(o[l]=Za(c.cm),a.add(c.key)):r.push(l)}if(!r.length)return o;const l=s>0?s:1,c=Math.max(.5*i,1e-9)*l,h=ar(t,l).filter(t=>t.w.cm>0),d=new Map;for(const t of r){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],r=rr(s,n),d=(s[0]+n[0])/2,u=(s[1]+n[1])/2;let p=null;const _=Math.hypot(n[0]-s[0],n[1]-s[1]);for(const t of h){if(a.has(t.w.key))continue;if(!lr(t.ang,r))continue;const e=nr(t.w,l);let i=!1,o=0;if(e){if(!lr(rr(e[0],e[1]),r))continue;if(Rr(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>c||Rr(n[0],n[1],e[0][0],e[0][1],e[1][0],e[1][1])>c)continue;i=!0,o=Math.max(0,Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1])-_)}else{if(Rr(t.x,t.y,s[0],s[1],n[0],n[1])>c)continue;o=Math.hypot(t.x-d,t.y-u)}(!p||i&&!p.exact||i===p.exact&&ot?.id===e),n=ii(s);if(!n)return[];const o=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=ii(s);if(t)for(const e of fs(n,t,i))o.push(e)}return o}(t,e,vr(n,r)),h=function(t,e,i,s,n){const o=vr(s,n),a=[];for(let r=0;rRr(h,d,t[0],t[1],t[2],t[3])<=o);a.push(u?"shared":"outer")}return a}(l.poly,c,s,n,r),d=Mr(i,l,n,r),u=d.map((t,e)=>h[e]&&t>0?Qa(t,o,a)/2:0);return{...l,kinds:h,cms:d,offsets:u}}function Dr(t,e,i,s,n,o,a=1){const r=[];for(const l of t||[]){if(!l?.id)continue;const c=Cr(t,l.id,e,i,s,n,o,a);if(c)for(let t=0;t0&&!l.has(c.key)&&(l.add(c.key),r.push(c));const c=[];for(const r of t||[]){if(!r?.id)continue;const l=Cr(t,r.id,e,i,s,n,o,a);if(l)for(let t=0;t0)||null===l.kinds[i]){t++;continue}let o=t;for(;o+10&&c.push({a:[h[0],h[1]],b:[d[0],d[1]],key:sr(h,d,s,a),cm:n,len:u}),t=o+1}}}c.sort((t,e)=>e.len-t.len||t.key.localeCompare(e.key));const h=[],d=new Set,u=new Set,p=4*vr(s,a);for(const t of c){const e=r.filter(e=>!u.has(e.key)&&e.cm===t.cm&&lr(rr(e.a,e.b),rr(t.a,t.b))&&Rr(e.a[0],e.a[1],t.a[0],t.a[1],t.b[0],t.b[1])<=p&&Rr(e.b[0],e.b[1],t.a[0],t.a[1],t.b[0],t.b[1])<=p);if(e.length){for(const t of e)u.add(t.key);d.has(t.key)||(d.add(t.key),h.push(or(t.a,t.b,t.cm,s,a)))}}for(const t of r)u.has(t.key)||d.has(t.key)||(d.add(t.key),h.push(or(t.a,t.b,t.cm,s,a)));return h}function Pr(t,e,i,s,n,o,a,r=1){const l=vr(n,r),c=(s[0]+s[2])/2,h=(s[1]+s[3])/2,d=rr([s[0],s[1]],[s[2],s[3]]);let u=null;for(const s of Dr(t,e,i,n,o,a,r)){if(!lr(rr(s.a,s.b),d))continue;const t=Rr(c,h,s.a[0],s.a[1],s.b[0],s.b[1]);t>4*l||(!u||tt?.id===e),c=ii(l);if(!c||c.length<3)return null;if(!i?.length)return c.map(t=>[t[0],t[1]]);const h=Cr(t,e,i,s,n,o,a,r);return h&&h.offsets.some(t=>t>0)&&kr(h.poly,h.offsets)||c.map(t=>[t[0],t[1]])}function Er(t){const e=t.map(t=>[t[0],t[1]]);return e.push([t[0][0],t[0][1]]),[e]}function Ir(t,e,i,s=[],n,o,a,r=1,l=[]){if(!e?.length&&!l.length)return null;const c=[];let h=0;for(const s of t||[]){if(!s?.id)continue;const l=Cr(t,s.id,e,i,n,o,a,r);if(!l||l.poly.length<3||!l.offsets.some(t=>t>0))continue;for(const t of l.offsets)t>0&&(h=Math.max(h,2*t));const d=Fr(l.poly,l.offsets),u=kr(l.poly,l.offsets);d&&c.push({outset:d,inset:u})}for(const t of l){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);if(e.length){const s=Math.min(Math.max(...e)-Math.min(...e),Math.max(...i)-Math.min(...i)),n=Math.min(...t.map((e,i)=>{const s=t[(i+1)%t.length];return Math.hypot(s[0]-e[0],s[1]-e[1])}));h=Math.max(h,t.length>16?s:n)}}const d=function(t,e,i,s,n,o,a){if(!e?.length||!i?.length)return[];const r=4*vr(s,a),l=new Map;for(const r of Dr(t,e,i,s,n,o,a))!r.open&&r.half>0&&!l.has(r.key)&&l.set(r.key,r);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])<=r)||h.push(e);const d=[],u=(t,e)=>{let i=0,s=0;if(Math.hypot(t.a[0]-e[0],t.a[1]-e[1])<=r)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])<=r))return null;i=t.a[0]-t.b[0],s=t.a[1]-t.b[1]}const n=Math.hypot(i,s);return n>r?[i/n,s/n]:null};for(const t of h){const e=c.map(e=>({iv:e,u:u(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(a>0?[t.slice(),h,p,u]:[t.slice(),u,p,h])}}return d}(t,e,i,n,o,a,r),u=s.length?Or(t,e,i,n,o,a,r):null;try{const t=t=>{const e=Er(t.outset);return t.inset?Ze(e,Er(t.inset)):e};let e=c.length?t(c[0]):null;for(let i=1;i0))continue;const i=qr(u,t,!0);if(!i.negative&&!i.positive)continue;const s=t.angle*Math.PI/180,o=Math.cos(s),a=Math.sin(s),l=-a,c=o,d=t.length/2,p=1.25*Math.max(h,n*r),_=[[t.x-o*d-l*p,t.y-a*d-c*p],[t.x+o*d-l*p,t.y+a*d-c*p],[t.x+o*d+l*p,t.y+a*d+c*p],[t.x-o*d+l*p,t.y-a*d+c*p]];e&&(e=Ze(e,Er(_)))}for(const t of l)t.length<3||(e=e?Ye(e,Er(t)):[Er(t)]);return{geom:e||[],depthUnits:h}}catch{return null}}function zr(t,e,i,s=[],n,o,a,r=1,l=[]){if(!e?.length&&!l.length)return null;const c=Ir(t,e,i,s,n,o,a,r,l),h=c?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?" ":"")+$r(i.map(t=>[t[0],t[1]])))}return e}(c.geom):"";if(c&&h)return{d:h,depthUnits:c.depthUnits,fillRule:"evenodd"};if(c)return null;const d=function(t,e,i,s,n,o,a=1){if(!e?.length)return[];const r=[];for(const l of t||[]){if(!l?.id)continue;const c=Cr(t,l.id,e,i,s,n,o,a);if(!c||c.poly.length<3||!c.offsets.some(t=>t>0))continue;const h=Fr(c.poly,c.offsets),d=kr(c.poly,c.offsets);if(!h||!d)continue;const u=`${$r(h)} ${$r(xr(d))}`;let p="",_="outer",m=0,g=0;for(let t=0;t0){p=sr(c.poly[t],c.poly[(t+1)%c.poly.length],s,a),_=c.kinds[t]||"outer",m=c.cms[t],g=Qa(m,n,o);break}r.push({d:u,key:p,kind:_,cm:m,depthUnits:g})}return r}(t,e,i,n,o,a,r),u=l.map(t=>$r(t)).join(" ");if(!d.length&&!u)return null;let p=0;for(const t of d)p=Math.max(p,t.depthUnits);return{d:[d.map(t=>t.d).join(" "),u].filter(Boolean).join(" "),depthUnits:p,fillRule:"nonzero"}}function Fr(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]]);xr(t),e.slice().reverse();const s=[];for(let n=0;n0||r>0)){s.push([t[n][0],t[n][1]]);continue}if(br(b,w)){const e=t[n],i=[e[0]-l*a,e[1]-c*a],o=[e[0]-h*r,e[1]-d*r];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=wr(k,b,$,w),S=Math.max(a,r,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}a>0&&s.push([t[n][0]-l*a,t[n][1]-c*a]),r>0&&s.push([t[n][0]-h*r,t[n][1]-d*r])}return s.length>=3?s:null}function Nr(t,e,i,s,n,o,a=1){const r=[];for(const l of t||[]){const c=ii(l);if(c&&c.length>=3){const h=Cr(t,l.id,e,i,s,n,o,a),d=h&&h.offsets.some(t=>t>0)?Fr(h.poly,h.offsets):null,u=d||c;r.push({poly:u.map(t=>t.join(",")).join(" ")})}else l&&null!=l.x&&null!=l.y&&null!=l.w&&null!=l.h&&r.push({rect:{x:l.x,y:l.y,w:l.w,h:l.h,rx:.03*Math.min(l.w,l.h)}})}return r}function Or(t,e,i,s,n,o,a=1){const r=[];for(const l of t||[]){if(!l?.id)continue;const c=Cr(t,l.id,e,i,s,n,o,a);if(!c)continue;const h=Math.abs(wi(c.poly));for(let t=0;t[Math.max(e,t.x0),Math.min(i,t.x1)]).filter(t=>t[1]-t[0]>s).sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(!n.length)return{coverage:0,full:!1};let o=n[0][0],a=n[0][1],r=0,l=o<=e+s;for(let t=1;t=i-s,{coverage:r,full:l}}function Hr(t,e){return Number(e.full)-Number(t.full)||t.faceDistance-e.faceDistance||t.area-e.area||t.roomId.localeCompare(e.roomId)}function qr(t,e,i=!1){const s=Number(e?.x),n=Number(e?.y),o=Number(e?.angle),a=Number(e?.length);if(!([s,n,o,a].every(Number.isFinite)&&a>0))return{negative:null,positive:null};const r=o*Math.PI/180,l=Math.cos(r),c=Math.sin(r),h=-c,d=l,u=a/2,p=Math.max(1e-9,t.adjacencyEps),_=new Map;let m=0;for(const e of t.edges){if(i&&!(e.half>0))continue;if(!ur(e.a,e.b,o))continue;const[t,a]=er(e.a,e.b);if(Math.abs((s-e.a[0])*a-(n-e.a[1])*t)>p)continue;const r=(e.a[0]-s)*l+(e.a[1]-n)*c,g=(e.b[0]-s)*l+(e.b[1]-n)*c,f=Math.max(-u,Math.min(r,g)),v=Math.min(u,Math.max(r,g));if(v-f<=p)continue;const y=e.inward[0]*h+e.inward[1]*d>=0?1:-1,b=((e.a[0]+e.b[0])/2-s)*h+((e.a[1]+e.b[1])/2-n)*d,w=Math.abs(b+y*e.half),k=`${y}|${e.roomId}`,$={x0:f,x1:v,half:e.half,cm:e.cm,key:e.key,axis:[t,a]},x=_.get(k);x?(x.pieces.push($),x.faceDistance=Math.min(x.faceDistance,w)):_.set(k,{roomId:e.roomId,side:y,order:m++,pieces:[$],faceDistance:w,area:e.area,coverage:0,full:!1})}for(const t of _.values()){const e=Lr(t.pieces,-u,u,p);t.coverage=e.coverage,t.full=e.full}const g=t=>{const e=[..._.values()].filter(e=>e.side===t&&e.coverage>p);return e.sort(Hr),e[0]||null};return{negative:g(-1),positive:g(1)}}function Br(t,e){const i=qr(t,e),s=[i.negative,i.positive].filter(t=>!!t).sort((t,e)=>t.order-e.order);if(!s.length)return{ox:0,oy:0,cm:0,side:-1};const n=s[0],o=e.flip_v?-n.side:n.side,a=(-1===o?i.negative:i.positive)||n,r=[...a.pieces].sort((t,e)=>(t.x0<=0&&t.x1>=0?0:Math.min(Math.abs(t.x0),Math.abs(t.x1)))-(e.x0<=0&&e.x1>=0?0:Math.min(Math.abs(e.x0),Math.abs(e.x1)))||e.x1-e.x0-(t.x1-t.x0)||t.key.localeCompare(e.key))[0];if(!(r.half>0&&r.cm>0))return{ox:0,oy:0,cm:0,side:o};const l=e.angle*Math.PI/180,c=-Math.sin(l),h=Math.cos(l);return{ox:c*o*r.half,oy:h*o*r.half,cm:r.cm,side:o}}function Ur(t,e){const i=1e-9,s=e.filter(t=>Number.isFinite(t.x0)&&Number.isFinite(t.x1)&&Number.isFinite(t.half)&&t.x1>t.x0&&t.half>0);if(!s.length)return"";const n=s.flatMap(t=>[t.x0,t.x1]).sort((t,e)=>t-e),o=[];for(const t of n){const e=o[o.length-1];(void 0===e||t>e+i)&&o.push(t)}const a=[];for(let t=0;t+1e+i))continue;const r=(e+n)/2,l=s.reduce((t,e)=>r>=e.x0-i&&r<=e.x1+i?Math.max(t,e.half):t,0);if(!(l>0))continue;const c=a[a.length-1];c&&e<=c.x1+i&&Math.abs(l-c.half)<=i?c.x1=n:a.push({x0:e,x1:n,half:l})}const r=[];for(const t of a){const e=r[r.length-1],s=e?.[e.length-1];s&&t.x0<=s.x1+i?(t.x0=s.x1,e.push(t)):r.push([t])}return r.map(e=>{const i=e[0],s=e[e.length-1],n=Math.min(.02*Math.min(...e.map(t=>t.half)),.05),o=-t*n,a=[];if(1===t){a.push(`M ${i.x0} ${o} L ${s.x1} ${o}`);for(let t=e.length-1;t>=0;t--){const i=e[t];a.push(`L ${i.x1} ${i.half} L ${i.x0} ${i.half}`)}}else{a.push(`M ${s.x1} ${o} L ${i.x0} ${o}`);for(const t of e)a.push(`L ${t.x0} ${-t.half} L ${t.x1} ${-t.half}`)}return a.push("Z"),a.join(" ")}).join(" ")}function Wr(t,e,i,s){if(!s)return i;const n=t.angle*Math.PI/180,o=Math.cos(n),a=Math.sin(n),r=[],l=1e-9;for(const n of i){const[i,c]=n.axis,h=o*i+a*c;if(Math.abs(h)<=l)continue;const d=t.x*i+t.y*c,u=d+h*n.x0,p=d+h*n.x1,_=Math.min(u,p),m=Math.max(u,p),g=`${n.key}|${e}`,f=s.get(g)||[];let v=[[_,m]];for(const[t,e]of f){const i=[];for(const[s,n]of v)e<=s+l||t>=n-l?i.push([s,n]):(t>s+l&&i.push([s,Math.min(n,t)]),et[0]-e[0]||t[1]-e[1]),b=[];for(const t of y){const e=b[b.length-1];e&&t[0]<=e[1]+l?e[1]=Math.max(e[1],t[1]):b.push([t[0],t[1]])}s.set(g,b)}return r}function jr(t,e){const i=new Map;return e.map(e=>function(t,e,i){const s=qr(t,e,!0),n=s.negative,o=s.positive;if(!n&&!o)return null;let a;if(n&&o)a=[{candidate:n,side:-1},{candidate:o,side:1}];else{const t=n||o;a=[{candidate:t,side:-1},{candidate:t,side:1}]}const r=a.map(({candidate:t,side:s})=>({candidate:t,side:s,pieces:Wr(e,s,t.pieces,i)})),l=r.map(({candidate:t,side:e,pieces:i})=>({side:e,roomId:t.roomId,d:Ur(e,i)})),c=r.flatMap(({pieces:t})=>t);if(!c.length)return null;const h=Math.max(...c.map(t=>t.half));return{faces:l,minY:-h,maxY:h,wallKey:[...new Set(c.map(t=>t.key))].sort().join("|")}}(t,e,i))}const Gr=150;function Vr(t){return Number.isFinite(t)?Math.max(1,Math.min(Gr,t)):1}function Kr(t){return((Number.isFinite(Number(t))?Number(t):0)%90+90)%90}const Yr=t=>{const e=t.map(t=>[t[0],t[1]]);return!e.length||e[0][0]===e[e.length-1][0]&&e[0][1]===e[e.length-1][1]||e.push([...e[0]]),[e]};function Jr(t){const e=[];for(const i of t||[])for(const t of i||[]){const i=(t||[]).filter(t=>Array.isArray(t)&&t.length>=2);i.length<4||e.push(`M ${i.slice(0,-1).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`)}return e.join(" ")}function Zr(t,e,i,s,n){const o=e[0]-t[0],a=e[1]-t[1],r=Math.hypot(o,a);if(!(r>1e-9))return null;const l=Qa(i,s,n)/2,c=-a/r*l,h=o/r*l;return[[t[0]+c,t[1]+h],[e[0]+c,e[1]+h],[e[0]-c,e[1]-h],[t[0]-c,t[1]-h]]}function Xr(t,e,i){const s=Number(e)>0?Number(e):5,n=Vr(t.cm)/s*i,o=t.center[0],a=t.center[1];if("circle"===t.shape){const t=n/2;return Array.from({length:96},(e,i)=>{const s=i/96*Math.PI*2;return[o+Math.cos(s)*t,a+Math.sin(s)*t]})}const r=n/2,l=Kr(t.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l);return[[-r,-r],[r,-r],[r,r],[-r,r]].map(([t,e])=>[o+t*c-e*h,a+t*h+e*c])}function Qr(t,e,i){const s=[];for(let n=0;n+1t.length>=3).map(t=>Yr(t));return e.length?Ye(e[0],...e.slice(1)):null}catch{return null}}const el=t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`;function il(t,e){const i=tl(t.filter(t=>t.length>=3)),s=tl(e.filter(t=>t.length>=3));if(!i)return[];if(!s)return[];try{return function(t){const e=[];for(const i of t||[]){if(nl([i])<=1e-6)continue;const t=[];for(const e of i||[]){const i=(e||[]).filter(t=>Array.isArray(t)&&t.length>=2);i.length<4||t.push(el(i.slice(0,-1)))}t.length&&e.push(t.join(" "))}return e}(Je(i,s))}catch{return[]}}function sl(t,e){if(!e.length)return[Yr(t)];try{const i=tl(e);if(i)return Ze(Yr(t),i)}catch{}let i=[Yr(t)];for(const t of e)if(!(t.length<3))try{i=Ze(i,Yr(t))}catch{}return i}function nl(t){let e=0;for(const i of t||[])if(i?.length){e+=wi(i[0]||[]);for(let t=1;t=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]))}return e}function al(t,e){let i=!1;for(let s=0,n=e.length-1;st[1]!=l>t[1]&&t[0]<(r-o)*(t[1]-a)/(l-a||1e-12)+o&&(i=!i)}return i}function rl(t,e){for(const i of e||[]){const e=i?.[0];if(!e?.length||!al(t,e))continue;let s=!1;for(let e=1;ei)return!1;if(Math.abs(Vr(t.cm)-Vr(e.cm))>1e-6)return!1;if(t.shape!==e.shape)return!0;if("circle"===t.shape||"circle"===e.shape)return!0;const s=Math.abs(Kr(t.angle)-Kr(e.angle));return Math.min(s,90-s)<=1e-6}const cl=1e3;function hl(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 dl=.01,ul=100;function pl(t,e=1e3){const i=hl(t?.plan_aspect,e),s=Number(t?.plan_scale),n=Number.isFinite(s)&&s>0?Math.min(ul,Math.max(dl,s)):1,o=Number(t?.plan_scale_x),a=Number(t?.plan_scale_y),r=Number.isFinite(o)&&o>0?Math.min(ul,Math.max(dl,o)):n,l=Number.isFinite(a)&&a>0?Math.min(ul,Math.max(dl,a)):n,c=Number(t?.plan_x),h=Number(t?.plan_y),d=La(t?.plan_angle);return{x:i.x+(Number.isFinite(c)?xl(c):0)*e,y:i.y+(Number.isFinite(h)?xl(h):0)*e,w:i.w*r,h:i.h*l,...d?{angle:d}:{}}}function _l(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 ml(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=cl,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]*cl,i[1]*e,i[2]*cl,i[3]*e],bg:t.plan_url?{href:$s(t.plan_url),...pl(t,cl)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,..._l(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*cl:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*cl:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*cl,t[1]*e]):void 0}}),room_drafts:(t.room_drafts||[]).map(t=>({id:t.id,points:(t.points||[]).map(t=>[t[0]*cl,t[1]*e]),segments:(t.segments||[]).map(t=>({cm:Number(t.cm)}))})),partitions:(t.partitions||[]).map(t=>({id:t.id,a:[t.a[0]*cl,t.a[1]*e],b:[t.b[0]*cl,t.b[1]*e],cm:Number(t.cm)})),wall_columns:(t.wall_columns||[]).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[t.center[0]*cl,t.center[1]*e],cm:Number(t.cm),..."circle"===t.shape?{}:{angle:Kr(t.angle)}}))}}):[]}const gl=5e3,fl=gl*cl,vl=240,yl=cl/vl,bl=1/vl;function wl(t){if(!Number.isFinite(t))return t;const e=Math.round(t*vl/cl)*cl/vl;return Math.abs(e-t)<=1e-9*yl?t:e}function kl(t){return{x:wl(t.x),y:wl(t.y)}}function $l(t){return Number.isFinite(t)?Math.min(fl,Math.max(-fl,t)):0}function xl(t){return Number.isFinite(t)?Math.min(gl,Math.max(-gl,t)):0}const Sl=1/3,Ml=200;function Cl(t){let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t){const t=Number(o[0]),a=Number(o[1]);Number.isFinite(t)&&Number.isFinite(a)&&(ts&&(s=t),a>n&&(n=a))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:n}}function Dl(t){return t.poly&&t.poly.length?Cl(t.poly):null==t.x||null==t.y?null:Cl([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function Tl(t,e){const i=[];for(const e of t.rooms||[]){const t=Dl(e);t&&i.push(t)}if(t.bg){const e=Cl(ja(t.bg));e&&i.push(e)}for(const t of e||[])if(Array.isArray(t)){const e=Cl([t]);e&&i.push(e)}else i.push(t);return i}const Pl=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},Rl=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 Al(t,e){let{x:i,y:s,w:n,h:o}=t;n<30&&(i=i+n/2-100,n=Ml),o<30&&(s=s+o/2-100,o=Ml);const a=Math.max(n,o)*e;return{x:i-a,y:s-a,w:n+2*a,h:o+2*a}}function El(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)<=fl&&Math.abs(t.maxX)<=fl&&Math.abs(t.minY)<=fl&&Math.abs(t.maxY)<=fl);if(!o.length)return{core:null,all:null,outliers:0};const a=Rl(o);if(o.length<4){const t=Al(a,i);return{core:t,all:t,outliers:0}}const r=o.map(t=>(t.minX+t.maxX)/2),l=o.map(t=>(t.minY+t.maxY)/2),c=Pl(r),h=Pl(l),d=o.map((t,e)=>Math.max(Math.abs(r[e]-c),Math.abs(l[e]-h))),u=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),p=d.map(t=>t>s*u),_=p.filter(Boolean).length,m=_&&_<=o.length*Sl?o.filter((t,e)=>!p[e]):o;return{core:Al(Rl(m)||a,i),all:Al(a,i),outliers:m===o?0:_}}function Il(t,e,i=.05){const s=El(Tl(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,cl,cl];return{x:n[0],y:n[1],w:n[2],h:n[3]}}function zl(t){const e=Il(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function Fl(t){const e=[];for(const i of t.rooms||[]){const t=Dl(i);t&&e.push(t)}const i=El(e,{pad:0}).core;return i?Math.max(cl,Math.min(fl,Math.max(i.w,i.h))):cl}function Nl(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*Fl(e)*o/n}const Ol=[1,2,5,10,20,50,100,200,500,1e3];function Ll(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 Hl(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 ql=["furniture","appliance","sanitary","other"],Bl=[{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]]}],Ul=new Map(Bl.map(t=>[t.id,t]));function Wl(t){return t&&Ul.get(t)||null}function jl(t){return Bl.filter(e=>e.group===t)}function Gl(t,e,i=yl,s=1e3){const n=Number(e)>0?Number(e):5;return(Number(t)||0)/n*i/s}const Vl=5e-4,Kl=gl;function Yl(t){return Number.isFinite(t)?Math.max(Vl,Math.min(Kl,t)):Vl}const Jl=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function Zl(t,e,i){const s=Wl(t);if(!(s&&e>0&&i>0))return"";const n=t=>Jl(t*e),o=t=>Jl(t*i),a=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,r]=t;a.push(`M${n(e)} ${o(i)}H${n(e+s)}V${o(i+r)}H${n(e)}Z`)}else if("l"===t[0]){const[,e,i,s,r]=t;a.push(`M${n(e)} ${o(i)}L${n(s)} ${o(r)}`)}else if("e"===t[0]){const[,s,r,l,c]=t;a.push(`M${n(s-l)} ${o(r)}A${Jl(l*e)} ${Jl(c*i)} 0 0 1 ${n(s+l)} ${o(r)}A${Jl(l*e)} ${Jl(c*i)} 0 0 1 ${n(s-l)} ${o(r)}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 Ql(t,e,i,s,n,o=0){let a=null,r=n;for(const n of s){const[s,l,c,h]=n,d=c-s,u=h-l,p=d*d+u*u;if(!p)continue;const _=Math.sqrt(p);let m=((t-s)*d+(e-l)*u)/p;m=Math.max(0,Math.min(1,m));let g=s+m*d,f=l+m*u;const v=Math.hypot(t-g,e-f);if(!(v0){const t=Math.round(m*_/o)*o;g=s+t/_*d,f=l+t/_*u}a={cx:g+y*(i/2),cy:f+b*(i/2),angle:Xl(180*Math.atan2(-y,b)/Math.PI),dist:v}}return a}function tc(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function ec(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function ic(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function sc(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!ic(t.a)||!ic(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 nc(t,e){const i=e[0],s=e[1],n=e[2]-i,o=e[3]-s,a=n*n+o*o;if(a<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let r=((t[0]-i)*n+(t[1]-s)*o)/a;r=Math.max(0,Math.min(1,r));const l=[i+r*n,s+r*o];return{q:l,t:r,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function oc(t,e){return nc(t,e).q}function ac(t,e,i,s,n){const o=nc(t,e).q;let a=o,r=1/0;for(const t of i){const e=Math.hypot(o[0]-t[0],o[1]-t[1]);e<=n&&e0?s:1,m=Math.round(p/_)*_,g=Math.max(0,Math.min(u,m))/u;return[l+h*g,c+d*g]}function rc(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt?.id),a=sc(e);if(a.length)return gc(a,t,i,s).map(t=>ec(t,i));if(!n)return[];const r=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;ttc([t[0],t[1]],[t[2],t[3]],e))}function hc(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])),a=(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;tTs(i,t)<4*s)){a(n[t].id,n[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}function dc(t,e,i,s=0){const n=e[2]-e[0],o=e[3]-e[1],a=Math.hypot(n,o);if(!(a>1e-9))return null;const r=n/a,l=o/a,c=(t[0]-e[0])*r+(t[1]-e[1])*l;if(c<-s||c>a+s)return null;return Math.abs((t[0]-e[0])*l-(t[1]-e[1])*r)>i?null:Ts(t,e)}function uc(t,e,i){const s=t[2]-t[0],n=t[3]-t[1],o=e[2]-e[0],a=e[3]-e[1],r=Math.hypot(s,n),l=Math.hypot(o,a);return r>1e-9&&l>1e-9&&(!(Math.abs(s/r*(a/l)-n/r*(o/l))>1e-6)&&(Ts([e[0],e[1]],t)<=4*i||Ts([t[0],t[1]],e)<=4*i))}function pc(t,e,i){if(!t.length)return null;t.sort((t,e)=>t.distance-e.distance);const s=t[0];for(let n=1;ne)break;if(o.semantic!==s.semantic||!uc(s.seg,o.seg,i))return"ambiguous"}return s}function _c(t,e,i,s,n=1,o=15){const a=function(t,e,i,s,n=1,o=15){const[a,r]=er([e[0],e[1]],[e[2],e[3]]);let l=0,c=1/0;const h=[(e[0]+e[2])/2,(e[1]+e[3])/2];for(const e of i){const[i,o]=er([e[0],e[1]],[e[2],e[3]]);if(Math.abs(a*o-r*i)>.05)continue;const d=hr(t,[e[0],e[1]],[e[2],e[3]],s,n);if(!(d>0))continue;const u=[(e[0]+e[2])/2,(e[1]+e[3])/2],p=Math.hypot(u[0]-h[0],u[1]-h[1]);p0?l:o}(t,e,i,s,n,o);return _r(t,[e[0],e[1]],[e[2],e[3]],a,s,n)}function mc(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(!(Ts(n,o)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Ts([o[0],o[1]],t)>i))for(const e of[[o[0],o[1]],[o[2],o[3]]])Ts(e,t)<=2*i&&s.push(e);return s}function gc(t,e,i,s){const n=sc(t);if(!n.length)return[];const o=rc(e,s);if(!o.length)return[];const a=[],r=Math.max(4*s,1e-6);for(const t of n){const e=ec(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 a=(t[0]-n)*u+(t[1]-l)*p,c=(t[2]-n)*u+(t[3]-l)*p,h=Math.max(0,Math.min(a,c)),m=Math.min(d,Math.max(a,c));if(m-ht.lo-e.lo||t.hi-e.hi);const i=[];for(const t of e){const e=i[i.length-1];e&&t.lo<=e.hi+r?e.hi=Math.max(e.hi,t.hi):i.push({...t})}for(const e of i){const i=[n+u*e.lo,l+p*e.lo],s=[n+u*e.hi,l+p*e.hi];Math.hypot(s[0]-i[0],s[1]-i[1])i.pair===t&&Math.abs(i.ux*a-i.uy*o)<=1e-6&&Math.abs((e[0]-i.origin[0])*i.uy-(e[1]-i.origin[1])*i.ux)<=c&&Math.abs((e[2]-i.origin[0])*i.uy-(e[3]-i.origin[1])*i.ux)<=c);h||(h={pair:t,origin:[e[0],e[1]],ux:o,uy:a,ranges:[]},l.push(h));const d=(e[0]-h.origin[0])*h.ux+(e[1]-h.origin[1])*h.uy,u=(e[2]-h.origin[0])*h.ux+(e[3]-h.origin[1])*h.uy;h.ranges.push({lo:Math.min(d,u),hi:Math.max(d,u)})}const h=[];for(const t of l){t.ranges.sort((t,e)=>t.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t.ranges){const t=e[e.length-1];t&&i.lo<=t.hi+r?t.hi=Math.max(t.hi,i.hi):e.push({...i})}for(const s of e){const e=[t.origin[0]+t.ux*s.lo,t.origin[1]+t.uy*s.lo],n=[t.origin[0]+t.ux*s.hi,t.origin[1]+t.uy*s.hi];Math.hypot(n[0]-e[0],n[1]-e[1])>=r&&h.push(tc(e,n,i))}}return h}function fc(t,e,i,s){const n=sc(t);if(!n.length)return[];if(e.length!==i.length)return n;const o=[];for(const t of n){const n=ec(t,s),a=[n[0],n[1]],r=[n[2],n[3]];let l=a,c=r;for(let t=0;t{const e=nc(t,[s[0],s[1],n[0],n[1]]);if(e.d>.001)return t;const i=h[0]-o[0],a=h[1]-o[1];return[o[0]+i*e.t,o[1]+a*e.t]};if(nc([(a[0]+r[0])/2,(a[1]+r[1])/2],[s[0],s[1],n[0],n[1]]).d<.01){l=d(a),c=d(r);break}}o.push(tc(l,c,s))}return o}const vc=15e3,yc=2e3,bc=new WeakMap;function wc(t,e){let i=bc.get(t);if(!i){const e=new Set;i={hiddenAt:"hidden"===t.visibilityState?Date.now():0,token:0,subscribers:e,onVisibility:()=>{const e=bc.get(t);if(!e)return;const i=Date.now();if("hidden"===t.visibilityState){e.hiddenAt||(e.hiddenAt=i);const t={kind:"hidden",token:e.token,at:i,hiddenFor:0,long:!1};for(const i of[...e.subscribers])i(t);return}const s=e.hiddenAt?Math.max(0,i-e.hiddenAt):0;e.hiddenAt=0,e.token++;const n={kind:"visible",token:e.token,at:i,hiddenFor:s,long:s>=vc};for(const t of[...e.subscribers])t(n)},onPageShow:e=>{const i=bc.get(t);if(!i)return;const s=Date.now();i.token++;const n={kind:"pageshow",token:i.token,at:s,hiddenFor:0,long:!!e.persisted,persisted:!!e.persisted};for(const t of[...i.subscribers])t(n)}},bc.set(t,i),t.addEventListener("visibilitychange",i.onVisibility),t.defaultView?.addEventListener("pageshow",i.onPageShow)}return i.subscribers.add(e),()=>{const i=bc.get(t);i&&(i.subscribers.delete(e),i.subscribers.size||(t.removeEventListener("visibilitychange",i.onVisibility),t.defaultView?.removeEventListener("pageshow",i.onPageShow),bc.delete(t)))}}const kc={now:()=>Date.now(),setTimeout:(t,e)=>window.setTimeout(t,e),clearTimeout:t=>window.clearTimeout(t),requestAnimationFrame:t=>window.requestAnimationFrame(t),cancelAnimationFrame:t=>window.cancelAnimationFrame(t)};class $c{constructor(t,e=kc){this.onChange=t,this.clock=e,this._state="steady",this._token=0,this._frameFingerprint="",this._hasCompleteFrame=!1,this._overlayPhase="none",this._recoveryReason=null,this._overlayTimer=0,this._overlayRaf=0,this._overlayOpaqueAt=0,this._barrierRafs=new Set,this._trace=[],this._disposed=!1}get state(){return this._state}get token(){return this._token}get frameFingerprint(){return this._frameFingerprint}get hasCompleteFrame(){return this._hasCompleteFrame}get overlayPhase(){return this._overlayPhase}get overlayVisible(){return"none"!==this._overlayPhase}get overlayBlocksInteraction(){return this.overlayVisible}get recoveryReason(){return this._recoveryReason}get trace(){return this._trace.map(t=>({...t,...t.stage?{stage:[...t.stage]}:{}}))}note(t,e={}){this.record(t,void 0,e)}record(t,e,i={}){this._trace.push({at:this.clock.now(),token:this._token,event:t,state:this._state,...e?{reason:e}:{},...i}),this._trace.length>80&&this._trace.splice(0,this._trace.length-80)}changed(){this._disposed||this.onChange()}adoptCompleteFrame(t){t&&(this._hasCompleteFrame=!0,this._frameFingerprint=t,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-adopted"),this.changed())}markCompleteFrame(t){t&&(this._hasCompleteFrame=!0,this._frameFingerprint=t,this._state="steady",this._recoveryReason=null,this.clearOverlay(),this.record("frame-complete"),this.changed())}refreshCompleteFrame(t){!this._disposed&&this._hasCompleteFrame&&"steady"===this._state&&t&&t!==this._frameFingerprint&&(this._frameFingerprint=t,this.record("frame-refreshed"))}visibility(t){return"hidden"===t.kind?(this.record("visibility-hidden"),this._token):t.long||"visible"!==t.kind&&"pageshow"!==t.kind?this.beginCandidate("pageshow"===t.kind?"pageshow":"long-resume"):(this.record("pageshow"===t.kind?"pageshow-noop":"visibility-visible-quick"),this._token)}beginCandidate(t,e="plan"){return this._token++,this._recoveryReason=e,this._hasCompleteFrame?(this._state="connection"===e?"offline-stale":"holding",this.clearOverlay()):this.overlayVisible?this._state="overlay-visible":(this._state="overlay-pending",this.scheduleOverlay(e)),this.record("candidate-start",t),this.changed(),this._token}connectionLost(){return this.beginCandidate("connection-lost","connection")}candidateReady(t){return!this._disposed&&t===this._token&&(this._state="candidate-ready",this.record("candidate-ready"),this.changed(),!0)}async commitAfterPaint(t,e){if(this._disposed||t!==this._token)return!1;let i=!0;const s=(async()=>{for(await e.updateComplete();i&&!this._disposed&&t===this._token;)if(e.stageValid()&&e.assetsReady()){if(await this.nextFrame(),t!==this._token||!e.stageValid()||!e.assetsReady())continue;if(await this.nextFrame(),t===this._token&&e.stageValid()&&e.assetsReady())return!0}else await this.nextFrame();return!1})();let n=0;const o=new Promise(t=>{n=this.clock.setTimeout(()=>t({ready:!1,timedOut:!0}),yc)}),a=await Promise.race([s.then(t=>({ready:t,timedOut:!1})),o]);return i=!1,this.clock.clearTimeout(n),!this._disposed&&t===this._token&&(!(!a.ready&&!a.timedOut)&&(a.ready?(this._hasCompleteFrame=!0,this._frameFingerprint=e.frameFingerprint(),this.record("paint-barrier"),this.finishOverlayAfterCommit(),!0):(this.record("paint-barrier-timeout"),this._hasCompleteFrame?(this._state="connection"===this._recoveryReason?"offline-stale":"steady",this.clearOverlay()):(this._state="recovery-error","opaque"!==this._overlayPhase&&this.showOverlayNow()),this.changed(),!1)))}retry(t=this._recoveryReason||"plan"){return this.beginCandidate("retry",t)}nextFrame(){return new Promise(t=>{const e=this.clock.requestAnimationFrame(()=>{this._barrierRafs.delete(e),t()});this._barrierRafs.add(e)})}scheduleOverlay(t){this.clearOverlayTimer(),this._recoveryReason=t,this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._hasCompleteFrame||"overlay-pending"!==this._state&&"candidate-ready"!==this._state||(this._state="overlay-visible",this._overlayPhase="entering",this.record("overlay-enter"),this.changed(),this._overlayRaf=this.clock.requestAnimationFrame(()=>{this._overlayRaf=0,"entering"===this._overlayPhase&&(this._overlayPhase="fading-in",this.changed(),this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,"fading-in"===this._overlayPhase&&(this._overlayPhase="opaque",this._overlayOpaqueAt=this.clock.now(),this.record("overlay-opaque"),this.changed())},150))}))},150)}showOverlayNow(){this.clearOverlayTimer(),this._overlayPhase="opaque",this._overlayOpaqueAt=this.clock.now(),this.record("overlay-error")}finishOverlayAfterCommit(){if(this.clearOverlayTimer(),"none"===this._overlayPhase||"entering"===this._overlayPhase||"fading-in"===this._overlayPhase)return this.clearOverlay(),this._state="steady",this._recoveryReason=null,this.record("candidate-committed"),void this.changed();const t=Math.max(0,this._overlayOpaqueAt+250-this.clock.now());this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._overlayPhase="leaving",this.record("overlay-leave"),this.changed(),this._overlayTimer=this.clock.setTimeout(()=>{this._overlayTimer=0,this._overlayPhase="none",this._state="steady",this._recoveryReason=null,this.record("candidate-committed"),this.changed()},180)},t)}clearOverlayTimer(){this._overlayTimer&&this.clock.clearTimeout(this._overlayTimer),this._overlayTimer=0}clearOverlay(){this.clearOverlayTimer(),this._overlayRaf&&this.clock.cancelAnimationFrame(this._overlayRaf),this._overlayRaf=0,this._overlayPhase="none",this._overlayOpaqueAt=0}dispose(){this._disposed=!0,this._token++,this.clearOverlay();for(const t of this._barrierRafs)this.clock.cancelAnimationFrame(t);this._barrierRafs.clear()}}function xc(t){let e=2166136261,i=2654435769;const s=new WeakSet,n=t=>{for(let s=0;s>>0)+(i>>>2)}},o=t=>{if(null===t)return void n("n");const e=typeof t;if("string"===e)return void n(`s${t.length}:${t}`);if("number"===e)return void n(`d${Number.isNaN(t)?"NaN":String(t)}`);if("boolean"===e)return void n(t?"t":"f");if("undefined"===e)return void n("u");if("object"!==e)return void n(`${e}:${String(t)}`);const i=t;if(s.has(i))n("[cycle]");else{if(s.add(i),Array.isArray(t)){n("[");for(const e of t)o(e);return n("]"),void s.delete(i)}n("{");for(const e of Object.keys(t).sort())n(e),o(t[e]);n("}"),s.delete(i)}};return o(t),`${(e>>>0).toString(16).padStart(8,"0")}${(i>>>0).toString(16).padStart(8,"0")}`}function Sc(t){return xc(t)}const Mc=new WeakMap;class Cc{constructor(t,e=()=>Date.now()){this.onUpdate=t,this.now=e,this.fallbackAuthority={},this.referenceOwner={},this.referenced=new Set,this.queued=new Set,this.disposed=!1,this.sharedUpdate=()=>{this.disposed||this.onUpdate()}}bind(t){const e=((t,e)=>{const i=t?.connection||t?.callWS||e;return!i||"object"!=typeof i&&"function"!=typeof i?e:i})(t,this.fallbackAuthority);return this.shared&&this.authority===e||(this.shared&&(this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner)),this.authority=e,this.shared=(t=>{let e=Mc.get(t);return e||(e={signed:{},queued:new Set,inFlight:new Map,retry:new Map,listeners:new Set,settlers:new Set,references:new Map},Mc.set(t,e)),e})(e),this.disposed||(this.shared.listeners.add(this.sharedUpdate),this.shared.references.set(this.referenceOwner,new Set(this.referenced)))),this.shared}start(t,e){this.disposed=!1,this.referenced=new Set(e());const i=this.bind(t());i.listeners.add(this.sharedUpdate),i.references.set(this.referenceOwner,new Set(this.referenced)),this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(t(),e()),288e5)}dispose(){if(this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.shared){this.shared.listeners.delete(this.sharedUpdate),this.shared.references.delete(this.referenceOwner);for(const t of this.queued)this.shared.queued.delete(t)}this.queued.clear()}invalidate(t){const e=this.bind(t);clearTimeout(this.batchTimer),this.queued.clear(),e.queued.clear(),e.inFlight.clear(),e.retry.clear(),e.signed={}}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(t,e){const i=$s(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.bind(t);this.referenced.add(i),s.references.set(this.referenceOwner,new Set(this.referenced));const n=s.signed[i],o=n?this.now()-n.at:1/0;return o{const e=[...this.queued];this.queued.clear();for(const t of e)i.queued.delete(t);this.sign(t,e)},30))}sign(t,e){if(!e.length||!t?.callWS)return;const i=this.bind(t);for(const s of function(t,e){const i=Math.max(1,Math.floor(e)),s=[];for(let e=0;e{const s=i.inFlight.get(t);return!(void 0!==s&&e-s<15e3)&&(i.inFlight.set(t,e),!0)});n.length&&t.callWS({type:"houseplan/content/sign",paths:n}).then(t=>{const e=this.now();let s=0,o=!1;for(const a of n){const n=t?.urls?.[a];if("string"==typeof n&&n){const t=i.signed[a];t?.loaded&&t.url!==n?(t.pending={url:n,at:e},this.preloadReplacement(i,a,n,e)):(i.signed[a]={url:n,at:e,loaded:t?.url===n&&!!t.loaded},o=!0),i.retry.delete(a),s++}else this.backOff(i,a)}if(s&&(this.trimShared(i),o))for(const t of[...i.listeners])t()}).catch(()=>{for(const t of n)this.backOff(i,t)}).finally(()=>{for(const t of n)i.inFlight.get(t)===e&&i.inFlight.delete(t);for(const t of[...i.settlers])t()})}}preloadReplacement(t,e,i,s){const n=()=>{const n=t.signed[e];if(n?.pending?.url===i){t.signed[e]={url:i,at:s,loaded:!0},t.retry.delete(e),this.trimShared(t);for(const e of[...t.listeners])e()}},o=()=>{const s=t.signed[e];s?.pending?.url===i&&(delete s.pending,this.backOff(t,e))};if("undefined"==typeof Image)return void n();const a=new Image;a.onload=()=>{("function"==typeof a.decode?a.decode():Promise.resolve()).then(n).catch(o)},a.onerror=o,a.src=i}preloadCurrentImage(t,e,i){if(i.loaded)return Promise.resolve(!0);if(i.preload)return i.preload;if("undefined"==typeof Image)return i.loaded=!0,Promise.resolve(!0);const s=i.url;return i.preload=new Promise(n=>{const o=new Image;let a=!1;const r=o=>{if(a)return;a=!0,clearTimeout(l);const r=t.signed[e];if(r===i&&delete r.preload,o&&r===i&&r.url===s){r.loaded=!0;for(const e of[...t.listeners])e();n(!0)}else o||r!==i||this.backOff(t,e),n(!1)};o.onload=()=>{("function"==typeof o.decode?o.decode():Promise.resolve()).then(()=>r(!0)).catch(()=>r(!1))},o.onerror=()=>r(!1);const l=setTimeout(()=>r(!1),yc);o.src=s}),i.preload}prepareImage(t,e){const i=$s(e);if(!i.startsWith("/api/houseplan/content/"))return Promise.resolve(!0);const s=this.bind(t),n=()=>{const e=s.signed[i];if(e&&this.now()-e.atthis.now()&&!s.inFlight.has(i)&&!s.queued.has(i)?Promise.resolve(!1):null},o=n();return o?(a=o,new Promise(t=>{let e=!1;const i=i=>{e||(e=!0,clearTimeout(s),t(i))},s=setTimeout(()=>i(!1),yc);a.then(i).catch(()=>i(!1))})):new Promise(t=>{let e=!1,i=null;const o=i=>{e||(e=!0,clearTimeout(r),s.settlers.delete(a),t(i))},a=()=>{e||i||(i=n(),i&&i.then(o).catch(()=>o(!1)))},r=setTimeout(()=>o(!1),yc);s.settlers.add(a),a()});var a}backOff(t,e){const i=t.retry.get(e)?.delay||0,s=Math.min(6e4,i?2*i:2e3);t.retry.set(e,{notBefore:this.now()+s,delay:s})}trimShared(t){const e=Object.entries(t.signed);if(e.length<=512)return;const i=new Set(t.inFlight.keys());for(const e of t.references.values())for(const t of e)i.add(t);for(const[t,s]of e)(s.pending||s.preload)&&i.add(t);e.sort((t,e)=>e[1].at-t[1].at);const s=e.filter(([t])=>i.has(t));for(const t of e){if(s.length>=512)break;i.has(t[0])||s.push(t)}t.signed=Object.fromEntries(s)}resign(t,e){const i=this.bind(t);this.referenced=new Set(e),i.references.set(this.referenceOwner,new Set(this.referenced));const s=this.now(),n=new Set;for(const t of i.references.values())for(const e of t)n.add(e);const o={};for(const[t,e]of Object.entries(i.signed))n.has(t)&&s-e.at!!o[t]&&!o[t].pending))}markLoaded(t,e,i){const s=$s(e);if(!s.startsWith("/api/houseplan/content/"))return;const n=this.bind(t).signed[s];if(n&&(!i||n.url===i)&&!n.loaded){n.loaded=!0;for(const e of[...this.bind(t).listeners])e()}}isReady(t,e){const i=$s(e);if(!i.startsWith("/api/houseplan/content/"))return!0;const s=this.bind(t).signed[i];return!!s&&this.now()-s.at{if(null==t||""===t)return null;const e=Number(t);return Number.isFinite(e)?e:null},Rc=t=>{const e=t,i=Pc(Array.isArray(e)?e[0]:e?.x),s=Pc(Array.isArray(e)?e[1]:e?.y);return null==i||null==s?null:[i,s]};function Ac(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=Rc(i);if(!t)return null;e.push(t)}if(e.length>1){const t=e[0],i=e[e.length-1];t[0]===i[0]&&t[1]===i[1]&&e.pop()}if(e.length<3)return null;let i=0,s=0,n=0;for(let t=0;tt+e[0],0)/e.length,e.reduce((t,e)=>t+e[1],0)/e.length]:[s/(3*i),n/(3*i)]}function Ec(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=Rc(i);if(!t)return null;e.push(t)}if(e.length>1){const t=e[0],i=e[e.length-1];t[0]===i[0]&&t[1]===i[1]&&e.pop()}const i=Ac(e);if(!i)return null;const s=e.map(t=>t[0]),n=e.map(t=>t[1]);return{centroid:i,bbox:[Math.min(...s),Math.min(...n),Math.max(...s),Math.max(...n)]}}function Ic(t){let e=t;if(e&&"object"==typeof e&&!Array.isArray(e)){const t=e;e=t.path??t.points??e}if(!Array.isArray(e)||!e.length)return[];let i=(e.some(t=>(t=>Array.isArray(t)?t.length>=2&&!Array.isArray(t[0])&&("object"!=typeof t[0]||null==t[0]):!!t&&"object"==typeof t&&("x"in t||"y"in t))(t))?[e]:e.filter(Array.isArray)).map(t=>t.map(Rc).filter(t=>null!=t)).filter(t=>t.length>=2);i.length>64&&(i=i.slice(-64));if(i.reduce((t,e)=>t+e.length,0)<=4e3)return i.map(t=>t.slice());const s=4e3-2*i.length,n=i.map(t=>t.length-2),o=n.reduce((t,e)=>t+e,0),a=n.map(t=>s*t/o),r=a.map(Math.floor);let l=s-r.reduce((t,e)=>t+e,0);const c=a.map((t,e)=>({index:e,fraction:t-Math.floor(t)})).sort((t,e)=>e.fraction-t.fraction||t.index-e.index);for(let t=0;tfunction(t,e){if(e>=t.length)return t.slice();if(e<=2)return[t[0],t[t.length-1]];const i=[];for(let s=0;st.slice()),i=e[e.length-1];return i?(i.length>2?i.pop():e.pop(),e):[]}function Nc(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=Pc(e.x)&&null!=Pc(e.y)?{x:Pc(e.x),y:Pc(e.y),a:Pc(e.a??e.angle??e.theta)}:null,s=Ic(t.path?.path??t.path?.points??t.path),n=[],o=t.rooms,a=Array.isArray(o)?o.map((t,e)=>[String(t?.id??e),t]):o&&"object"==typeof o?Object.entries(o):[];for(const[t,e]of a){if(!e||"object"!=typeof e)continue;const i=String(e.name??e.label??"").trim(),s=[[Pc(e.cx),Pc(e.cy)],[Pc(e.center?.x),Pc(e.center?.y)],[Pc(e.x),Pc(e.y)]].find(([t,e])=>null!=t&&null!=e);let o=s?.[0]??null,a=s?.[1]??null;const r=Ec(e.outline),l=Pc(e.x0),c=Pc(e.y0),h=Pc(e.x1),d=Pc(e.y1),u=(null!=l&&null!=c&&null!=h&&null!=d?[Math.min(l,h),Math.min(c,d),Math.max(l,h),Math.max(c,d)]:null)||r?.bbox||null;if(null!=o&&null!=a||!r||([o,a]=r.centroid),null!=o&&null!=a||!u||(o=(u[0]+u[2])/2,a=(u[1]+u[3])/2),i&&null!=o&&null!=a){const e={id:t,name:i,cx:o,cy:a};u&&([e.x0,e.y0,e.x1,e.y1]=u),n.push(e)}}const r=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||n.length||s.length?{pos:i,path:s,rooms:n,mapId:r}:null}function Oc(t,e,i){if(!t||!e)return null;const s=e.attributes||{},n=t.split(".")[0]||"",o=null!=i?.platform?String(i.platform):null,a=!!Rc(s.vacuum_position??s.robot_position),r=!(!s.rooms||"object"!=typeof s.rooms),l=Ic(s.path?.path??s.path?.points??s.path).length>0,c=[s.map_name,s.current_map,s.map_index,s.selected_map].some(t=>null!=t),h="xiaomi_cloud_map_extractor"===o,d=r||l||c;let u=null;if(a?u="compatible":h?u="known_xcme_incomplete":d?u="partial":"camera"===n&&(u="camera"),!u)return null;const p=a?"camera"===n?300:200:h?100:d?50:0;return{entityId:t,name:String(s.friendly_name||t),platform:o,category:u,hasPosition:a,hasRooms:r,hasPath:l,hasMapId:c,score:p}}const Lc=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function Hc(t,e){const i=new Map(e.map(t=>[Lc(t.name),t])),s=[],n=[];for(const e of t){const t=i.get(Lc(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,a=0,r=0,l=0,c=0,h=0,d=0,u=0;for(const[[p,_],[m,g]]of t){if(![p,_,m,g].every(Number.isFinite))return null;e+=p*p,i+=p*_,s+=p,n+=_*_,o+=_,a+=1,r+=p*m,l+=_*m,c+=m,h+=p*g,d+=_*g,u+=g}const p=[e,i,s,i,n,o,s,o,a],_=t=>{const[e,i,s,n,o,a,r,l,c]=p,h=e*(o*c-a*l)-i*(n*c-a*r)+s*(n*l-o*r);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-a*l)/h,(s*l-i*c)/h,(i*a-s*o)/h,(a*r-n*c)/h,(e*c-s*r)/h,(s*n-e*a)/h,(n*l-o*r)/h,(i*r-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]]},m=_([r,l,c]),g=_([h,d,u]);if(!m||!g)return null;const f=[m[0],m[1],m[2],g[0],g[1],g[2]];return f.every(Number.isFinite)?f:null}(s);return o?{matrix:o,matched:n,residual:Tc(o,s)}:null}function qc(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(),[a,r]=t[n],[l,c]=t[o],h=l-a,d=c-r,u=Math.hypot(h,d)||1e-9;let p=0,_=-1;for(let e=n+1;ep&&(p=i,_=e)}_>0&&p>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 Bc(t){return"cleaning"===t||"returning"===t||"on"===t}const Uc={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function Wc(t){const[e,i]=Uc[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 jc(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}}function Gc(t,e,i,s){const[n,o]=Dc(Wc(e),i,s),a=Wc({...t,ox:0,oy:0}),[r,l]=Dc(a,i,s);return{...t,ox:n-r,oy:o-l}}function Vc(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}const Kc=1e-5;function Yc(t){const e=t.map(()=>[]);for(let i=0;i=1-1e-9||u<=1e-9||u>=1-1e-9||(e[i].push(d),e[a].push(u))}}const i=[];for(let s=0;st-e),1];for(let t=1;t{const i=e[2]-e[0],s=e[3]-e[1],n=i*i+s*s;if(!(n>0))return Math.hypot(t[0]-e[0],t[1]-e[1]);const o=Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*s)/n));return Math.hypot(t[0]-(e[0]+o*i),t[1]-(e[1]+o*s))},Xc=(t,e,i,s)=>{const n=s[2]-s[0],o=s[3]-s[1],a=e*o-i*n;if(Math.abs(a)<1e-12)return 1/0;const r=s[0]-t[0],l=s[1]-t[1],c=(r*o-l*n)/a;if(!(c>1e-9))return 1/0;const h=(r*i-l*e)/a;return h<-1e-9||h>1+1e-9?1/0:c};function Qc(t,e,i,s=96){if(!(e>0&&Number.isFinite(t[0])&&Number.isFinite(t[1])))return[];const n=[];for(const s of i){if(!s||s.length<4)continue;if(![s[0],s[1],s[2],s[3]].every(Number.isFinite))continue;const i=Zc(t,s);if(i<1e-7)return[];i>e||n.push(s)}const o=[],a=Math.max(12,Math.round(s));for(let t=0;tt-e);const l=[];let c=Number.NEGATIVE_INFINITY;for(const i of o){if(i-c<1e-9)continue;c=i;const s=Math.cos(i),o=Math.sin(i);let a=e;for(const e of n){const i=Xc(t,s,o,e);i=3?l:[]}class th extends ht{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 Zo(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:Xo(e,"editor.lang_auto")},{value:"en",label:Xo(e,"editor.lang_en")},{value:"ru",label:Xo(e,"editor.lang_ru")}]}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const t=this._lang,e={title:Xo(t,"editor.title"),default_floor:Xo(t,"editor.default_floor"),language:Xo(t,"editor.language"),icon_size:Xo(t,"editor.icon_size"),show_temperature:Xo(t,"editor.show_temperature"),live_states:Xo(t,"editor.live_states"),show_signal:Xo(t,"editor.show_signal"),kiosk:Xo(t,"editor.kiosk"),cycle:Xo(t,"editor.cycle")};return U`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)}}function th(t,e=new WeakMap){if(null===t||"object"!=typeof t)return t;const i=t,s=e.get(i);if(s)return s;if(Array.isArray(t)){const s=[];e.set(i,s);for(const i of t)s.push(th(i,e));return Object.freeze(s)}const n={};e.set(i,n);for(const[i,s]of Object.entries(t))"function"!=typeof s&&(n[i]=th(s,e));return Object.freeze(n)}function eh(t){const e=new Map(t);let i;return i=Object.freeze({get size(){return e.size},get:t=>e.get(t),has:t=>e.has(t),entries:()=>e.entries(),keys:()=>e.keys(),values:()=>e.values(),forEach:(t,s)=>e.forEach((e,n)=>t.call(s,e,n,i)),[Symbol.iterator]:()=>e[Symbol.iterator]()}),i}function ih(t){const e=new Set(t.entityIds||[]),i=new Set(t.deviceIds||[]),s=new Set(t.areaIds||[]);for(const s of t.devices){for(const t of s.entities||[])e.add(t);s.primary&&e.add(s.primary);for(const t of s.controls||[])e.add(t);s.marker?.vacuum?.source&&e.add(s.marker.vacuum.source),"device"===s.bindingKind&&s.bindingRef&&i.add(s.bindingRef),"entity"===s.bindingKind&&s.bindingRef&&e.add(s.bindingRef)}const n={},o={};for(const[o,a]of Object.entries(t.hass?.entities||{})){const r=a?.device_id?t.hass?.devices?.[a.device_id]:null;(e.has(o)||a?.device_id&&i.has(a.device_id)||s.has(a?.area_id)||s.has(r?.area_id))&&(e.add(o),n[o]=a,a?.device_id&&i.add(a.device_id))}for(const e of i){const i=t.hass?.devices?.[e];i&&(o[e]=i)}const a={};for(const i of e){const e=t.hass?.states?.[i];e&&(a[i]=e)}const r=Object.freeze({states:th(a),entities:th(n),devices:th(o),config:th(t.hass?.config),locale:th(t.hass?.locale),themes:th(t.hass?.themes)});return Object.freeze({sourceSequence:t.sourceSequence,capturedAt:t.capturedAt??Date.now(),hass:r,devices:th([...t.devices]),positions:eh([...t.positions||[]].map(([t,e])=>[t,Object.freeze({x:e.x,y:e.y})])),presentations:eh([...t.presentations].map(([t,e])=>[t,th(e)])),facts:eh([...t.facts||[]].map(([t,e])=>[t,th(e)]))})}Qc.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",Qc);const sh=(t,e)=>`${t}:${e?1:0}`;function nh(t){const e=_l(t.cfg),i={};for(const e of t.cfg.spaces||[])for(const t of e.rooms||[])t.area&&(i[t.area]=e.id);const s=t.cfg.settings?.exclude_integrations?new Set(t.cfg.settings.exclude_integrations):qt,n=Ut(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:Bt);return Wn({hass:t.hass,registry:t.registry,areaToSpace:i,markers:t.cfg.markers||[],settings:t.cfg.settings||{},excluded:s,showAll:!!t.cfg.settings?.show_all,firstSpaceId:e[0]?.id||"",loc:e=>Xo(t.lang,e),iconRules:n})}function oh(t){const e=_l(t.cfg).find(e=>e.id===t.spaceId);if(!e)return null;const i=Qi(t.cfg.spaces.find(e=>e.id===t.spaceId)),s=is(t.cfg.settings),n=t.iconSize??2.5,o=n>8?2.5:n,a=t.registry?hn(t.hass,t.registry):t.hass,r=(t.devices||nh(t)).filter(e=>e.space===t.spaceId),l=r.filter(t=>!t.hidden),c=function(t,e,i){const s={},n=i/100*zl(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=Ol(i),a=.1*Math.min(o.w,o.h),r=o.w-2*a,l=o.h-2*a,c=Math.max(1,Math.round(Math.sqrt(e.length*r/Math.max(l,1)))),h=r/c,d=l/Math.max(Math.ceil(e.length/c),1),u=e.map((t,e)=>({x:o.x+a+h*(e%c+.5),y:o.y+a+d*(Math.floor(e/c)+.5)}));Di(u,o,n,.5*a),e.forEach((t,e)=>s[t.id]=wl(u[e]))}return s}(r,e,o),h=[];for(const e of l){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*ll,e=i.y*ll;h.push({minX:t,minY:e,maxX:t,maxY:e})}}const d=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},u=Array.isArray(d.walls)?d.walls:[],p=Number(d.cell_cm)>0?Number(d.cell_cm):5,_=function(t,e,i){const s=[];for(const n of t.partitions||[]){const t=Jr(n.a,n.b,n.cm,e,i);t&&s.push(t)}for(const n of t.room_drafts||[])s.push(...Xr(n,e,i));for(const n of t.wall_columns||[])s.push(Zr(n,e,i));return s}(e,p,vl);for(const t of _){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);e.length&&h.push({minX:Math.min(...e),minY:Math.min(...i),maxX:Math.max(...e),maxY:Math.max(...i)})}const m=El(e,h),g=[m.x,m.y,m.w,m.h],f=new Map(e.rooms.map(t=>{const e=xs(i.fill,t);return[t,ns(e,"lqi"===e&&t.area?Vn(a,r,t.area):null,"light"===e?En(Rn(a,r,t)):"none","temp"===e&&t.area?Kn(a,r,t.area):null,i.tempMin,i.tempMax,s,Xi(i.customFill,t))]})),v=e.rooms.filter(t=>t.area||i.showBorders||"none"!==xs(i.fill,t)).map(t=>{let s="room "+(e.bg?"overlay":"yard"),n="";const o=xs(i.fill,t);if(i.showBorders||"none"!==o){s+=" styled";const e=[`--room-stroke:${i.color}`,`--room-stroke-op:${i.showBorders?i.opacity:0}`],o=f.get(t)||null;o?(s+=" filled",e.push(`--room-fill:${o.color}`,`--room-fill-op:${o.opacity.toFixed(3)}`)):e.push("--room-fill:transparent","--room-fill-op:0"),n=e.join(";")}const a=t.id||G,r=t.area||G,l=t.poly?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)}}function eh(t,e=new WeakMap){if(null===t||"object"!=typeof t)return t;const i=t,s=e.get(i);if(s)return s;if(Array.isArray(t)){const s=[];e.set(i,s);for(const i of t)s.push(eh(i,e));return Object.freeze(s)}const n={};e.set(i,n);for(const[i,s]of Object.entries(t))"function"!=typeof s&&(n[i]=eh(s,e));return Object.freeze(n)}function ih(t){const e=new Map(t);let i;return i=Object.freeze({get size(){return e.size},get:t=>e.get(t),has:t=>e.has(t),entries:()=>e.entries(),keys:()=>e.keys(),values:()=>e.values(),forEach:(t,s)=>e.forEach((e,n)=>t.call(s,e,n,i)),[Symbol.iterator]:()=>e[Symbol.iterator]()}),i}function sh(t){const e=new Set(t.entityIds||[]),i=new Set(t.deviceIds||[]),s=new Set(t.areaIds||[]);for(const s of t.devices){for(const t of s.entities||[])e.add(t);s.primary&&e.add(s.primary);for(const t of s.controls||[])e.add(t);s.marker?.vacuum?.source&&e.add(s.marker.vacuum.source),"device"===s.bindingKind&&s.bindingRef&&i.add(s.bindingRef),"entity"===s.bindingKind&&s.bindingRef&&e.add(s.bindingRef)}const n={},o={};for(const[o,a]of Object.entries(t.hass?.entities||{})){const r=a?.device_id?t.hass?.devices?.[a.device_id]:null;(e.has(o)||a?.device_id&&i.has(a.device_id)||s.has(a?.area_id)||s.has(r?.area_id))&&(e.add(o),n[o]=a,a?.device_id&&i.add(a.device_id))}for(const e of i){const i=t.hass?.devices?.[e];i&&(o[e]=i)}const a={};for(const i of e){const e=t.hass?.states?.[i];e&&(a[i]=e)}const r=Object.freeze({states:eh(a),entities:eh(n),devices:eh(o),config:eh(t.hass?.config),locale:eh(t.hass?.locale),themes:eh(t.hass?.themes)});return Object.freeze({sourceSequence:t.sourceSequence,capturedAt:t.capturedAt??Date.now(),hass:r,devices:eh([...t.devices]),positions:ih([...t.positions||[]].map(([t,e])=>[t,Object.freeze({x:e.x,y:e.y})])),presentations:ih([...t.presentations].map(([t,e])=>[t,eh(e)])),facts:ih([...t.facts||[]].map(([t,e])=>[t,eh(e)]))})}th.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",th);const nh=(t,e)=>`${t}:${e?1:0}`;function oh(t){const e=ml(t.cfg),i={};for(const e of t.cfg.spaces||[])for(const t of e.rooms||[])t.area&&(i[t.area]=e.id);const s=t.cfg.settings?.exclude_integrations?new Set(t.cfg.settings.exclude_integrations):qt,n=Ut(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:Bt);return Wn({hass:t.hass,registry:t.registry,areaToSpace:i,markers:t.cfg.markers||[],settings:t.cfg.settings||{},excluded:s,showAll:!!t.cfg.settings?.show_all,firstSpaceId:e[0]?.id||"",loc:e=>Xo(t.lang,e),iconRules:n})}function ah(t){const e=ml(t.cfg).find(e=>e.id===t.spaceId);if(!e)return null;const i=Qi(t.cfg.spaces.find(e=>e.id===t.spaceId)),s=is(t.cfg.settings),n=t.iconSize??2.5,o=n>8?2.5:n,a=t.registry?hn(t.hass,t.registry):t.hass,r=(t.devices||oh(t)).filter(e=>e.space===t.spaceId),l=r.filter(t=>!t.hidden),c=function(t,e,i){const s={},n=i/100*Fl(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=Ll(i),a=.1*Math.min(o.w,o.h),r=o.w-2*a,l=o.h-2*a,c=Math.max(1,Math.round(Math.sqrt(e.length*r/Math.max(l,1)))),h=r/c,d=l/Math.max(Math.ceil(e.length/c),1),u=e.map((t,e)=>({x:o.x+a+h*(e%c+.5),y:o.y+a+d*(Math.floor(e/c)+.5)}));Di(u,o,n,.5*a),e.forEach((t,e)=>s[t.id]=kl(u[e]))}return s}(r,e,o),h=[];for(const e of l){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*cl,e=i.y*cl;h.push({minX:t,minY:e,maxX:t,maxY:e})}}const d=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},u=Array.isArray(d.walls)?d.walls:[],p=Number(d.cell_cm)>0?Number(d.cell_cm):5,_=function(t,e,i){const s=[];for(const n of t.partitions||[]){const t=Zr(n.a,n.b,n.cm,e,i);t&&s.push(t)}for(const n of t.room_drafts||[])s.push(...Qr(n,e,i));for(const n of t.wall_columns||[])s.push(Xr(n,e,i));return s}(e,p,yl);for(const t of _){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);e.length&&h.push({minX:Math.min(...e),minY:Math.min(...i),maxX:Math.max(...e),maxY:Math.max(...i)})}const m=Il(e,h),g=[m.x,m.y,m.w,m.h],f=new Map(e.rooms.map(t=>{const e=xs(i.fill,t);return[t,ns(e,"lqi"===e&&t.area?Vn(a,r,t.area):null,"light"===e?En(Rn(a,r,t)):"none","temp"===e&&t.area?Kn(a,r,t.area):null,i.tempMin,i.tempMax,s,Xi(i.customFill,t))]})),v=e.rooms.filter(t=>t.area||i.showBorders||"none"!==xs(i.fill,t)).map(t=>{let s="room "+(e.bg?"overlay":"yard"),n="";const o=xs(i.fill,t);if(i.showBorders||"none"!==o){s+=" styled";const e=[`--room-stroke:${i.color}`,`--room-stroke-op:${i.showBorders?i.opacity:0}`],o=f.get(t)||null;o?(s+=" filled",e.push(`--room-fill:${o.color}`,`--room-fill-op:${o.opacity.toFixed(3)}`)):e.push("--room-fill:transparent","--room-fill-op:0"),n=e.join(";")}const a=t.id||G,r=t.area||G,l=t.poly?W``:W``;return l}),y=e.rooms.filter(t=>{const e=f.get(t);return Ss(i.glow,t)&&(!e||e.opacity<=0)}).map(t=>t.poly?W`