From f8f1718ad2e168d88772ad28ef4fbce6c4778a04 Mon Sep 17 00:00:00 2001 From: Matysh Date: Mon, 10 Aug 2026 18:01:16 +0300 Subject: [PATCH] Release v1.61.0-beta.5 candidate --- .github/workflows/announce.yml | 14 +- .github/workflows/performance.yml | 172 +++ .github/workflows/release.yml | 9 + .github/workflows/validate.yml | 188 +-- custom_components/houseplan/const.py | 2 +- .../houseplan/frontend/houseplan-card.js | 1070 +++++++++-------- custom_components/houseplan/manifest.json | 2 +- custom_components/houseplan/validation.py | 12 + demo/benchmark_glow.mjs | 6 +- demo/golden/harness.mjs | 10 + demo/golden/matrix.mjs | 10 +- demo/performance/README.md | 49 +- demo/performance/budgets-glow-smoke.json | 39 + demo/performance/compare.mjs | 4 +- demo/performance/evaluate.mjs | 84 +- demo/smoke_glow.mjs | 30 +- demo/smoke_glow_blending.mjs | 26 +- demo/smoke_ha_controls.mjs | 8 +- demo/srv/assets/houseplan-card.js | 1070 +++++++++-------- dist/houseplan-card.js | 1070 +++++++++-------- docs/ARCHITECTURE.md | 28 +- docs/CHANGELOG.md | 21 + docs/CHANGELOG.ru.md | 21 + docs/CONFIG-COMPATIBILITY.md | 20 + docs/DEVELOPMENT.md | 5 +- docs/FILTERING.md | 7 +- docs/RELEASE-NOTES.md | 10 +- docs/STATUS.md | 16 +- docs/TESTING-DEMO.md | 4 +- docs/TESTING.md | 37 +- docs/USER-GUIDE.ru.md | 29 +- docs/specs/067-light-source-controls.md | 593 +++++++++ package-lock.json | 4 +- package.json | 2 +- scripts/release-gate.mjs | 40 +- src/devices.ts | 84 +- src/houseplan-card.ts | 241 +++- src/hp-color-opacity.ts | 34 +- src/i18n/en.json | 19 +- src/i18n/ru.json | 19 +- src/logic.ts | 87 +- src/styles.ts | 33 +- src/types.ts | 11 +- test/devices.test.mjs | 62 +- test/logic.test.mjs | 64 +- test/performance-budget.test.mjs | 15 + test/performance-workflow.test.mjs | 37 + test/release-contract.test.mjs | 4 +- test/release-gate.test.mjs | 9 +- tests_backend/test_validation.py | 25 + 50 files changed, 3567 insertions(+), 1889 deletions(-) create mode 100644 .github/workflows/performance.yml create mode 100644 demo/performance/budgets-glow-smoke.json create mode 100644 docs/specs/067-light-source-controls.md create mode 100644 test/performance-workflow.test.mjs diff --git a/.github/workflows/announce.yml b/.github/workflows/announce.yml index c32fc473..7b7bdd7c 100644 --- a/.github/workflows/announce.yml +++ b/.github/workflows/announce.yml @@ -1,9 +1,8 @@ name: Announce release # Telegram notifications for t.me/ha_houseplan (owner request, 2026-08-07). -# Fires on every published release, prereleases included: the workflow file -# lives at the TAGGED commit, so it works for beta tags cut from dev as soon -# as this file is on dev, and for stable tags once it reaches main. -# workflow_dispatch exists purely as a connectivity test button. +# Stable releases are announced; prereleases are deliberately silent. +# workflow_dispatch exists purely as a connectivity test button and therefore +# remains allowed to send a test message. on: release: types: [published] @@ -37,6 +36,7 @@ permissions: contents: read jobs: telegram: + if: ${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'release' && github.event.release.prerelease == false) || (github.event_name == 'workflow_call' && inputs.prerelease == false) }} runs-on: ubuntu-latest steps: - name: Check out release notes for a reusable call @@ -79,7 +79,11 @@ jobs: PRE=$RELEASE_PRE BODY=$RELEASE_BODY fi - KIND=$([ "$PRE" = "true" ] && echo "🧪 Пре-релиз" || echo "🏠 Релиз") + if [ "$PRE" = "true" ]; then + echo "Prerelease Telegram announcement is disabled" + exit 0 + fi + KIND="🏠 Релиз" SUMMARY=$(printf '%s' "$BODY" | head -c 2500) TEXT=$(printf '%s houseplan-card %s — %s\n\n%s\n\n%s' \ "$KIND" "$TAG" "$NAME" "$SUMMARY" "$URL") diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml new file mode 100644 index 00000000..aa3ef38c --- /dev/null +++ b/.github/workflows/performance.yml @@ -0,0 +1,172 @@ +name: Full Performance + +on: + # Every main promotion is a stable-release candidate and must have an + # exact-SHA full comparison before stable assets are published. + push: + branches: + - main + schedule: + - cron: "0 4 * * 1" + workflow_dispatch: + inputs: + comparison_ref: + description: "Optional baseline tag, branch or SHA; empty uses the candidate parent" + required: false + type: string + +permissions: + contents: read + +concurrency: + group: full-performance-${{ github.ref }} + cancel-in-progress: false + +jobs: + performance: + # Base and candidate stay sequential on one hosted runner. Splitting them + # across runners would turn machine variance into a false regression. + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Check out candidate + uses: actions/checkout@v4 + with: + path: candidate + fetch-depth: 2 + + - name: Resolve comparison SHA + id: base + working-directory: candidate + env: + EVENT_NAME: ${{ github.event_name }} + PUSH_BEFORE_SHA: ${{ github.event.before }} + MANUAL_BASE: ${{ inputs.comparison_ref }} + run: | + set -euo pipefail + if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then + git fetch --force --tags --prune --unshallow origin + else + git fetch --force --tags --prune origin + fi + + if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ -n "$MANUAL_BASE" ]; then + sha="$(git rev-parse "${MANUAL_BASE}^{commit}" 2>/dev/null || true)" + source="manual comparison ref $MANUAL_BASE" + elif [ "$EVENT_NAME" = "push" ] && [ -n "$PUSH_BEFORE_SHA" ] && ! printf '%s' "$PUSH_BEFORE_SHA" | grep -Eq '^0+$'; then + sha="$PUSH_BEFORE_SHA" + source="push before" + else + sha="$(git rev-parse HEAD^ 2>/dev/null || true)" + source="candidate parent" + fi + requested_sha="$sha" + + usable=true + reason="" + if [ -z "$sha" ] || ! git cat-file -e "${sha}^{commit}" 2>/dev/null; then + usable=false + reason="commit is not present after fetching all remote refs" + elif [ "$source" = "push before" ] && ! git merge-base --is-ancestor "$sha" HEAD; then + usable=false + reason="commit is no longer an ancestor of the pushed revision" + fi + + if [ "$usable" != true ]; then + parent_sha="$(git rev-parse HEAD^ 2>/dev/null || true)" + if [ -n "$parent_sha" ] && [ "$parent_sha" != "$(git rev-parse HEAD)" ]; then + sha="$parent_sha" + source="candidate parent (unusable requested-base fallback)" + echo "::warning::Comparison SHA ${requested_sha:-none} is unusable ($reason); using candidate parent $sha." + usable=true + fi + fi + + if [ "$usable" != true ]; then + fallback_tag="" + fallback_sha="" + head_sha="$(git rev-parse HEAD)" + while IFS= read -r tag; do + case "$tag" in + v[0-9]*.[0-9]*.[0-9]*) ;; + *) continue ;; + esac + tag_sha="$(git rev-list -n 1 "$tag")" + if [ "$tag_sha" != "$head_sha" ]; then + fallback_tag="$tag" + fallback_sha="$tag_sha" + break + fi + done < <(git tag --merged HEAD --sort=-version:refname) + if [ -z "$fallback_sha" ]; then + echo "::error::No usable comparison commit or previous release tag is reachable from HEAD." + exit 1 + fi + sha="$fallback_sha" + source="release tag $fallback_tag" + echo "::warning::Using $fallback_tag ($sha) as the comparison base." + fi + + if ! git cat-file -e "${sha}:demo/bundle-freshness.mjs" 2>/dev/null; then + echo "::warning::Comparison $sha predates HP-PERF-01; using candidate parent HEAD^." + sha="$(git rev-parse HEAD^)" + source="candidate parent (HP-PERF-01 compatibility)" + fi + echo "sha=$sha" >> "$GITHUB_OUTPUT" + echo "Comparison base: $sha ($source)" >> "$GITHUB_STEP_SUMMARY" + + - name: Check out base SHA + uses: actions/checkout@v4 + with: + ref: ${{ steps.base.outputs.sha }} + path: baseline + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: | + candidate/package-lock.json + baseline/package-lock.json + + - name: Install candidate and baseline dependencies + run: npm ci --prefix candidate && npm ci --prefix baseline + + - name: Install pinned Chromium + working-directory: candidate + run: npx playwright install --with-deps chromium + + - name: Build both exact source trees + run: | + npm --prefix candidate run build + cp candidate/dist/houseplan-card.js candidate/demo/srv/assets/houseplan-card.js + npm --prefix baseline run build + cp baseline/dist/houseplan-card.js baseline/demo/srv/assets/houseplan-card.js + + - name: Capture base and candidate profiles + working-directory: candidate + run: | + npm run benchmark:large-house -- --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/baseline.json + npm run benchmark:large-house -- --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/candidate.json + npm run benchmark:glow -- --profile=large-light-blend-v1 --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/blend-baseline.json + npm run benchmark:glow -- --profile=large-light-blend-v1 --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/blend-candidate.json + npm run benchmark:glow -- --profile=large-house-glow-overlay-v1 --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/overlay-baseline.json + npm run benchmark:glow -- --profile=large-house-glow-overlay-v1 --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/overlay-candidate.json + if ! grep -q "glow_enabled" ../baseline/src/logic.ts; then + echo "Base predates independent Glow; bootstrap relative overlay baseline, keep absolute gate" + cp ../artifacts/performance/overlay-candidate.json ../artifacts/performance/overlay-baseline.json + fi + + - name: Enforce relative and absolute performance budgets + working-directory: candidate + run: | + npm run benchmark:compare -- --baseline=../artifacts/performance/baseline.json --candidate=../artifacts/performance/candidate.json --output=../artifacts/performance/comparison.json + npm run benchmark:compare -- --budgets=demo/performance/budgets-large-light-blend.json --baseline=../artifacts/performance/blend-baseline.json --candidate=../artifacts/performance/blend-candidate.json --output=../artifacts/performance/blend-comparison.json + npm run benchmark:compare -- --budgets=demo/performance/budgets-large-house-glow-overlay.json --baseline=../artifacts/performance/overlay-baseline.json --candidate=../artifacts/performance/overlay-candidate.json --output=../artifacts/performance/overlay-comparison.json + + - name: Upload full performance reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: full-performance + path: artifacts/performance diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7251e98a..08e82622 100755 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,6 +34,15 @@ jobs: SHA=$(git rev-parse HEAD) echo "release tag: $TAG; exact commit: $SHA" node scripts/release-gate.mjs "$SHA" + - name: Require full performance for a stable release + if: ${{ !github.event.release.prerelease }} + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + run: | + set -euo pipefail + SHA=$(git rev-parse HEAD) + node scripts/release-gate.mjs "$SHA" --workflow=performance.yml --label="Full Performance" build: needs: gate runs-on: ubuntu-latest diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 6633df6b..a91d5587 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -1,13 +1,19 @@ name: Validate + on: push: # The branch commit is the release-gate authority. An annotated tag points - # to the same SHA and must not duplicate every expensive browser/perf job. + # to the same SHA and must not duplicate the browser validation jobs. branches: - '**' pull_request: - schedule: - - cron: "0 4 * * 1" + +# A new push supersedes an unfinished validation for the same branch or PR. +# Exact-SHA release gates never depend on an obsolete commit. +concurrency: + group: validate-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: hacs: runs-on: ubuntu-latest @@ -17,18 +23,22 @@ jobs: uses: hacs/action@main with: category: integration + hassfest: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Hassfest validation uses: home-assistant/actions/hassfest@master + frontend: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 - with: { node-version: 22 } + with: + node-version: 22 + cache: npm - run: npm ci - name: Typecheck run: npm run typecheck @@ -40,21 +50,21 @@ jobs: run: | cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js cmp dist/houseplan-card.js demo/srv/assets/houseplan-card.js + smoke: - # audit T2: the end-to-end layer used to run only when a human remembered. # Gated on `frontend` so a typecheck failure does not burn browser minutes. needs: frontend runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 - with: { node-version: 22 } + with: + node-version: 22 + cache: npm - run: npm ci - name: Install Chromium for Playwright run: npx playwright install --with-deps chromium - - name: Build a FRESH bundle for the smokes - # Never depend on a previous job's artefact; build the exact source - # under test again before the browser suite. + - name: Build a fresh bundle for the smokes run: npm run build && cp dist/houseplan-card.js demo/srv/assets/houseplan-card.js - name: Smoke suite run: | @@ -79,15 +89,16 @@ jobs: path: /tmp/smoke-logs golden: - # Separate deterministic pixel layer. Before the first reviewed baseline - # set it captures candidates; once baselines exist the same job becomes a - # blocking comparison and uploads actual/diff/report on failure. + # Deterministic visual correctness stays in every prerelease gate: it is + # inexpensive and catches a different class of regressions than timings. needs: frontend runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 - with: { node-version: 22 } + with: + node-version: 22 + cache: npm - run: npm ci - name: Install pinned Chromium run: npx playwright install --with-deps chromium @@ -110,156 +121,35 @@ jobs: name: golden-images path: artifacts/golden - performance: - # Candidate and base SHA run sequentially on the same hosted runner with - # one Playwright/Chromium installation. This avoids turning a developer - # laptop or cross-runner variance into a timing budget. + performance_smoke: + # Candidate-only catastrophic-regression guard for ordinary pushes and + # prereleases. The expensive same-runner comparison lives in performance.yml. needs: frontend runs-on: ubuntu-latest + timeout-minutes: 15 steps: - - name: Check out candidate - uses: actions/checkout@v4 - with: - path: candidate - fetch-depth: 2 - - name: Resolve comparison SHA - id: base - working-directory: candidate - env: - EVENT_NAME: ${{ github.event_name }} - PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} - PUSH_BEFORE_SHA: ${{ github.event.before }} - run: | - if [ "$EVENT_NAME" = "pull_request" ] && [ -n "$PR_BASE_SHA" ]; then - sha="$PR_BASE_SHA" - source="pull-request base" - elif [ "$EVENT_NAME" = "push" ] && [ -n "$PUSH_BEFORE_SHA" ] && ! printf '%s' "$PUSH_BEFORE_SHA" | grep -Eq '^0+$'; then - sha="$PUSH_BEFORE_SHA" - source="push before" - else - sha="$(git rev-parse HEAD^)" - source="candidate parent" - fi - requested_sha="$sha" - - # A force-push can leave github.event.before outside every remote - # ref. Fetch the complete graph first, then verify the object and, - # for push events, that it is still an ancestor of the candidate. - # Never ask checkout to resolve a known-unreachable object. - if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then - git fetch --force --tags --prune --unshallow origin - else - git fetch --force --tags --prune origin - fi - usable=true - reason="" - if ! git cat-file -e "${sha}^{commit}" 2>/dev/null; then - usable=false - reason="commit is not present after fetching all remote refs" - elif [ "$source" = "push before" ] && ! git merge-base --is-ancestor "$sha" HEAD; then - usable=false - reason="commit is no longer an ancestor of the pushed revision" - fi - - if [ "$usable" != true ]; then - # For a rewritten push, the direct parent is the closest honest - # comparison and normally carries the same benchmark generation. - # Prefer it over an arbitrarily old release tag. PR bases keep the - # tag fallback because HEAD^ is not necessarily their base. - parent_sha="$(git rev-parse HEAD^ 2>/dev/null || true)" - if [ "$source" = "push before" ] && [ -n "$parent_sha" ] && [ "$parent_sha" != "$(git rev-parse HEAD)" ]; then - sha="$parent_sha" - source="candidate parent (unusable push-before fallback)" - echo "::warning::Comparison SHA $requested_sha is unusable ($reason); using candidate parent $sha." - usable=true - fi - fi - - if [ "$usable" != true ]; then - fallback_tag="" - fallback_sha="" - head_sha="$(git rev-parse HEAD)" - while IFS= read -r tag; do - case "$tag" in - v[0-9]*.[0-9]*.[0-9]*) ;; - *) continue ;; - esac - tag_sha="$(git rev-list -n 1 "$tag")" - if [ "$tag_sha" != "$head_sha" ]; then - fallback_tag="$tag" - fallback_sha="$tag_sha" - break - fi - done < <(git tag --merged HEAD --sort=-version:refname) - if [ -z "$fallback_sha" ]; then - echo "::error::Comparison SHA $requested_sha is unusable ($reason), and no previous release tag is reachable from HEAD." - exit 1 - fi - sha="$fallback_sha" - source="release tag $fallback_tag" - echo "::warning::Comparison SHA $requested_sha is unusable ($reason); using $fallback_tag ($sha)." - fi - - # The first main promotion after HP-PERF-01 can legitimately point - # at an older stable commit which predates the benchmark's embedded - # bundle fingerprint. Comparing against that legacy tree would fail - # the freshness guard before any measurements are taken. Fall back - # to the candidate's direct parent, which is the last dev revision - # included in the promotion and already has the current harness. - if ! git cat-file -e "${sha}:demo/bundle-freshness.mjs" 2>/dev/null; then - echo "::warning::Comparison $sha predates HP-PERF-01; using candidate parent HEAD^." - sha="$(git rev-parse HEAD^)" - source="candidate parent (HP-PERF-01 compatibility)" - fi - echo "sha=$sha" >> "$GITHUB_OUTPUT" - - name: Check out base SHA - uses: actions/checkout@v4 - with: - ref: ${{ steps.base.outputs.sha }} - path: baseline + - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - cache-dependency-path: | - candidate/package-lock.json - baseline/package-lock.json - - name: Install candidate and baseline dependencies - run: npm ci --prefix candidate && npm ci --prefix baseline + - run: npm ci - name: Install pinned Chromium - working-directory: candidate run: npx playwright install --with-deps chromium - - name: Build both exact source trees + - name: Build the exact candidate source + run: npm run build && cp dist/houseplan-card.js demo/srv/assets/houseplan-card.js + - name: Capture the heaviest Glow state run: | - npm --prefix candidate run build - cp candidate/dist/houseplan-card.js candidate/demo/srv/assets/houseplan-card.js - npm --prefix baseline run build - cp baseline/dist/houseplan-card.js baseline/demo/srv/assets/houseplan-card.js - - name: Capture base and candidate profiles - working-directory: candidate + npm run benchmark:glow -- --profile=large-house-glow-overlay-v1 --variants=60 --samples=3 --warmups=1 --output=artifacts/performance-smoke/candidate.json + - name: Enforce absolute smoke ceilings run: | - npm run benchmark:large-house -- --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/baseline.json - npm run benchmark:large-house -- --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/candidate.json - npm run benchmark:glow -- --profile=large-light-blend-v1 --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/blend-baseline.json - npm run benchmark:glow -- --profile=large-light-blend-v1 --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/blend-candidate.json - npm run benchmark:glow -- --profile=large-house-glow-overlay-v1 --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/overlay-baseline.json - npm run benchmark:glow -- --profile=large-house-glow-overlay-v1 --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/overlay-candidate.json - if ! grep -q "glow_enabled" ../baseline/src/logic.ts; then - echo "Base predates independent Glow; bootstrap relative overlay baseline, keep absolute gate" - cp ../artifacts/performance/overlay-candidate.json ../artifacts/performance/overlay-baseline.json - fi - - name: Enforce performance budgets - working-directory: candidate - run: | - npm run benchmark:compare -- --baseline=../artifacts/performance/baseline.json --candidate=../artifacts/performance/candidate.json --output=../artifacts/performance/comparison.json - npm run benchmark:compare -- --budgets=demo/performance/budgets-large-light-blend.json --baseline=../artifacts/performance/blend-baseline.json --candidate=../artifacts/performance/blend-candidate.json --output=../artifacts/performance/blend-comparison.json - npm run benchmark:compare -- --budgets=demo/performance/budgets-large-house-glow-overlay.json --baseline=../artifacts/performance/overlay-baseline.json --candidate=../artifacts/performance/overlay-candidate.json --output=../artifacts/performance/overlay-comparison.json - - name: Upload performance reports + npm run benchmark:compare -- --absolute-only --budgets=demo/performance/budgets-glow-smoke.json --candidate=artifacts/performance-smoke/candidate.json --output=artifacts/performance-smoke/comparison.json + - name: Upload performance smoke report if: always() uses: actions/upload-artifact@v4 with: - name: large-house-performance - path: artifacts/performance + name: performance-smoke + path: artifacts/performance-smoke backend: runs-on: ubuntu-latest diff --git a/custom_components/houseplan/const.py b/custom_components/houseplan/const.py index 63146e7f..aac850eb 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.61.0-beta.4" +VERSION = "1.61.0-beta.5" DEFAULT_CONFIG: dict = { "spaces": [], diff --git a/custom_components/houseplan/frontend/houseplan-card.js b/custom_components/houseplan/frontend/houseplan-card.js index 64c8704c..eb1264c2 100755 --- a/custom_components/houseplan/frontend/houseplan-card.js +++ b/custom_components/houseplan/frontend/houseplan-card.js @@ -1,5 +1,5 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f5f9700be5383ff9d6201dba36f46";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!a(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),u.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set: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=p(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??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}),(u.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,$=t=>t,x=k.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,T=`<${D}>`,P=document,R=()=>P.createComment(""),z=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,I="[ \t\n\f\r]",E=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,N=/-->/g,F=/>/g,O=RegExp(`>|${I}(?:([^\\s"'>=/]+)(${I}*=${I}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,H=/"/g,q=/^(?:script|style|textarea|title)$/i,U=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),j=U(1),W=U(2),B=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=P.createTreeWalker(P,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",r=E;for(let e=0;e"===l[0]?(r=n??E,c=-1):void 0===l[1]?c=-2:(c=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?O:'"'===l[3]?H:L):r===H||r===L?r=O:r===N||r===F?r=E:(r=O,n=void 0);const d=r===O&&t[e+1].startsWith("/>")?" ":"";o+=r===E?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,o+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const r=t.length-1,a=this.parts,[l,c]=J(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iA(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==G&&z(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){A(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=!z(t)||t!==this._$AH&&t!==B,o&&(this._$AH=t);else{const s=t;let r,a;for(t=n[0],r=0;r{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n})(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return B}}lt._$litElement$=!0,lt.finalized=!0,at.litElementHydrateSupport?.({LitElement:lt});const ct=at.litElementPolyfillSupport;ct?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ht=new WeakMap;let dt=0;class pt extends lt{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._titleId="hp-dialog-title-"+ ++dt,this._focusInitial=()=>{const t=this.querySelector("[autofocus]")||this._focusableElements()[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key)return t.preventDefault(),t.stopImmediatePropagation(),void this._requestClose();if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),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=ht.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),ht.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=this._focusRoot,e=this._opener;this._opener=null,this._focusRoot=null;const i=t?ht.get(t):void 0;i?.dialogs.delete(this),super.disconnectedCallback(),t&&i&&requestAnimationFrame(()=>{const i=ht.get(t);if(!i)return;if(!i.dialogs.size)return i.opener?.isConnected&&i.opener.focus({preventScroll:!0}),void ht.delete(t);const s=e?.closest("hp-dialog");e?.isConnected&&s&&i.dialogs.has(s)&&e.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(",");return Array.from(this.querySelectorAll(t)).filter(t=>{const e=getComputedStyle(t);return!t.hidden&&"none"!==e.display&&"hidden"!==e.visibility})}render(){const t=j` - ${this.icon?j``:G} +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="fc04a319d946937a0d7fb1f2df149fb0fe5164d616bf18f84cac66f0f69e6890";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let o=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const n=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new o(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new o("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!a(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),u.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:o}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const n=s?.call(this);o?.call(this,e),this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=p(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),o=t.litNonce;void 0!==o&&s.setAttribute("nonce",o),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const o=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==o?this.removeAttribute(s):this.setAttribute(s,o),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),o="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const n=o.fromAttribute(e,t.type);this[s]=n??this._$Ej?.get(s)??n,this._$Em=null}}requestUpdate(t,e,i,s=!1,o){if(void 0!==t){const n=this.constructor;if(!1===s&&(o=this[t]),i??=n.getPropertyOptions(t),!((i.hasChanged??y)(o,e)||i.useDefault&&i.reflect&&o===this._$Ej?.get(t)&&!this.hasAttribute(n._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:o},n){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,n??e??this[t]),!0!==o||void 0!==n)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(u.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,$=t=>t,x=k.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,T=`<${D}>`,P=document,R=()=>P.createComment(""),z=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,I="[ \t\n\f\r]",E=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,N=/-->/g,F=/>/g,O=RegExp(`>|${I}(?:([^\\s"'>=/]+)(${I}*=${I}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,H=/"/g,q=/^(?:script|style|textarea|title)$/i,U=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),j=U(1),B=U(2),G=Symbol.for("lit-noChange"),W=Symbol.for("lit-nothing"),V=new WeakMap,K=P.createTreeWalker(P,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let o,n=2===e?"":3===e?"":"",r=E;for(let e=0;e"===l[0]?(r=o??E,c=-1):void 0===l[1]?c=-2:(c=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?O:'"'===l[3]?H:L):r===H||r===L?r=O:r===N||r===F?r=E:(r=O,o=void 0);const d=r===O&&t[e+1].startsWith("/>")?" ":"";n+=r===E?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,n+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let o=0,n=0;const r=t.length-1,a=this.parts,[l,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())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iA(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==W&&z(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){A(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const o of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(o),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=W}_$AI(t,e=this,i,s){const o=this.strings;let n=!1;if(void 0===o)t=X(this,t,e,0),n=!z(t)||t!==this._$AH&&t!==G,n&&(this._$AH=t);else{const s=t;let r,a;for(t=o[0],r=0;r{const s=i?.renderBefore??e;let o=s._$litPart$;if(void 0===o){const t=i?.renderBefore??null;s._$litPart$=o=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return G}}lt._$litElement$=!0,lt.finalized=!0,at.litElementHydrateSupport?.({LitElement:lt});const ct=at.litElementPolyfillSupport;ct?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ht=new WeakMap;let dt=0;class pt extends lt{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._titleId="hp-dialog-title-"+ ++dt,this._focusInitial=()=>{const t=this.querySelector("[autofocus]")||this._focusableElements()[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key)return t.preventDefault(),t.stopImmediatePropagation(),void this._requestClose();if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),o=i[0],n=i[i.length-1];!t.shiftKey||s!==o&&i.includes(s)?t.shiftKey||s!==n||(t.preventDefault(),o.focus()):(t.preventDefault(),n.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=ht.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),ht.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=this._focusRoot,e=this._opener;this._opener=null,this._focusRoot=null;const i=t?ht.get(t):void 0;i?.dialogs.delete(this),super.disconnectedCallback(),t&&i&&requestAnimationFrame(()=>{const i=ht.get(t);if(!i)return;if(!i.dialogs.size)return i.opener?.isConnected&&i.opener.focus({preventScroll:!0}),void ht.delete(t);const s=e?.closest("hp-dialog");e?.isConnected&&s&&i.dialogs.has(s)&&e.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(",");return Array.from(this.querySelectorAll(t)).filter(t=>{const e=getComputedStyle(t);return!t.hidden&&"none"!==e.display&&"hidden"!==e.visibility})}render(){const t=j` + ${this.icon?j``:W} ${this.title} `;return this._useHaDialog?j` - `}}pt.properties={title:{type:String},icon:{type:String},wide:{type:Boolean,reflect:!0},dismissOnScrim:{type:Boolean,attribute:"dismiss-on-scrim"},hass:{attribute:!1}},pt.styles=o` + `}}pt.properties={title:{type:String},icon:{type:String},wide:{type:Boolean,reflect:!0},dismissOnScrim:{type:Boolean,attribute:"dismiss-on-scrim"},hass:{attribute:!1}},pt.styles=n` :host { display: contents; color: var(--primary-text-color, #e6e7eb); @@ -184,12 +184,12 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f display: flex; flex-direction: column; } - `,customElements.get("hp-dialog")||customElements.define("hp-dialog",pt);const ut=/^#[0-9a-fA-F]{6}$/;function _t(t,e){return"string"==typeof t&&ut.test(t)?t:e}const mt="(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])",gt=new RegExp(`^rgb\\(${mt}, ${mt}, ${mt}\\)$`);function ft(t){if(!Array.isArray(t)||t.length<3)return null;const e=t.slice(0,3);if(!e.every(t=>"number"==typeof t&&Number.isFinite(t)))return null;const[i,s,n]=e.map(t=>Math.round(Math.min(255,Math.max(0,t))));return`rgb(${i}, ${s}, ${n})`}class vt extends lt{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this._open=!1,this._pickerRaf=0,this._outsidePointerDown=t=>{this._open&&!t.composedPath().includes(this)&&this._closePicker()},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closePicker(!0))},this._queuePickerPosition=()=>{this._open&&(this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._pickerRaf=requestAnimationFrame(()=>{this._pickerRaf=0,this._positionPicker()}))}}connectedCallback(){super.connectedCallback(),document.addEventListener("pointerdown",this._outsidePointerDown,!0),document.addEventListener("scroll",this._queuePickerPosition,!0),window.addEventListener("resize",this._queuePickerPosition),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){document.removeEventListener("pointerdown",this._outsidePointerDown,!0),document.removeEventListener("scroll",this._queuePickerPosition,!0),window.removeEventListener("resize",this._queuePickerPosition),this.removeEventListener("keydown",this._keyDown,!0),this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._pickerRaf=0,super.disconnectedCallback()}_toggle(){this.disabled||(this._open?this._closePicker():(this._open=!0,this.updateComplete.then(()=>this._positionPicker())))}_closePicker(t=!1){const e=this.renderRoot.querySelector(".picker");if(e?.hidePopover)try{e.matches(":popover-open")&&e.hidePopover()}catch{}this._open=!1,t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_positionPicker(){if(!this._open)return;const t=this.renderRoot.querySelector(".trigger"),e=this.renderRoot.querySelector(".picker");if(!t||!e)return;if(e.style.visibility="hidden",e.showPopover)try{e.matches(":popover-open")||e.showPopover()}catch{}const i=t.getBoundingClientRect(),s=e.getBoundingClientRect(),n=Math.max(8,window.innerWidth-s.width-8);let o=i.left;o+s.width>window.innerWidth-8&&(o=i.right-s.width),o=Math.min(n,Math.max(8,o));const r=i.bottom+7,a=i.top-7-s.height;let l=r;l=r+s.height>window.innerHeight-8&&a>=8?a:Math.min(Math.max(8,l),Math.max(8,window.innerHeight-s.height-8)),e.style.left=`${Math.round(o)}px`,e.style.top=`${Math.round(l)}px`,e.style.visibility=""}_emit(t,e){const i=Number.isFinite(e)?e:Number(this.opacity)||0,s=Math.min(1,Math.max(0,i));this.color=t,this.opacity=s,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:t,opacity:s},bubbles:!0,composed:!0}))}render(){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),e=_t(this.color,"#607d8b"),i=`${this.label||"Color"}: ${e}, ${t}%`;return j` - ${this.label?j`${this.label}`:G} + `,customElements.get("hp-dialog")||customElements.define("hp-dialog",pt);const ut=/^#[0-9a-fA-F]{6}$/;function _t(t,e){return"string"==typeof t&&ut.test(t)?t:e}const mt="(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])",gt=new RegExp(`^rgb\\(${mt}, ${mt}, ${mt}\\)$`);function ft(t){if(!Array.isArray(t)||t.length<3)return null;const e=t.slice(0,3);if(!e.every(t=>"number"==typeof t&&Number.isFinite(t)))return null;const[i,s,o]=e.map(t=>Math.round(Math.min(255,Math.max(0,t))));return`rgb(${i}, ${s}, ${o})`}class vt extends lt{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this._open=!1,this._pickerRaf=0,this._outsidePointerDown=t=>{this._open&&!t.composedPath().includes(this)&&this._closePicker()},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closePicker(!0))},this._queuePickerPosition=()=>{this._open&&(this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._pickerRaf=requestAnimationFrame(()=>{this._pickerRaf=0,this._positionPicker()}))}}connectedCallback(){super.connectedCallback(),document.addEventListener("pointerdown",this._outsidePointerDown,!0),document.addEventListener("scroll",this._queuePickerPosition,!0),window.addEventListener("resize",this._queuePickerPosition),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){document.removeEventListener("pointerdown",this._outsidePointerDown,!0),document.removeEventListener("scroll",this._queuePickerPosition,!0),window.removeEventListener("resize",this._queuePickerPosition),this.removeEventListener("keydown",this._keyDown,!0),this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._pickerRaf=0,super.disconnectedCallback()}_toggle(){this.disabled||(this._open?this._closePicker():(this._open=!0,this.updateComplete.then(()=>this._positionPicker())))}_closePicker(t=!1){const e=this.renderRoot.querySelector(".picker");if(e?.hidePopover)try{e.matches(":popover-open")&&e.hidePopover()}catch{}this._open=!1,t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_positionPicker(){if(!this._open)return;const t=this.renderRoot.querySelector(".trigger"),e=this.renderRoot.querySelector(".picker");if(!t||!e)return;if(e.style.visibility="hidden",e.showPopover)try{e.matches(":popover-open")||e.showPopover()}catch{}const i=t.getBoundingClientRect(),s=e.getBoundingClientRect(),o=Math.max(8,window.innerWidth-s.width-8);let n=i.left;n+s.width>window.innerWidth-8&&(n=i.right-s.width),n=Math.min(o,Math.max(8,n));const r=i.bottom+7,a=i.top-7-s.height;let l=r;l=r+s.height>window.innerHeight-8&&a>=8?a:Math.min(Math.max(8,l),Math.max(8,window.innerHeight-s.height-8)),e.style.left=`${Math.round(n)}px`,e.style.top=`${Math.round(l)}px`,e.style.visibility=""}_emit(t,e){const i=Number.isFinite(e)?e:Number(this.opacity)||0,s=Math.min(1,Math.max(0,i));this.color=t,this.opacity=s,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:t,opacity:s},bubbles:!0,composed:!0}))}render(){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),e=_t(this.color,"#607d8b"),i=this.showOpacity?`${this.label||"Color"}: ${e}, ${t}%`:`${this.label||"Color"}: ${e}`;return j` + ${this.label?j`${this.label}`:W} ${this._open&&!this.disabled?j` -
- ${this.opacityLabel} - this._emit(e,Number(t.target.value)/100)} /> - this._emit(e,Number(t.target.value)/100)} /> - % -
- `:G} - `}}vt.properties={label:{type:String},opacityLabel:{type:String,attribute:"opacity-label"},color:{type:String},opacity:{type:Number},disabled:{type:Boolean,reflect:!0},_open:{state:!0}},vt.styles=o` + ${this.showOpacity?j`
+ ${this.opacityLabel} + this._emit(e,Number(t.target.value)/100)} /> + this._emit(e,Number(t.target.value)/100)} /> + % +
`:W} + `:W} + `}}vt.properties={label:{type:String},opacityLabel:{type:String,attribute:"opacity-label"},color:{type:String},opacity:{type:Number},disabled:{type:Boolean,reflect:!0},showOpacity:{type:Boolean,attribute:"show-opacity"},_open:{state:!0}},vt.styles=n` :host { position: relative; display: inline-flex; @@ -329,7 +329,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f opacity: .5; pointer-events: none; } - `,customElements.get("hp-color-opacity")||customElements.define("hp-color-opacity",vt);const yt=o` + `,customElements.get("hp-color-opacity")||customElements.define("hp-color-opacity",vt);const yt=n` :host { --hp-bg: var(--card-background-color, #16212e); --hp-line: var(--divider-color, #2b3d4f); @@ -831,8 +831,8 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f pointer-events: none; } /* The isolated group makes source pools blend with one another, never with - the room data fill, Glow base, paper or backdrop. Outer opacity is the - existing 0.7 and is applied once after this flat group is composited. */ + the room data fill, Glow base, paper or backdrop. Per-stop alpha already + contains the shared 0.7 ceiling; an outer opacity would apply it twice. */ .glow-pools-frame, .glow-pools { isolation: isolate; @@ -2256,6 +2256,35 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f .srcrow > span:first-of-type { white-space: nowrap; } .colorrow .opl { color: var(--hp-muted); font-size: var(--fs-s); } .colorrow .opv { font-size: var(--fs-s); min-width: 34px; text-align: right; } + .markerlightgroup { + min-width: 0; + margin: var(--sp-5) 0 0; + padding: var(--sp-4); + border: 1px solid var(--hp-line); + border-radius: var(--rad-m); + } + .markerlightgroup legend { + padding: 0 var(--sp-2); + color: var(--hp-txt); + font-weight: 600; + } + .markerlightgroup[disabled] { opacity: .62; } + .markerlighttip { margin: 0 0 var(--sp-3) !important; } + .markerradios { display: grid; gap: var(--sp-1); min-width: 0; } + .markerlightgroup .srcrow > span:first-of-type { + min-width: 0; + white-space: normal; + overflow-wrap: anywhere; + } + .markerglowvalue { margin: var(--sp-3) 0; flex-wrap: wrap; } + .markerglowvalue hp-color-opacity { flex: none; } + .markerlightdisabled { + display: flex; + align-items: center; + gap: var(--sp-2); + margin-top: var(--sp-2) !important; + } + .markerlightdisabled ha-icon { --mdc-icon-size: 18px; flex: none; } .planrow { display: flex; align-items: center; @@ -3172,17 +3201,17 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f z-index: 120; max-width: 90vw; } - `;function bt(t){const e=[];if(1!==t.scale&&e.push(`--dev-scale:${t.scale}`),"icon_ripple"===t.display){e.push(`--ripple-scale:${t.rippleScale}`);const n=(i=t.rippleColor,s=null,"string"==typeof i&&(ut.test(i)||gt.test(i))?i:s);n&&e.push(`--ripple-color:${n}`)}var i,s;return e}function wt(t,e){const i=t.activity,s=t.classes.includes("activity-gen2");return j` - ${"none"!==i?j``:G} - ${e.newDevice?j``:G} - ${t.haDisabled?j``:G} + `;function bt(t){const e=[];if(1!==t.scale&&e.push(`--dev-scale:${t.scale}`),"icon_ripple"===t.display){e.push(`--ripple-scale:${t.rippleScale}`);const o=(i=t.rippleColor,s=null,"string"==typeof i&&(ut.test(i)||gt.test(i))?i:s);o&&e.push(`--ripple-color:${o}`)}var i,s;return e}function wt(t,e){const i=t.activity,s=t.classes.includes("activity-gen2");return j` + ${"none"!==i?j``:W} + ${e.newDevice?j``:W} + ${t.haDisabled?j``:W} ${null!=t.valueText?j`${t.valueText}`:j``} - ${null!=t.tempText&&null==t.valueText?j`${t.tempText}°`:G} - ${null!=t.humText&&null==t.valueText?j`${t.humText}%`:G} - ${null!=t.lqiText?j`${t.lqiText}`:G} - `}const kt=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),$t=[{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 xt(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 St=xt($t),Mt={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"},Ct="mdi:chip";function Dt(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??St)if(t.test(s))return e;return Ct}const Tt=["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 Pt=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,Rt=Math.ceil,zt=Math.floor,At="[BigNumber Error] ",It=At+"Number primitive has more than 15 significant digits: ",Et=1e14,Nt=14,Ft=9007199254740991,Ot=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],Lt=1e7,Ht=1e9;function qt(t){var e=0|t;return t>0||t===e?e:e-1}function Ut(t){for(var e,i,s=1,n=t.length,o=t[0]+"";sc^i?1:-1;for(a=(l=n.length)<(c=o.length)?l:c,r=0;ro[r]^i?1:-1;return l==c?0:l>c^i?1:-1}function Wt(t,e,i,s){if(ti||t!==zt(t))throw Error(At+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Bt(t){var e=t.c.length-1;return qt(t.e/Nt)==e&&t.c[e]%2!=0}function Gt(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Vt(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?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>y?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!Pt.test(d=String(t)))return n(p,d,c);p.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(a=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(a<0&&(a=l),a+=+d.slice(l+1),d=d.substring(0,l)):a<0&&(a=d.length)}else{if(Wt(e,2,x.length,"Base"),10==e&&S)return P(p=new M(t),_+p.e+1,m);if(d=String(t),c="number"==typeof t){if(0*t!=0)return n(p,d,c,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(It+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,h=d.length;la){a=h;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return n(p,String(t),c,e)}c=!1,(a=(d=s(d,e,10,p.s)).indexOf("."))>-1?d=d.replace(".",""):a=d.length}for(l=0;48===d.charCodeAt(l);l++);for(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(t>Ft||t!==zt(t)))throw Error(It+p.s*t);if((a=a-l-1)>y)p.c=p.e=null;else if(a=f)?Gt(l,r):Vt(l,r,"0");else if(o=(t=P(new M(t),e,i)).e,a=(l=Ut(t.c)).length,1==s||2==s&&(e<=o||o<=g)){for(;ar),l=Vt(l,o,"0"),o+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=o-a)>0)for(o+1==a&&(l+=".");e--;l+="0");return t.s<0&&n?"-"+l:l}function D(t,e){for(var i,s,n=1,o=new M(t[0]);n=10;n/=10,s++);return(i=s+i*Nt-1)>y?t.c=t.e=null:i=10;a/=10,n++);if((o=e-n)<0)o+=Nt,r=e,l=d[c=0],h=zt(l/p[n-r-1]%10);else if((c=Rt((o+1)/Nt))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,n=1,r=(o%=Nt)-Nt+1}else{for(l=a=d[c],n=1;a>=10;a/=10,n++);h=(r=(o%=Nt)-Nt+n)<0?0:zt(l/p[n-r-1]%10)}if(s=s||e<0||null!=d[c+1]||(r<0?l:l%p[n-r-1]),s=i<4?(h||s)&&(0==i||i==(t.s<0?3:2)):h>5||5==h&&(4==i||s||6==i&&(o>0?r>0?l/p[n-r]:0:d[c-1])%10&1||i==(t.s<0?8:7)),e<1||!d[0])return d.length=0,s?(e-=t.e+1,d[0]=p[(Nt-e%Nt)%Nt],t.e=-e||0):d[0]=t.e=0,t;if(0==o?(d.length=c,a=1,c--):(d.length=c+1,a=p[Nt-o],d[c]=r>0?zt(l/p[n-r]%p[r])*a:0),s)for(;;){if(0==c){for(o=1,r=d[0];r>=10;r/=10,o++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);o!=a&&(t.e++,d[0]==Et&&(d[0]=1));break}if(d[c]+=a,d[c]!=Et)break;d[c--]=0,a=1}for(o=d.length;0===d[--o];d.pop());}t.e>y?t.c=t.e=null:t.e=f?Gt(e,i):Vt(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(At+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(Wt(i=t[e],0,Ht,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(Wt(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(Wt(i[0],-Ht,0,e),Wt(i[1],0,Ht,e),g=i[0],f=i[1]):(Wt(i,-Ht,Ht,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)Wt(i[0],-Ht,-1,e),Wt(i[1],1,Ht,e),v=i[0],y=i[1];else{if(Wt(i,-Ht,Ht,e),!i)throw Error(At+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(At+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(At+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(Wt(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(Wt(i=t[e],0,Ht,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(At+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(At+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>=-Ht&&n<=Ht&&n===zt(n)){if(0===s[0]){if(0===n&&1===s.length)return!0;break t}if((e=(n+1)%Nt)<1&&(e+=Nt),String(s[0]).length==e){for(e=0;e=Et||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(At+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(o=9007199254740992,r=Math.random()*o&2097151?function(){return zt(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,n,o,a=0,l=[],c=new M(u);if(null==t?t=_:Wt(t,0,Ht),n=Rt(t/Nt),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(n*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(o%1e14),a+=2);a=n/2}else{if(!crypto.randomBytes)throw b=!1,Error(At+"crypto unavailable");for(e=crypto.randomBytes(n*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(o%1e14),a+=7);a=n/7}if(!b)for(;a=10;o/=10,a++);ai-1&&(null==r[n+1]&&(r[n+1]=0),r[n+1]+=r[n]/i|0,r[n]%=i)}return r.reverse()}return function(s,n,o,r,a){var l,c,h,d,p,u,g,f,v=s.indexOf("."),y=_,b=m;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(n)).pow(s.length-v),k=d,f.c=e(Vt(Ut(u.c),u.e,"0"),10,o,t),f.e=f.c.length),h=d=(g=e(s,n,o,a?(l=x,t):(l=t,x))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--h:(u.c=g,u.e=h,u.s=r,g=(u=i(u,f,y,b,o)).c,p=u.r,h=u.e),v=g[c=h+y+1],d=o/2,p=p||c<0||null!=g[c+1],p=b<4?(null!=v||p)&&(0==b||b==(u.s<0?3:2)):v>d||v==d&&(4==b||p||6==b&&1&g[c-1]||b==(u.s<0?8:7)),c<1||!g[0])s=p?Vt(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=c,p)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=Vt(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,n,o,r,a=0,l=t.length,c=e%Lt,h=e/Lt|0;for(t=t.slice();l--;)a=((n=c*(o=t[l]%Lt)+(s=h*o+(r=t[l]/Lt|0)*c)%Lt*Lt+a)/i|0)+(s/Lt|0)+h*r,t[l]=n%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var n,o;if(i!=s)o=i>s?1:-1;else for(n=o=0;ne[n]?1:-1;break}return o}function i(t,e,i,s){for(var n=0;i--;)t[i]-=n,n=t[i]1;t.splice(0,1));}return function(s,n,o,r,a){var l,c,h,d,p,u,_,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,a||(a=Et,c=qt(s.e/Nt)-qt(n.e/Nt),S=S/Nt|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,(p=zt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),$=D.length,w=C.length),b=$,v=(f=C.slice(0,$)).length;v<$;f[v++]=0);x=D.slice(),x=[0].concat(x),k=D[0],D[1]>=a/2&&k++;do{if(p=0,(l=e(D,f,$,v))<0){if(y=f[0],$!=v&&(y=y*a+(f[1]||0)),(p=zt(y/k))>1)for(p>=a&&(p=a-1),_=(u=t(D,p,a)).length,v=f.length;1==e(u,f,_,v);)p--,i(u,$<_?x:D,_,a),_=u.length,l=1;else 0==p&&(l=p=1),_=(u=D.slice()).length;if(_=10;S/=10,h++);P(m,o+(m.e=h+c*Nt-1)+1,r,d)}else m.e=c,m.r=+d;return m}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,n=function(t,e,i,s){var n,o=i?e:e.replace(d,"");if(h.test(o))t.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(a,function(t,e,i){return n="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=n?t:e}),s&&(n=s,o=o.replace(l,"$1").replace(c,"0.$1")),e!=o))return new M(o,n);if(M.DEBUG)throw Error(At+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},p.absoluteValue=p.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},p.comparedTo=function(t,e){return jt(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,n,o=this;if(null!=t)return Wt(t,0,Ht),null==e?e=m:Wt(e,0,8),P(new M(o),t+o.e+1,e);if(!(i=o.c))return null;if(s=((n=i.length-1)-qt(this.e/Nt))*Nt,n=i[n])for(;n%10==0;n/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,m)},p.dividedToIntegerBy=p.idiv=function(t,e){return i(this,new M(t,e),0,1)},p.exponentiatedBy=p.pow=function(t,e){var i,s,n,o,r,a,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(At+"Exponent not an integer: "+R(t));if(null!=e&&(e=new M(e)),r=t.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!t.c||!t.c[0])return c=new M(Math.pow(+R(h),r?t.s*(2-Bt(t)):+R(t))),e?c.mod(e):c;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&h.isInteger()&&e.isInteger())&&(h=h.mod(e))}else{if(t.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||r&&h.c[1]>=24e7:h.c[0]<8e13||r&&h.c[0]<=9999975e7)))return o=h.s<0&&Bt(t)?-0:0,h.e>-1&&(o=1/o),new M(a?1/o:o);k&&(o=Rt(k/Nt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Bt(t)):l=(n=Math.abs(+R(t)))%2,c=new M(u);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):s&&(c=c.mod(e))}if(n){if(0===(n=zt(n/2)))break;l=n%2}else if(P(t=t.times(i),t.e+1,1),t.e>14)l=Bt(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:(a&&(c=u.div(c)),e?c.mod(e):o?P(c,k,m,void 0):c)},p.integerValue=function(t){var e=new M(this);return null==t?t=m:Wt(t,0,8),P(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===jt(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return jt(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=jt(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&qt(this.e/Nt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return jt(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=jt(this,new M(t,e)))||0===e},p.isNaN=function(){return!this.s},p.isNegative=function(){return this.s<0},p.isPositive=function(){return this.s>0},p.isZero=function(){return!!this.c&&0==this.c[0]},p.minus=function(t,e){var i,s,n,o,r=this,a=r.s;if(e=(t=new M(t,e)).s,!a||!e)return new M(NaN);if(a!=e)return t.s=-e,r.plus(t);var l=r.e/Nt,c=t.e/Nt,h=r.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?r:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?r:3==m?-0:0)}if(l=qt(l),c=qt(c),h=h.slice(),a=l-c){for((o=a<0)?(a=-a,n=h):(c=l,n=d),n.reverse(),e=a;e--;n.push(0));n.reverse()}else for(s=(o=(a=h.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;h[i++]=0);for(e=Et-1;s>a;){if(h[--s]=0;){for(i=0,p=y[n]%g,u=y[n]/g|0,o=n+(r=l);o>n;)i=((c=p*(c=v[--r]%g)+(a=u*c+(h=v[r]/g|0)*p)%g*g+_[o]+i)/m|0)+(a/g|0)+u*h,_[o--]=c%m;_[o]=i}return i?++s:_.splice(0,1),T(t,_,s)},p.negated=function(){var t=new M(this);return t.s=-t.s||null,t},p.plus=function(t,e){var i,s=this,n=s.s;if(e=(t=new M(t,e)).s,!n||!e)return new M(NaN);if(n!=e)return t.s=-e,s.minus(t);var o=s.e/Nt,r=t.e/Nt,a=s.c,l=t.c;if(!o||!r){if(!a||!l)return new M(n/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*n)}if(o=qt(o),r=qt(r),a=a.slice(),n=o-r){for(n>0?(r=o,i=l):(n=-n,i=a),i.reverse();n--;i.push(0));i.reverse()}for((n=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=n),n=0;e;)n=(a[--e]=a[e]+l[e]+n)/Et|0,a[e]=Et===a[e]?0:a[e]%Et;return n&&(a=[n].concat(a),++r),T(t,a,r)},p.precision=p.sd=function(t,e){var i,s,n,o=this;if(null!=t&&t!==!!t)return Wt(t,1,Ht),null==e?e=m:Wt(e,0,8),P(new M(o),t,e);if(!(i=o.c))return null;if(s=(n=i.length-1)*Nt+1,n=i[n]){for(;n%10==0;n/=10,s--);for(n=i[0];n>=10;n/=10,s++);}return t&&o.e+1>s&&(s=o.e+1),s},p.shiftedBy=function(t){return Wt(t,-9007199254740991,Ft),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,n,o,r=this,a=r.c,l=r.s,c=r.e,h=_+4,d=new M("0.5");if(1!==l||!a||!a[0])return new M(!l||l<0&&(!a||a[0])?NaN:a?r:1/0);if(0==(l=Math.sqrt(+R(r)))||l==1/0?(((e=Ut(a)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=qt((c+1)/2)-(c<0||c%2),s=new M(e=l==1/0?"5e"+c:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+c)):s=new M(l+""),s.c[0])for((l=(c=s.e)+h)<3&&(l=0);;)if(o=s,s=d.times(o.plus(i(r,o,h,1))),Ut(o.c).slice(0,l)===(e=Ut(s.c)).slice(0,l)){if(s.e0&&_>0){for(o=_%a||a,h=u.substr(0,o);o<_;o+=a)h+=c+u.substr(o,a);l>0&&(h+=c+u.slice(o)),p&&(h="-"+h)}s=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,n,o,r,a,l,c,h,d,p,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(At+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+R(l));if(!f)return new M(g);for(e=new M(u),h=s=new M(u),n=c=new M(u),_=Ut(f),r=e.e=_.length-g.e-1,e.c[0]=Ot[(a=r%Nt)<0?Nt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:h:l,a=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,p=i(h,n,r*=2,m).minus(g).abs().comparedTo(i(c,s,r,m).minus(g).abs())<1?[h,n]:[c,s],y=a,p},p.toNumber=function(){return+R(this)},p.toPrecision=function(t,e){return null!=t&&Wt(t,1,Ht),C(this,t,e,2)},p.toString=function(t){var e,i=this,n=i.s,o=i.e;return null===o?n?(e="Infinity",n<0&&(e="-"+e)):e="NaN":(null==t?e=o<=g||o>=f?Gt(Ut(i.c),o):Vt(Ut(i.c),o,"0"):10===t&&S?e=Vt(Ut((i=P(new M(i),_+o+1,m)).c),i.e,"0"):(Wt(t,2,x.length,"Base"),e=s(Vt(Ut(i.c),o,"0"),10,t,n,!0)),n<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return R(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),Yt=class{key;left=null;right=null;constructor(t){this.key=t}},Jt=class extends Yt{constructor(t){super(t)}},Zt=class{size=0;modificationCount=0;splayCount=0;splay(t){const e=this.root;if(null==e)return this.compare(t,t),-1;let i=null,s=null,n=null,o=null,r=e;const a=this.compare;let l;for(;;)if(l=a(r.key,t),l>0){let e=r.left;if(null==e)break;if(l=a(e.key,t),l>0&&(r.left=e.right,e.right=r,r=e,e=r.left,null==e))break;null==i?s=r:i.left=r,i=r,r=e}else{if(!(l<0))break;{let e=r.right;if(null==e)break;if(l=a(e.key,t),l<0&&(r.right=e.left,e.left=r,r=e,e=r.right,null==e))break;null==n?o=r:n.right=r,n=r,r=e}}return null!=n&&(n.right=r.left,r.left=o),null!=i&&(i.left=r.right,r.right=s),this.root!==r&&(this.root=r,this.splayCount++),l}splayMin(t){let e=t,i=e.left;for(;null!=i;){const t=i;e.left=t.right,t.right=e,e=t,i=e.left}return e}splayMax(t){let e=t,i=e.right;for(;null!=i;){const t=i;e.right=t.left,t.left=e,e=t,i=e.right}return e}_delete(t){if(null==this.root)return null;if(0!=this.splay(t))return null;let e=this.root;const i=e,s=e.left;if(this.size--,null==s)this.root=e.right;else{const t=e.right;e=this.splayMax(s),e.right=t,this.root=e}return this.modificationCount++,i}addNewRoot(t,e){this.size++,this.modificationCount++;const i=this.root;null!=i?(e<0?(t.left=i,t.right=i.right,i.right=null):(t.right=i,t.left=i.left,i.left=null),this.root=t):this.root=t}_first(){const t=this.root;return null==t?null:(this.root=this.splayMin(t),this.root)}_last(){const t=this.root;return null==t?null:(this.root=this.splayMax(t),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(t){return this.validKey(t)&&0==this.splay(t)}defaultCompare(){return(t,e)=>te?1:0}wrap(){return{getRoot:()=>this.root,setRoot:t=>{this.root=t},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:t=>{this.splayCount=t},splay:t=>this.splay(t),has:t=>this.has(t)}}},Xt=class t extends Zt{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 Jt(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Jt(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 Jt(t.key);return function t(e,i){let s,n;do{if(s=e.left,n=e.right,null!=s){const e=new Jt(s.key);i.left=e,t(s,e)}if(null!=n){const t=new Jt(n.key);i.right=t,e=n,i=t}}while(null!=n)}(t,e),e}toSet(){return this.clone()}entries(){return new ee(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new te(this.wrap())}[Symbol.toStringTag]="[object Set]"},Qt=class{tree;path=new Array;modificationCount=null;splayCount;constructor(t){this.tree=t,this.splayCount=t.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const t=this.path[this.path.length-1];return this.getValue(t)}rebuildPath(t){this.path.splice(0,this.path.length),this.tree.splay(t),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(t){for(;null!=t;)this.path.push(t),t=t.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let t=this.tree.getRoot();for(;null!=t;)this.path.push(t),t=t.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let t=this.path[this.path.length-1],e=t.right;if(null!=e){for(;null!=e;)this.path.push(e),e=e.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===t;)t=this.path.pop();return this.path.length>0}},te=class extends Qt{getValue(t){return t.key}},ee=class extends Qt{getValue(t){return[t.key,t.key]}},ie=t=>()=>t,se=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):ie(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function ne(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)):ie(!1);return(t,i,s)=>{const n=t.x,o=t.y,r=s.x,a=s.y,l=o.minus(a).times(i.x.minus(r)).minus(n.minus(r).times(i.y.minus(a)));return e(l,n,o,r,a)?0:l.comparedTo(0)}}var oe=t=>t,re=t=>{if(t){const e=new Xt(se(t)),i=new Xt(se(t)),s=(t,e)=>e.addAndReturn(t),n=t=>({x:s(t.x,e),y:s(t.y,i)});return n({x:new Kt(0),y:new Kt(0)}),n}return oe},ae=t=>({set:t=>{le=ae(t)},reset:()=>ae(t),compare:se(t),snap:re(t),orient:ne(t)}),le=ae(),ce=(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),he=(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}}},de=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),pe=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ue=t=>pe(t,t).sqrt(),_e=(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 de(n,s).div(ue(n)).div(ue(s))},me=(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(ue(n)).div(ue(s))},ge=(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)))},ve=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:Me.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:_e(this.point,t.point,s.point),cosine:me(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:n,cosine:o}=e.get(t),{sine:r,cosine:a}=e.get(s);return n.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?o.isLessThan(a)?1:o.isGreaterThan(a)?-1:0:n.isLessThan(0)&&r.isLessThan(0)?o.isLessThan(a)?-1:o.isGreaterThan(a)?1:0:r.isLessThan(n)?-1:r.isGreaterThan(n)?1:0}}},ye=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}}},be=class{exteriorRing;interiorRings;constructor(t){this.exteriorRing=t,t.poly=this,this.interiorRings=[]}addInterior(t){this.interiorRings.push(t),t.poly=this}getGeom(){const t=this.exteriorRing.getGeom();if(null===t)return null;const e=[t];for(let t=0,i=this.interiorRings.length;t0?(this.tree.delete(e),i.push(t)):(this.segments.push(e),e.prev=s)}else{if(s&&n){const t=s.getIntersection(n);if(null!==t){if(!s.isAnEndpoint(t)){const e=this._splitSafely(s,t);for(let t=0,s=e.length;t0)return-1;const s=e.comparePoint(t.rightSE.point);return 0!==s?s:-1}if(i.isGreaterThan(s)){if(r.isLessThan(a)&&r.isLessThan(c))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(c))return 1;const i=e.comparePoint(t.leftSE.point);if(0!==i)return i;const s=t.comparePoint(e.rightSE.point);return s<0?1:s>0?-1:1}if(r.isLessThan(a))return-1;if(r.isGreaterThan(a))return 1;if(n.isLessThan(o)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(n.isGreaterThan(o)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!n.eq(o)){const t=l.minus(r),e=n.minus(i),h=c.minus(a),d=o.minus(s);if(t.isGreaterThan(e)&&h.isLessThan(d))return 1;if(t.isLessThan(e)&&h.isGreaterThan(d))return-1}return n.isGreaterThan(o)?1:n.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++Se,this.leftSE=t,t.segment=this,t.otherSE=e,this.rightSE=e,e.segment=this,e.otherSE=t,this.rings=i,this.windings=s}static fromRing(e,i,s){let n,o,r;const a=ve.comparePoints(e,i);if(a<0)n=e,o=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);n=i,o=e,r=-1}const l=new ve(n,!0),c=new ve(o,!1);return new t(l,c,[s],[r])}replaceRightSE(t){this.rightSE=t,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const t=this.leftSE.point.y,e=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:t.isLessThan(e)?t:e},ur:{x:this.rightSE.point.x,y:t.isGreaterThan(e)?t:e}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(t){return t.x.eq(this.leftSE.point.x)&&t.y.eq(this.leftSE.point.y)||t.x.eq(this.rightSE.point.x)&&t.y.eq(this.rightSE.point.y)}comparePoint(t){return le.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=he(e,i);if(null===s)return null;const n=this.leftSE.point,o=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=ce(e,r)&&0===this.comparePoint(r),c=ce(i,n)&&0===t.comparePoint(n),h=ce(e,a)&&0===this.comparePoint(a),d=ce(i,o)&&0===t.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?a:null;if(c)return h&&n.x.eq(a.x)&&n.y.eq(a.y)?null:n;if(l)return d&&o.x.eq(r.x)&&o.y.eq(r.y)?null:r;if(d&&h)return null;if(d)return o;if(h)return a;const p=((t,e,i,s)=>{if(e.x.isZero())return fe(i,s,t.x);if(s.x.isZero())return fe(t,e,i.x);if(e.y.isZero())return ge(i,s,t.y);if(s.y.isZero())return ge(t,e,i.y);const n=de(e,s);if(n.isZero())return null;const o={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=de(o,e).div(n),a=de(o,s).div(n),l=t.x.plus(a.times(e.x)),c=i.x.plus(r.times(s.x)),h=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(n,this.vector(),r,t.vector());return null===p?null:ce(s,p)?le.snap(p):null}split(e){const i=[],s=void 0!==e.events,n=new ve(e,!0),o=new ve(e,!1),r=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(n);const a=new t(n,r,this.rings.slice(),this.windings.slice());return ve.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),ve.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}},Ce=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=le.snap({x:new Kt(t[0][0]),y:new Kt(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;exe.run("union",t,e),Re=(t,...e)=>xe.run("intersection",t,e),ze=(t,...e)=>xe.run("difference",t,e);function Ae(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function Ie(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 Ee(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 Ne(t,e,i=1){const s=t[0].toFixed(i),n=t[1].toFixed(i),o=e[0].toFixed(i),r=e[1].toFixed(i),a=s=3?t.poly:t&&null!=t.x&&null!=t.y&&null!=t.w&&null!=t.h?[[t.x,t.y],[t.x+t.w,t.y],[t.x+t.w,t.y+t.h],[t.x,t.y+t.h]]:null}function Oe(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 Le(t){const e=[],i=new Set;for(const s of t||[]){const t=Fe(s);if(t)for(let s=0;s=90?t-=180:t<-90&&(t+=180),s.step&&s.step>0){const i=Math.sqrt(d),o=Math.min(Math.max(s.length||0,0)/2,i/2);let a=Math.round(p*i/s.step)*s.step;Math.abs(p*i-i/2)<=s.step/2&&(a=i/2),a=Math.max(o,Math.min(i-o,a));const l=a/i;n={x:e+l*c,y:r+l*h,angle:t}}else n={x:u[0],y:u[1],angle:t}}}return n}function qe(t,e,i,s,n,o=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,c=o;for(const e of Le(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(a[0]*(e[1]-t[1])-a[1]*(e[0]-t[0]));if(s(i[0])>o||s(i[1])>o)continue;const n=(i[0][0]-t[0])*a[0]+(i[0][1]-t[1])*a[1],r=(i[1][0]-t[0])*a[0]+(i[1][1]-t[1])*a[1],h=Math.min(n,r),d=Math.max(n,r),p=h>0?h:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],g=(h+d)/2;return{wallA:m(h),wallB:m(d),sideA:u,sideB:_,midA:m((h-p)/2),midB:m((p+d)/2),wallCenter:m(g),centered:Math.abs(g)<=n}}function Ue(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 je(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}function Be(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;let r=o?((t[0]-e[0])*s+(t[1]-e[1])*n)/o:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*n))}function Ge(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let n=0;nn&&r<-n||o<-n&&r>n)&&(a>n&&l<-n||a<-n&&l>n)}function Ze(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1;let c=0,h=0,d=0;for(let e=0;e1e-9?[h/(3*c),d/(3*c)]:[(n+o)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!We([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=(a-r)/e;for(let e=-4;e<=4;e++)for(let n=-4;n<=4;n++){const o=t+s*e/4,r=i+l*n/4,a=u(o,r);a>m&&(m=a,_=[o,r])}}return _||Xe(t)||t[0]}function Xe(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(Ke(s,t,e))return s;for(let s=0;sti(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&ti(e,t,i)))}function si(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 oi(t,e,i){for(let s=0;s1&&je(i[0],i[i.length-1],e)&&i.pop(),i}function ai(t,e,i){const[s,n]=t.split(":");return"device"===s?n:"entity"===s?"lg_"+n:e&&e.startsWith("v_")?e:i()}function li(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 hi(t,e,i,s){if(t.length<2)return;const n=e.x+s,o=e.x+e.w-s,r=e.y+s,a=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;st.startsWith("cover."))||null}const wi=["automation","script","scene"];const ki="—",$i="{}";function xi(t){const e=String(t??"").trim();if(!e)return null;let i=e,s="";const n=e.indexOf(":");if(n>=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 Si(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),n=xi(s?`${i}:${s}`:i);return n?`{${n.entity}${n.attr?`:${n.attr}`:""}}`:""}function Mi(t,e,i){const s=String(e??"").trim();if(!s)return null;const n=t?.states?.[s];if(!n)return null;const o=String(i??"").trim(),r=t=>t.slice(0,60);if(o){const e=function(t){if(null==t)return null;if(Array.isArray(t)){const e=t.map(t=>null==t?"":String(t)).join(", ");return e?e.slice(0,60):null}if("object"==typeof t)return null;const e=String(t);return""===e?null:e.slice(0,60)}(n.attributes?.[o]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,n,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=n.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function Ci(t,e,i){const s=String(e??"").trim(),n=String(i??"").trim()||s;if(!n)return t.text;const o=t.formatted&&s?function(t,e){if(!e)return t;const i=t.replace(/\s+$/,"");return i.endsWith(e)?i.slice(0,i.length-e.length).replace(/\s+$/,""):t}(t.text,s):t.text;return`${o} ${n}`}function Di(t,e){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 ki;const o=(e?.attr||"").trim(),r=Mi(t,i,o||null);if(null===r)return ki;return Ci(r,o?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}const Ti=20;function Pi(t){const e=Number(t?.scale);if(Number.isFinite(e)&&e>0)return Math.min(20,Math.max(.15,e));return{s:.7,m:1,l:1.5}[String(t?.size??"")]??1}function Ri(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 zi="#55606c",Ai=.55,Ii={c:"#607d8b",a:.18};function Ei(t,e=Ii){const i=t&&"object"==typeof t?t:null,s=i?.a;return{c:_t(i?.c,e.c),a:"number"==typeof s&&Number.isFinite(s)?Math.min(1,Math.max(0,s)):e.a}}function Ni(t,e){const i=Ei(t),s=e?.settings?.custom_fill;return s&&"object"==typeof s?Ei(s,i):i}function Fi(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:_t(e.room_color,zi),opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):Ai,fill:["lqi","light","temp","custom"].includes(e.fill_mode)?e.fill_mode:"none",customFill:Ei(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:_t(e.bg_color,null),hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function Oi(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return _t(i,"")}const Li={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 Hi(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(Li)){const s=Li[t],n=i[t];e[t]={c:_t(n?.c,s.c),a:n&&"number"==typeof n.a?Math.min(1,Math.max(0,n.a)):s.a}}return e}function qi(t,e,i){const s=Math.min(1,Math.max(0,i)),n=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),o=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=n.map((t,e)=>Math.round(t+(o[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function Ui(t,e,i,s,n,o,r,a=Ii){const l=function(t,e,i,s,n,o,r,a=Ii){if("custom"===t)return Ei(a);if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:qi(r.lqi_low.c,r.lqi_high.c,t),a:r.lqi_low.a+(r.lqi_high.a-r.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?r.light_none.a>0?r.light_none:null:"on"===i?r.light_on:r.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(n,o),e=Math.max(n,o);return se?r.temp_hot:r.temp_ok}return null}(t,e,i,s,n,o,r,a);return l?{color:l.c,opacity:l.a,mode:t}:null}const ji={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"]},Wi=[["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 Bi(t){for(const e of[...Object.values(ji),...Wi])if(t===e[0]||t===e[1])return e;return null}function Gi(t,e,i,s,n){if(!s||"unavailable"===s||"unknown"===s)return t;if(n){const i="cover"===e?Bi(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=ji[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const n=Bi(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 Vi(t,e){if(!t||"on"!==t.state)return null;const i=t.attributes||{},s=Number(i.brightness),n=Number.isFinite(s)&&s>0?Math.max(.15,Math.min(1,s/255)):1,o=ft(i.rgb_color);if(o)return{c:o,bri:n};const r=Number(i.color_temp_kelvin)||(Number(i.color_temp)>0?1e6/Number(i.color_temp):NaN);if(Number.isFinite(r)&&r>0){const[t,e,i]=function(t){const e=Math.min(4e4,Math.max(1e3,t))/100,i=e<=66?255:329.698727446*Math.pow(e-60,-.1332047592),s=e<=66?99.4708025861*Math.log(e)-161.1195681661:288.1221695283*Math.pow(e-60,-.0755148492),n=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,o=t=>Math.round(Math.min(255,Math.max(0,t)));return[o(i),o(s),o(n)]}(r);return{c:`rgb(${t}, ${e}, ${i})`,bri:n}}return{c:e,bri:n}}function Ki(t,e,i,s,n=170,o=0){const r=Math.hypot(e[0]-t[0],e[1]-t[1]),a=Math.hypot(i[0]-t[0],i[1]-t[1]);if(r<1e-6||a<1e-6||Math.min(r,a)>=s)return null;let l,c;if(Number.isFinite(o)&&o>1e-6){const s=i[0]-e[0],n=i[1]-e[1],r=Math.hypot(s,n);if(r<1e-6)return null;const a=-(n/r),h=s/r,d=(e[0]+i[0])/2,p=(e[1]+i[1])/2,u=(t[0]-d)*a+(t[1]-p)*h>=0?1:-1,_=o/2,m=[e[0]+a*_*u,e[1]+h*_*u],g=[i[0]+a*_*u,i[1]+h*_*u],f=[e[0]-a*_*u,e[1]-h*_*u],v=[i[0]-a*_*u,i[1]-h*_*u],y=Math.atan2(p-t[1],d-t[0]),b=e=>{let i=Math.atan2(e[1]-t[1],e[0]-t[0]);for(;i-y>Math.PI;)i-=2*Math.PI;for(;i-y<-Math.PI;)i+=2*Math.PI;return i},w=b(m),k=b(g),$=b(f),x=b(v),S=Math.max(Math.min(w,k),Math.min($,x)),M=Math.min(Math.max(w,k),Math.max($,x));if(!(M-S>1e-9))return null;l=S,c=M-S}else{for(l=Math.atan2(e[1]-t[1],e[0]-t[0]),c=Math.atan2(i[1]-t[1],i[0]-t[0])-l;c>Math.PI;)c-=2*Math.PI;for(;c<-Math.PI;)c+=2*Math.PI}const h=n*Math.PI/180;if(Math.abs(c)>h){const t=l+c/2;c=h*Math.sign(c),l=t-c/2}const d=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=l+c*e/8;d.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return d}function Yi(t,e,i,s,n){const o=e*Math.PI/180,r=[-Math.sin(o),Math.cos(o)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*n*a,t[1]+r[1]*n*a];return s.some(t=>Ke(l,t,1e-9))}function Ji(t){return t.startsWith("light.")||t.startsWith("switch.")}function Zi(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let n=0;np||l>p)continue;const u=(n[0]-o[0])*h+(n[1]-o[1])*d,_=(r[0]-o[0])*h+(r[1]-o[1])*d,m=Math.max(0,Math.min(u,_)),g=Math.min(c,Math.max(u,_));g-m>i&&s.push([o[0]+h*m,o[1]+d*m,o[0]+h*g,o[1]+d*g])}}return s}function Xi(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let n=!0;for(;n;){n=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const o of e)if(o.id&&i.has(o.id)&&s(t,o)){i.add(t.id),n=!0;break}}return i}function Qi(t,e,i=1e-6){const s=[];for(const n of t){const t=[n[0],n[1]],o=[n[2],n[3]],r=o[0]-t[0],a=o[1]-t[1],l=Math.hypot(r,a);if(lo||n>o)continue;const r=(s[0]-t[0])*c+(s[1]-t[1])*h,a=(s[2]-t[0])*c+(s[3]-t[1])*h,p=Math.max(0,Math.min(r,a)),u=Math.min(l,Math.max(r,a));u-p>i&&d.push([p,u])}if(!d.length){s.push([t[0],t[1],o[0],o[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,n]of d)e-p>i&&s.push([t[0]+c*p,t[1]+h*p,t[0]+c*e,t[1]+h*e]),p=Math.max(p,n);l-p>i&&s.push([t[0]+c*p,t[1]+h*p,o[0],o[1]])}return s}function ts(t,e,i=1e-6){const s=[];for(let e=0;e{if("string"!=typeof t||!t)return;const i=ns(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 ns(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 os(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i||"custom"===i?i:t}function rs(t,e){const i=e?.settings;return"boolean"==typeof i?.glow?i.glow:"glow"===i?.fill_mode||t}function as(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function ls(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function cs(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function hs(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 ds=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);function ps(t,e){return"alarm_control_panel"===t||"siren"===t||"binary_sensor"===t&&!!e&&ds.has(e)}function us(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 _s={availability:"available",status:"alarm",activity:"none"},ms=new Set(["motion","vibration","sound"]),gs=new Set(["occupancy","presence"]),fs=new Set(["door","window","garage_door","opening"]),vs=new Set(["running","power"]),ys=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const bs=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),ws=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping"]),ks=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),$s=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,xs=t=>String(t??"").trim().toLowerCase();function Ss(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(xs).some(t=>["power","main power","power switch","питание"].includes(t)))return!0;const o=xs(s?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(o)||/(?:^|[\s._-])питание$/.test(o)}function Ms(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=xs(t?.[e]);if(bs.has(i)||ws.has(i))return i}return""}function Cs(t,e){const i=t?.states?.[e],s=i?xs(i.state):"__missing__",n=String(e||"").split(".")[0],o=xs(i?.attributes?.device_class),r={eid:e,state:s,availability:$s(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===r.availability)return r;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&ps(t,e)}(n,o,s))return{...r,status:"alarm"};if("binary_sensor"===n)return ms.has(o)?{...r,edge:"rising"}:gs.has(o)?{...r,activity:"on"===s?"presence":"none"}:fs.has(o)?{...r,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===o?{...r,activity:"on"===s?"transition":"none"}:vs.has(o)&&"on"===s?{...r,status:"working",activity:"running"}:r;if("cover"===n)return{...r,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===n)return{...r,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===n)return{...r,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===n){const t=Ms(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(xs):[],n=!ws.has(s)&&(ks.has(s)||bs.has(s)||e.includes(s));return(t?bs.has(t):n)?{...r,status:"working",activity:"running"}:r}if(["light","switch","fan","humidifier"].includes(n))return"on"===s?{...r,status:"working",activity:"running"}:r;if("media_player"===n)return"off"===s?{...r,availability:"unavailable"}:r;if("vacuum"===n)return"cleaning"===s?{...r,status:"working",activity:"running"}:"returning"===s?{...r,status:"working",activity:"transition"}:r;if("script"===n)return"on"===s?{...r,status:"working",activity:"running"}:r;if("automation"===n)return r;if("button"===n||"event"===n)return{...r,edge:"change"};const a=Ms(i.attributes);return bs.has(a)||bs.has(s)&&!ws.has(s)?{...r,status:"working",activity:"running"}:r}function Ds(t,e){if(!(t=>!!t&&!$s(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 Ts=new WeakMap,Ps=new WeakMap;let Rs=1;const zs="houseplan.ha-binding-status.v1";let As=null;function Is(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=Ts.get(e);return i||(i={revision:0,authoritative:!1,access:"pending",devices:{},entities:{},lastSuccess:0,listeners:new Set,refs:0},Ts.set(e,i)),i}function Es(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 Ns(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=Es(i,"id"),o=Es(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 Fs(t,e){void 0===e.reloadTimer&&(e.reloadTimer=globalThis.setTimeout(()=>{e.reloadTimer=void 0,Ns(t,e)},80))}async function Os(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,()=>Fs(t,e),"device_registry_updated")),e.unsubEntity||(e.unsubEntity=await i.call(t.connection,()=>Fs(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 Ls(t,e){const i=Is(t);if(!i)return()=>{};const s=0===i.refs;i.refs++,i.listeners.add(e),("pending"===i.access||s)&&Ns(t,i),Os(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 Hs(t){const e=Is(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&&Fs(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 qs(t){if(!t||"object"!=typeof t&&"function"!=typeof t)return 0;let e=Ps.get(t);return e||(e=Rs++,Ps.set(t,e)),e}function Us(t,e=Hs(t)){return[e.revision,e.access,qs(e.devices),qs(e.entities)].join(":")}function js(t){return!!t&&null==t.disabled_by}function Ws(t,e=Hs(t)){const i={},s={},n={};for(const[t,s]of Object.entries(e.devices||{}))js(s)&&(i[t]=s);for(const[t,i]of Object.entries(e.entities||{})){if(!js(i))continue;const n=i.device_id?e.devices?.[i.device_id]:null;e.authoritative&&i.device_id&&!n||(n&&!js(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&&!js(t))continue;const o=t?.device_id?e.devices?.[t.device_id]:null;e.authoritative&&t?.device_id&&!o||(o&&!js(o)||(n[i]=s))}return{...t,devices:i,entities:s,states:n}}function Bs(t,e=Hs(t)){return{...t,devices:e.devices||{},entities:e.entities||{}}}function Gs(t,e){const i=[];for(const[s,n]of Object.entries(t||{}))n?.device_id===e&&i.push(s);return i}function Vs(){if(As)return As;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 As=i,As}catch{return{}}}function Ks(t,e,i=Hs(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 r=i.devices||{},a=i.entities||{};if(i.authoritative){if("device"===n){const t=r[o];if(!t)return{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[]};const e=Gs(a,o);if(!js(t))return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:e};const i=e.filter(t=>js(a[t]));return e.length&&!i.length?{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:e}:{kind:"active",enabledEntityIds:i,allEntityIds:e}}const e=a[o];return e&&!js(e)?{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]}:e?.device_id&&!r[e.device_id]?{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[o]}:e?.device_id&&!js(r[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 Vs()[t]||null}(e);if("device"===n){const e=r[o],i=Gs(a,o);if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:i};const s=i.filter(t=>{const e=a[t];return null==e?.disabled_by&&(!e.device_id||null==r[e.device_id]?.disabled_by)});if(i.length&&!s.length&&i.every(t=>null!=a[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=a[o];if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]};if(e?.device_id&&null!=r[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 Ys(t){const e={};for(const[i,s]of Object.entries(t.entities)){if(!s?.device_id||!js(s))continue;const n=t.devices?.[s.device_id];n&&!js(n)||(e[s.device_id]=e[s.device_id]||[]).push(i)}return e}function Js(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 Zs(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 Xs=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],Qs=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function tn(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 Xs){const e=n.filter(e=>e.eid.startsWith(t+"."));if(e.length)return Qs(e).map(t=>t.eid)}const o=n.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=xs(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return ms.has(i)||gs.has(i)||fs.has(i)||vs.has(i)||"moving"===i||ys.has(i)}(t,e.eid));if(o.length)return Qs(o).map(t=>t.eid);const r=n.filter(t=>t.eid.startsWith("switch."));if(r.length){const e=Qs(r);return[(e.find(e=>Ss(t,e.eid))||e[0]).eid]}const a=[];for(const t of Tt)a.push(...Qs(n.filter(e=>e.eid.startsWith(t+"."))));return a.push(...Qs(n.filter(t=>!Tt.includes(t.eid.split(".")[0])))),a.map(t=>t.eid)}function en(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=>Zs(t,e.eid));if(i)return i.eid}return tn(t,e)[0]}function sn(t,e,i=[]){return[...new Set(nn(t,e,i))].filter(t=>Ji(t))}function nn(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 on(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 rn(t,e){const i=sn(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"}));if(!0===e.marker?.is_light){const t=function(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(Ji),s=t.primary&&Ji(t.primary)?t.primary:null;return(e&&Ji(e)?e:null)||s||i[0]||null}(e);t&&!i.some(e=>e.eid===t)&&i.push({eid:t,via:"forced"})}if(i.length)return i;const s=e.primary||tn(t,e.entities)[0];return s&&!s.startsWith("light.")?[]:e.entities.filter(t=>t.startsWith("light.")).map(t=>({eid:t,via:"light"}))}function an(t,e,i){const s=[],n=new Map;for(const o of e){if(o.hidden||null!=i&&!on(o,i))continue;const e=rn(t,o);for(const{eid:i,via:r}of e){if(!i)continue;const e="controls"!==r,a={eid:i,device:o,via:r,castsGlow:e,on:"on"===t.states[i]?.state},l=n.get(i);null==l?(n.set(i,s.length),s.push(a)):e&&!s[l].castsGlow&&(s[l]=a)}}return s}function ln(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function cn(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 li(i)}function hn(t,e){for(const i of e){if(!Zs(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 dn(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 pn(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 un(t,e){for(const i of e){if(!pn(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 _n(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden||!js(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(!js(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 mn(t,e,i,s,n){const o=Dt(e,i,n);if(o!==Ct)return o;const r=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&r.push(i)}return function(t){for(const e of t){const t=Mt[e];if(t)return t}return null}(r)??Ct}function gn(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 fn(t,e,i){if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function vn(t,e,i,s,n){const o=sn(e.binding,e.controls,t.entities).filter(t=>!fn(i,t,s)).filter(t=>"active"===Ks(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 yn(t){const e=t.hass,i=t.registry||Hs(e),s=Ws(e,i),n=Bs(e,i),{areaToSpace:o,markers:r,settings:a,excluded:l,showAll:c,firstSpaceId:h,loc:d,iconRules:p}=t,u=!1!==a.group_lights,_=gn(r),m=_n(s,u).filter(t=>!fn(s,t.eid,_)),g=new Set(m.map(t=>t.area)),f=Ys(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 r){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||y.add(t.binding)}const b=(t,e)=>r.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 r=Ks(e,"device:"+t.id,i);if("active"!==r.kind)continue;const h=b("device",t.id);if(h&&h.hidden&&!a.filter_seeded)continue;const _=f[t.id]||[],m=Js(s,t,_),v=!a.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=mn(s,$,t.model,_,p);if(_.some(t=>t.startsWith("lock."))&&(S="mdi:lock"),v&&!c&&u&&"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:r.allEntityIds,bindingStatus:r,bindingKind:"device",bindingRef:t.id,pdfs:[]};C.primary=en(s,_,S),"mdi:thermometer"!==S&&"mdi:air-filter"!==S||(C.temp=hn(s,_)),C.primary&&pn(s,C.primary)&&(C.hum=un(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 r){if(t.removed)continue;const[r,l]=t.binding.split(":"),c="device"===r||"entity"===r?Ks(e,t.binding,i):null;if(!t.hidden||a.filter_seeded||"ha_disabled"===c?.kind)if("device"===r){const e=c;if("unverified"===e.kind)continue;const r=n.devices[l],a=t.area||r?.area_id||"",u=a&&o[a]||t.space||h,m="active"===e.kind?e.enabledEntityIds:[];let g=r?mn(s,r.name_by_user||r.name||"",r.model,m,p):"mdi:help-circle";m.some(t=>t.startsWith("lock."))&&(g="mdi:lock");const f={id:t.id,name:r?.name_by_user||r?.name||d("device.fallback"),model:r?.model||"",area:a,space:u,icon:g,entities:m,allEntities:e.allEntityIds.length?e.allEntityIds:r&&v[r.id]||[],bindingStatus:e,bindingKind:"device",bindingRef:l};f.primary=en(s,m,g),"mdi:thermometer"!==g&&"mdi:air-filter"!==g||(f.temp=hn(s,m)),f.primary&&pn(s,f.primary)&&(f.hum=un(s,m)),vn(f,t,n,_,i),k.push(f)}else if("entity"===r){if(fn(n,l,_))continue;const e=c;if("unverified"===e.kind)continue;const r=n.entities[l],a=t.area||r?.area_id||r?.device_id&&n.devices[r.device_id]?.area_id||"",d=a&&o[a]||t.space||h,u=s.states[l],m=r?.name||u?.attributes?.friendly_name||l;let g=mn(s,m,"",[l],p);l.startsWith("lock.")&&(g="mdi:lock");const f={id:t.id,name:m,model:"",area:a,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=hn(s,f.entities)),f.entities.length&&pn(s,l)&&(f.hum=un(s,f.entities)),vn(f,t,n,_,i),k.push(f)}else{const e=t.area||"",s=t.space||e&&o[e]||h,r={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};vn(r,t,n,_,i),k.push(r)}}return k}function bn(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),r=gn(i);return"device"===n?r.devices.has(o):"entity"===n&&fn(t,o,r)}(t,e,s))return null;const n=e.indexOf(":");if(n<0)return null;const o=e.slice(0,n),r=e.slice(n+1);if(!r)return null;if("entity"===o){const e=parseFloat(t.states[r]?.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===r).map(([t])=>t);return"temp"===i?hn(t,e):un(t,e)}return null}const wn=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 kn(t,e,i){const s=[];for(const n of e){if(n.area!==i||n.virtual)continue;const e=cn(t,n.entities);null!=e&&s.push(e)}return li(s)}function $n(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=hn(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 xn={sourceKind:"none",visualSources:[],criticalSources:[],samples:[]};function Sn(t,e){const i=t?.entities?.[e],s=t?.states?.[e];return String(i?.name||i?.original_name||s?.attributes?.friendly_name||e)}function Mn(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 Cn(t,e,i,s){return{eid:e,role:i,name:Sn(t,e),state:String(t?.states?.[e]?.state??""),stateText:Mn(t,e),integrationDomain:t?.entities?.[e]?.platform||null,sample:s||Cs(t,e)}}function Dn(t,e){const i=e.hidden&&e.userHidden?{...e,hidden:!1}:e;let s="none",n=[];const o="cover"===i.tapAction?bi(i.entities):null,r=an(t,[i]);if(o)s="cover",n=[Cn(t,o,"cover")];else if(r.length)s=r.some(t=>"controls"===t.via)?"controls":"light",n=r.map(e=>Cn(t,e.eid,"controls"===e.via?"control":"forced"===e.via?"forced_light":"light"));else{const e=tn(t,i.entities);if(e.length){s="device_role";const o=function(t,e,i){const s=e.map(e=>Cs(t,e)),n=i.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category);return 1===e.length&&n.length>1&&Ss(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,e,i.entities);n=e.map((e,i)=>Cn(t,e,"device_role",o[i]))}else i.primary&&(s="primary",n=[Cn(t,i.primary,"primary")])}const a=[];for(const e of i.entities||[]){const i=Cs(t,e);"alarm"!==i.status||n.some(t=>t.eid===e)||a.push(Cn(t,e,"critical",i))}return{sourceKind:s,visualSources:n,criticalSources:a,samples:[...n,...a].map(t=>t.sample)}}function Tn(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(!Zs(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||!pn(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=Mi(t,e);return o?{text:Ci(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 Pn(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 Rn(t,e,i=!0,s){const n=s||Dn(t,e);return Pn(e,n,Tn(t,e,n,i).source)}function zn(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 An(t,e,i){const s=ui(e.marker?.display),n="static_icon"===s,o=n&&!1===i.sourceDetails?xn:Dn(t,e),r=e.bindingStatus,a="ha_disabled"===r?.kind,l="orphaned"===r?.kind,c=!0===e.userHidden||!0===e.marker?.hidden,h=a||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 _s;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 p=d;h||n?p={availability:"available",status:"neutral",activity:"none"}:"alarm"===d.status||i.liveStates||(p={availability:"available",status:"neutral",activity:"none"});const u=n&&!1===i.sourceDetails?{source:null,text:null,fallback:null}:Tn(t,e,o,i.showTemperature),_=Pn(e,o,u.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&&(p={...p,activity:m.flashKind});const f="icon_ripple"===s&&!h&&i.liveStates&&"alarm"!==p.status?p.activity:"none",v=n||h||!i.showTemperature?null:!0===e.marker?.use_climate_temp?dn(t,e.entities):"mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon?hn(t,e.entities):null,y=!n&&!h&&i.showTemperature&&e.primary&&pn(t,e.primary)?un(t,e.entities):null,b=n||h||!i.showSignal||e.virtual?null:cn(t,e.entities),w="cover"===o.sourceKind?o.visualSources[0]?.eid:e.primary||o.visualSources[0]?.eid,k=w?t?.states?.[w]:null,$=!i.liveStates||n||h?e.icon:Gi(e.icon,w?.split(".")[0],k?.attributes?.device_class,k?.state,!!e.marker?.icon),x=!i.liveStates||n||h?null:an(t,[{...e,hidden:!1}]).map(e=>function(t){return t&&"on"===t.state?ft(t.attributes?.rgb_color):null}(t?.states?.[e.eid])).find(t=>!!t)||null,S=Number(e.marker?.size)>0?Number(e.marker.size):1,M=Number(e.marker?.angle)||0,C=Number(e.marker?.ripple_size)>0?Number(e.marker.ripple_size):3,D=_t(e.marker?.ripple_color,null),T=n?null:D||x||null,P="value"!==s||h?null:u.text,R="ha_disabled"===r?.kind?r.reason:null,z=function(t,e,i,s,n,o,r,a){return o?"ha_disabled":r?"orphaned":"static_icon"===a?"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"===a&&"none"!==i?"working_activity":"working":"open"===e.status?"open":(t.primary||"").startsWith("media_player.")?"media_neutral":"neutral":"live_states_disabled"}(e,p,f,o.sourceKind,i.liveStates,a,l,s),A=[];i.designPreview&&c&&A.push("hidden_design_preview"),n||!0!==e.marker?.vacuum?.live||A.push("vacuum_live_plan_only");const I=1===o.visualSources.length&&Ss(t,o.visualSources[0].eid),E=e.entities.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category).length;I&&E>1&&A.push("composite_power_source"),"icon_ripple"!==s&&"static_icon"!==s&&"alarm"!==d.status&&"none"!==d.activity&&A.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:u.source,sourceSignature:_,visual:p,display:s,icon:$,valueText:P,valueFullText:P,fallbackReason:"value"!==s||P?null:u.fallback,activity:f,activityGeneration:m?.gen||1,classes:[],tempText:null==v?null:String(v),humText:null==y?null:String(y),lqiText:null==b?null:String(b),lqiColor:null==b?null:Ae(b),lightColor:x,scale:S,angle:M,rippleScale:C,rippleColor:T,userHidden:c,effectiveHidden:h,haDisabled:a,disabledReason:R,orphaned:l,vacuumLive:!n&&!0===e.marker?.vacuum?.live,explanation:{reason:z,notices:A}};return{...N,classes:zn(N)}}const In=new WeakMap;function En(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=In.get(e);return i||(i={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},In.set(e,i)),i}function Nn(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function Fn(t,e){const i=En(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,Nn(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Nn(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,Nn(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 On(t){const e=En(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function Ln(t,e,i=On(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 Hn(t,e,i,s,n){const o=String(e||"").trim();if(!o)return null;const r=n?s.configEntries[n]:null;return{domain:o,label:Ln(t,o,s,r?.title),configEntryId:n,configEntryTitle:r?.title,confidence:i}}function qn(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 Un(t,e,i,s=On(t)){const n=i.entities?.[e]||t?.entities?.[e];if(!n)return null;const o=n.config_entry_id||void 0,r=o?s.configEntries[o]?.domain:"";return Hn(t,n.platform||r,"entity-platform",s,o)}var jn={"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 (lights/switches)","marker.tap_label":"Tap action for this device","tap.toggle_note":"Toggle never applies to locks and alarms; hold the icon to open the info card.","import.title":"Create spaces from HA floors","import.hint":"Your Home Assistant already knows these floors. Pick the ones to turn into plan spaces — you will upload a floor-plan image for each one next. Rooms are then outlined by hand on the plan.","import.start":"Create {n} space(s)","import.manual":"Start from scratch","import.progress":"Floor {i} of {n}","import.done":"Spaces created. Outline the rooms: click grid dots and close the contour.","btn.skip":"Skip","space.scale_label":"Scale (grid cell size)","space.scale_unit":"cm per cell","space.display_section":"Display","space.show_borders":"Always show room borders","space.show_names":"Show room names (drag to move)","space.room_color":"Border & name color","space.opacity":"Opacity","space.fill_label":"Room fill","fill.none":"None","fill.lqi":"Zigbee signal","fill.light":"Lights","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…","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_hint":"empty = default from general settings","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set how thick it is. Empty or zero removes the thickness.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new room-wall segment (1–100 cm). Shared walls keep the neighbour's value.","toast.wallthick_pick":"Click a wall (not an open boundary)","toast.wallthick_open":"Open boundaries have no thickness","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from 1 to {max} {unit}","toast.physical_angle":"Enter a rotation angle from 0° up to, but not including, 90°","toast.physical_limit":"The space has reached the limit for this type of geometry","markup.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":"Use the device's temperature sensor","marker.use_climate_temp_tip":"Air conditioners and thermostats know the room temperature (current_temperature). Shows it as a badge next to the icon and counts it in the room average — like a regular thermometer.","marker.is_light":"This device is a light source","marker.is_light_tip":"Creates light-source glow even without a light entity — for a smart switch driving ordinary fixtures. The glow follows the switch (or the lights bound above).","confirm.unlock":"Unlock “{name}”?","toast.files_migrate_failed":"Attachments could not be moved to the new binding, links keep pointing at the old files: {err}","space.pick_saved":"Already uploaded","space.pick_saved_hint":"Plans stored on the server, including ones you detached earlier","space.no_saved":"No plans stored on the server yet.","space.loading":"Loading…","space.used_by":"in use: {list}","space.in_use":"A space still uses this plan — detach it first","btn.use":"Use","confirm.delete_plan":'Delete the plan file "{name}" from the server? This cannot be undone.',"toast.plans_list_failed":"Could not list the stored plans: {err}","toast.plan_delete_failed":"Could not delete the plan: {err}","marker.hide":"Hide","marker.hide_tip":'The device will disappear from the plan after saving but will still count toward the room signal. Restore it through "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.","run.automation":"automation","run.script":"script","run.scene":"scene","confirm.tap_run":'Run "{name}"?',"confirm.tap_toggle":'Toggle "{name}"?',"confirm.tap_cover":'Open/close "{name}"?',"toast.run_started":"Started: {name}","toast.run_target_missing":"Run target not found — check the device settings","toast.run_target_required":"Pick an automation, script or scene","btn.run":"Run","vac.section":"Robot vacuum: live position","vac.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"};const Wn={en:jn,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":"Toggle никогда не применяется к замкам и сигнализациям; долгое нажатие всегда открывает инфо-карточку.","import.title":"Создать пространства из этажей HA","import.hint":"Home Assistant уже знает эти этажи. Отметьте, какие превратить в пространства плана — далее для каждого попросим картинку плана. Комнаты затем обводятся вручную по плану.","import.start":"Создать: {n}","import.manual":"Начать с нуля","import.progress":"Этаж {i} из {n}","import.done":"Пространства созданы. Обведите комнаты: кликайте по точкам сетки и замкните контур.","btn.skip":"Пропустить","space.scale_label":"Масштаб (размер клетки сетки)","space.scale_unit":"см на клетку","space.display_section":"Отображение","space.show_borders":"Всегда отображать границы комнат","space.show_names":"Отображать названия комнат (перетаскиваются)","space.room_color":"Цвет границ и названий","space.opacity":"Прозрачность","space.fill_label":"Заливка комнат","fill.none":"Нет","fill.lqi":"По силе зигби-сигнала","fill.light":"По освещению","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":"Загрузка…","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_hint":"пусто = по умолчанию из общих настроек","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены комнаты (1–100 см). Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от 1 до {max} {unit}","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","markup.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.is_light":"Это устройство — источник света","marker.is_light_tip":"Создаёт свечение источника света даже без light-сущности — для умного выключателя с обычными светильниками. Свечение следует за выключателем (или за привязанными выше лампами).","confirm.unlock":"Открыть замок «{name}»?","toast.files_migrate_failed":"Не удалось перенести вложения к новой привязке, ссылки остались на старые файлы: {err}","space.pick_saved":"Уже загруженные","space.pick_saved_hint":"Планы, сохранённые на сервере, включая отцеплённые ранее","space.no_saved":"На сервере пока нет сохранённых планов.","space.loading":"Загрузка…","space.used_by":"используется: {list}","space.in_use":"План используется пространством — сначала отцепите его","btn.use":"Выбрать","confirm.delete_plan":"Удалить файл плана «{name}» с сервера? Действие необратимо.","toast.plans_list_failed":"Не удалось получить список планов: {err}","toast.plan_delete_failed":"Не удалось удалить план: {err}","marker.hide":"Скрыть","marker.hide_tip":"Устройство исчезнет с плана после сохранения, но продолжит участвовать в расчёте сигнала комнаты. Вернуть его можно через кнопку «Скрытые и деактивированные» в редакторе устройств.","marker.show":"Показать","marker.show_tip":"Устройство снова появится на плане после сохранения.","marker.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":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","run.automation":"автоматизация","run.script":"скрипт","run.scene":"сцена","confirm.tap_run":"Запустить «{name}»?","confirm.tap_toggle":"Переключить «{name}»?","confirm.tap_cover":"Открыть/закрыть «{name}»?","toast.run_started":"Запущено: {name}","toast.run_target_missing":"Цель запуска не найдена — проверьте настройки устройства","toast.run_target_required":"Выберите автоматизацию, скрипт или сцену","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.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":"Применить"}};function Bn(t,e){if(e&&e in Wn)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function Gn(t,e,i){return Ri(Wn[t][e]??jn[e]??e,i)}class Vn extends lt{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=Fn(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0}get _lang(){return Bn(this.hass)}_t(t,e){return Gn(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=On(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),r=e.slice(n+1);if("entity"===o){const e=Un(t,r,i,s);return e?[e]:[]}if("device"!==o)return[];const a=i.devices?.[r]||t?.devices?.[r];if(!a)return[];const l=[a.config_entry_id,...Array.isArray(a.config_entries)?a.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const i=s.configEntries[e];return Hn(t,i?.domain,"registry-owner",s,e)});if(l.some(Boolean))return qn(l);const c=[];for(const[e,n]of Object.entries(i.entities||{}))n?.device_id===r&&null==n?.disabled_by&&c.push(Un(t,e,i,s));return c.some(Boolean)?qn(c):qn((Array.isArray(a.identifiers)?a.identifiers:[]).map(e=>{const i=Array.isArray(e)?e[0]:null;return Hn(t,i,"identifier-fallback",s)}))}(this.hass,this.presentation.binding,this.registry,On(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=Un(this.hass,t,this.registry,On(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:zn(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),r=Math.min(1,2.5/Math.max(1,o)),a=Math.round(100*r),l=["dev",...i.classes,"virtual"===i.binding?"virtual":"",i.haDisabled?"ghost ha-disabled":"",null!=i.valueText?"valonly":""].filter(Boolean).join(" "),c=bt(i).join(";"),h=e?this._t("marker.preview.demo_notice"):this._reason(t.explanation.reason),d=[this.deviceName,this._providerSummary(s),this._stateSummary(t),h,t.valueFullText||""].filter(Boolean).join(". ");return j`
+ style=${t.angle?`transform:rotate(${t.angle}deg)`:W}>`} + ${null!=t.tempText&&null==t.valueText?j`${t.tempText}°`:W} + ${null!=t.humText&&null==t.valueText?j`${t.humText}%`:W} + ${null!=t.lqiText?j`${t.lqiText}`:W} + `}const kt=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),$t=[{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 xt(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 St=xt($t),Mt={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"},Ct="mdi:chip";function Dt(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??St)if(t.test(s))return e;return Ct}const Tt=["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 Pt=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,Rt=Math.ceil,zt=Math.floor,At="[BigNumber Error] ",It=At+"Number primitive has more than 15 significant digits: ",Et=1e14,Nt=14,Ft=9007199254740991,Ot=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],Lt=1e7,Ht=1e9;function qt(t){var e=0|t;return t>0||t===e?e:e-1}function Ut(t){for(var e,i,s=1,o=t.length,n=t[0]+"";sc^i?1:-1;for(a=(l=o.length)<(c=n.length)?l:c,r=0;rn[r]^i?1:-1;return l==c?0:l>c^i?1:-1}function Bt(t,e,i,s){if(ti||t!==zt(t))throw Error(At+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Gt(t){var e=t.c.length-1;return qt(t.e/Nt)==e&&t.c[e]%2!=0}function Wt(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Vt(t,e,i){var s,o;if(e<0){for(o=i+".";++e;o+=i);t=o+t}else if(++e>(s=t.length)){for(o=i,e-=s;--e;o+=i);t+=o}else ey?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>y?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!Pt.test(d=String(t)))return o(p,d,c);p.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(a=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(a<0&&(a=l),a+=+d.slice(l+1),d=d.substring(0,l)):a<0&&(a=d.length)}else{if(Bt(e,2,x.length,"Base"),10==e&&S)return P(p=new M(t),_+p.e+1,m);if(d=String(t),c="number"==typeof t){if(0*t!=0)return o(p,d,c,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(It+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,h=d.length;la){a=h;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return o(p,String(t),c,e)}c=!1,(a=(d=s(d,e,10,p.s)).indexOf("."))>-1?d=d.replace(".",""):a=d.length}for(l=0;48===d.charCodeAt(l);l++);for(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(t>Ft||t!==zt(t)))throw Error(It+p.s*t);if((a=a-l-1)>y)p.c=p.e=null;else if(a=f)?Wt(l,r):Vt(l,r,"0");else if(n=(t=P(new M(t),e,i)).e,a=(l=Ut(t.c)).length,1==s||2==s&&(e<=n||n<=g)){for(;ar),l=Vt(l,n,"0"),n+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=n-a)>0)for(n+1==a&&(l+=".");e--;l+="0");return t.s<0&&o?"-"+l:l}function D(t,e){for(var i,s,o=1,n=new M(t[0]);o=10;o/=10,s++);return(i=s+i*Nt-1)>y?t.c=t.e=null:i=10;a/=10,o++);if((n=e-o)<0)n+=Nt,r=e,l=d[c=0],h=zt(l/p[o-r-1]%10);else if((c=Rt((n+1)/Nt))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,o=1,r=(n%=Nt)-Nt+1}else{for(l=a=d[c],o=1;a>=10;a/=10,o++);h=(r=(n%=Nt)-Nt+o)<0?0:zt(l/p[o-r-1]%10)}if(s=s||e<0||null!=d[c+1]||(r<0?l:l%p[o-r-1]),s=i<4?(h||s)&&(0==i||i==(t.s<0?3:2)):h>5||5==h&&(4==i||s||6==i&&(n>0?r>0?l/p[o-r]:0:d[c-1])%10&1||i==(t.s<0?8:7)),e<1||!d[0])return d.length=0,s?(e-=t.e+1,d[0]=p[(Nt-e%Nt)%Nt],t.e=-e||0):d[0]=t.e=0,t;if(0==n?(d.length=c,a=1,c--):(d.length=c+1,a=p[Nt-n],d[c]=r>0?zt(l/p[o-r]%p[r])*a:0),s)for(;;){if(0==c){for(n=1,r=d[0];r>=10;r/=10,n++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);n!=a&&(t.e++,d[0]==Et&&(d[0]=1));break}if(d[c]+=a,d[c]!=Et)break;d[c--]=0,a=1}for(n=d.length;0===d[--n];d.pop());}t.e>y?t.c=t.e=null:t.e=f?Wt(e,i):Vt(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(At+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(Bt(i=t[e],0,Ht,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(Bt(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(Bt(i[0],-Ht,0,e),Bt(i[1],0,Ht,e),g=i[0],f=i[1]):(Bt(i,-Ht,Ht,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)Bt(i[0],-Ht,-1,e),Bt(i[1],1,Ht,e),v=i[0],y=i[1];else{if(Bt(i,-Ht,Ht,e),!i)throw Error(At+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(At+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(At+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(Bt(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(Bt(i=t[e],0,Ht,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(At+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(At+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:k,FORMAT:$,ALPHABET:x}},M.isBigNumber=function(t){if(!t||!0!==t._isBigNumber)return!1;if(!M.DEBUG)return!0;var e,i,s=t.c,o=t.e,n=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===n||-1===n)&&o>=-Ht&&o<=Ht&&o===zt(o)){if(0===s[0]){if(0===o&&1===s.length)return!0;break t}if((e=(o+1)%Nt)<1&&(e+=Nt),String(s[0]).length==e){for(e=0;e=Et||i!==zt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===o&&(null===n||1===n||-1===n))return!0;throw Error(At+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(n=9007199254740992,r=Math.random()*n&2097151?function(){return zt(Math.random()*n)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,o,n,a=0,l=[],c=new M(u);if(null==t?t=_:Bt(t,0,Ht),o=Rt(t/Nt),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(o*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(n%1e14),a+=2);a=o/2}else{if(!crypto.randomBytes)throw b=!1,Error(At+"crypto unavailable");for(e=crypto.randomBytes(o*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(n%1e14),a+=7);a=o/7}if(!b)for(;a=10;n/=10,a++);ai-1&&(null==r[o+1]&&(r[o+1]=0),r[o+1]+=r[o]/i|0,r[o]%=i)}return r.reverse()}return function(s,o,n,r,a){var l,c,h,d,p,u,g,f,v=s.indexOf("."),y=_,b=m;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(o)).pow(s.length-v),k=d,f.c=e(Vt(Ut(u.c),u.e,"0"),10,n,t),f.e=f.c.length),h=d=(g=e(s,o,n,a?(l=x,t):(l=t,x))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--h:(u.c=g,u.e=h,u.s=r,g=(u=i(u,f,y,b,n)).c,p=u.r,h=u.e),v=g[c=h+y+1],d=n/2,p=p||c<0||null!=g[c+1],p=b<4?(null!=v||p)&&(0==b||b==(u.s<0?3:2)):v>d||v==d&&(4==b||p||6==b&&1&g[c-1]||b==(u.s<0?8:7)),c<1||!g[0])s=p?Vt(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=c,p)for(--n;++g[--c]>n;)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=Vt(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,o,n,r,a=0,l=t.length,c=e%Lt,h=e/Lt|0;for(t=t.slice();l--;)a=((o=c*(n=t[l]%Lt)+(s=h*n+(r=t[l]/Lt|0)*c)%Lt*Lt+a)/i|0)+(s/Lt|0)+h*r,t[l]=o%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var o,n;if(i!=s)n=i>s?1:-1;else for(o=n=0;oe[o]?1:-1;break}return n}function i(t,e,i,s){for(var o=0;i--;)t[i]-=o,o=t[i]1;t.splice(0,1));}return function(s,o,n,r,a){var l,c,h,d,p,u,_,m,g,f,v,y,b,w,k,$,x,S=s.s==o.s?1:-1,C=s.c,D=o.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&o.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(g=(m=new M(S)).c=[],S=n+(c=s.e-o.e)+1,a||(a=Et,c=qt(s.e/Nt)-qt(o.e/Nt),S=S/Nt|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,(p=zt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),$=D.length,w=C.length),b=$,v=(f=C.slice(0,$)).length;v<$;f[v++]=0);x=D.slice(),x=[0].concat(x),k=D[0],D[1]>=a/2&&k++;do{if(p=0,(l=e(D,f,$,v))<0){if(y=f[0],$!=v&&(y=y*a+(f[1]||0)),(p=zt(y/k))>1)for(p>=a&&(p=a-1),_=(u=t(D,p,a)).length,v=f.length;1==e(u,f,_,v);)p--,i(u,$<_?x:D,_,a),_=u.length,l=1;else 0==p&&(l=p=1),_=(u=D.slice()).length;if(_=10;S/=10,h++);P(m,n+(m.e=h+c*Nt-1)+1,r,d)}else m.e=c,m.r=+d;return m}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,o=function(t,e,i,s){var o,n=i?e:e.replace(d,"");if(h.test(n))t.s=isNaN(n)?null:n<0?-1:1;else{if(!i&&(n=n.replace(a,function(t,e,i){return o="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=o?t:e}),s&&(o=s,n=n.replace(l,"$1").replace(c,"0.$1")),e!=n))return new M(n,o);if(M.DEBUG)throw Error(At+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},p.absoluteValue=p.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},p.comparedTo=function(t,e){return jt(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,o,n=this;if(null!=t)return Bt(t,0,Ht),null==e?e=m:Bt(e,0,8),P(new M(n),t+n.e+1,e);if(!(i=n.c))return null;if(s=((o=i.length-1)-qt(this.e/Nt))*Nt,o=i[o])for(;o%10==0;o/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,m)},p.dividedToIntegerBy=p.idiv=function(t,e){return i(this,new M(t,e),0,1)},p.exponentiatedBy=p.pow=function(t,e){var i,s,o,n,r,a,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(At+"Exponent not an integer: "+R(t));if(null!=e&&(e=new M(e)),r=t.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!t.c||!t.c[0])return c=new M(Math.pow(+R(h),r?t.s*(2-Gt(t)):+R(t))),e?c.mod(e):c;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&h.isInteger()&&e.isInteger())&&(h=h.mod(e))}else{if(t.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||r&&h.c[1]>=24e7:h.c[0]<8e13||r&&h.c[0]<=9999975e7)))return n=h.s<0&&Gt(t)?-0:0,h.e>-1&&(n=1/n),new M(a?1/n:n);k&&(n=Rt(k/Nt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Gt(t)):l=(o=Math.abs(+R(t)))%2,c=new M(u);;){if(l){if(!(c=c.times(h)).c)break;n?c.c.length>n&&(c.c.length=n):s&&(c=c.mod(e))}if(o){if(0===(o=zt(o/2)))break;l=o%2}else if(P(t=t.times(i),t.e+1,1),t.e>14)l=Gt(t);else{if(0===(o=+R(t)))break;l=o%2}h=h.times(h),n?h.c&&h.c.length>n&&(h.c.length=n):s&&(h=h.mod(e))}return s?c:(a&&(c=u.div(c)),e?c.mod(e):n?P(c,k,m,void 0):c)},p.integerValue=function(t){var e=new M(this);return null==t?t=m:Bt(t,0,8),P(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===jt(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return jt(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=jt(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&qt(this.e/Nt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return jt(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=jt(this,new M(t,e)))||0===e},p.isNaN=function(){return!this.s},p.isNegative=function(){return this.s<0},p.isPositive=function(){return this.s>0},p.isZero=function(){return!!this.c&&0==this.c[0]},p.minus=function(t,e){var i,s,o,n,r=this,a=r.s;if(e=(t=new M(t,e)).s,!a||!e)return new M(NaN);if(a!=e)return t.s=-e,r.plus(t);var l=r.e/Nt,c=t.e/Nt,h=r.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?r:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?r:3==m?-0:0)}if(l=qt(l),c=qt(c),h=h.slice(),a=l-c){for((n=a<0)?(a=-a,o=h):(c=l,o=d),o.reverse(),e=a;e--;o.push(0));o.reverse()}else for(s=(n=(a=h.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;h[i++]=0);for(e=Et-1;s>a;){if(h[--s]=0;){for(i=0,p=y[o]%g,u=y[o]/g|0,n=o+(r=l);n>o;)i=((c=p*(c=v[--r]%g)+(a=u*c+(h=v[r]/g|0)*p)%g*g+_[n]+i)/m|0)+(a/g|0)+u*h,_[n--]=c%m;_[n]=i}return i?++s:_.splice(0,1),T(t,_,s)},p.negated=function(){var t=new M(this);return t.s=-t.s||null,t},p.plus=function(t,e){var i,s=this,o=s.s;if(e=(t=new M(t,e)).s,!o||!e)return new M(NaN);if(o!=e)return t.s=-e,s.minus(t);var n=s.e/Nt,r=t.e/Nt,a=s.c,l=t.c;if(!n||!r){if(!a||!l)return new M(o/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*o)}if(n=qt(n),r=qt(r),a=a.slice(),o=n-r){for(o>0?(r=n,i=l):(o=-o,i=a),i.reverse();o--;i.push(0));i.reverse()}for((o=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=o),o=0;e;)o=(a[--e]=a[e]+l[e]+o)/Et|0,a[e]=Et===a[e]?0:a[e]%Et;return o&&(a=[o].concat(a),++r),T(t,a,r)},p.precision=p.sd=function(t,e){var i,s,o,n=this;if(null!=t&&t!==!!t)return Bt(t,1,Ht),null==e?e=m:Bt(e,0,8),P(new M(n),t,e);if(!(i=n.c))return null;if(s=(o=i.length-1)*Nt+1,o=i[o]){for(;o%10==0;o/=10,s--);for(o=i[0];o>=10;o/=10,s++);}return t&&n.e+1>s&&(s=n.e+1),s},p.shiftedBy=function(t){return Bt(t,-9007199254740991,Ft),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,o,n,r=this,a=r.c,l=r.s,c=r.e,h=_+4,d=new M("0.5");if(1!==l||!a||!a[0])return new M(!l||l<0&&(!a||a[0])?NaN:a?r:1/0);if(0==(l=Math.sqrt(+R(r)))||l==1/0?(((e=Ut(a)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=qt((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(n=s,s=d.times(n.plus(i(r,n,h,1))),Ut(n.c).slice(0,l)===(e=Ut(s.c)).slice(0,l)){if(s.e0&&_>0){for(n=_%a||a,h=u.substr(0,n);n<_;n+=a)h+=c+u.substr(n,a);l>0&&(h+=c+u.slice(n)),p&&(h="-"+h)}s=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,o,n,r,a,l,c,h,d,p,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(At+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+R(l));if(!f)return new M(g);for(e=new M(u),h=s=new M(u),o=c=new M(u),_=Ut(f),r=e.e=_.length-g.e-1,e.c[0]=Ot[(a=r%Nt)<0?Nt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:h:l,a=y,y=1/0,l=new M(_),c.c[0]=0;d=i(l,e,0,1),1!=(n=s.plus(d.times(o))).comparedTo(t);)s=o,o=n,h=c.plus(d.times(n=h)),c=n,e=l.minus(d.times(n=e)),l=n;return n=i(t.minus(s),o,0,1),c=c.plus(n.times(h)),s=s.plus(n.times(o)),c.s=h.s=g.s,p=i(h,o,r*=2,m).minus(g).abs().comparedTo(i(c,s,r,m).minus(g).abs())<1?[h,o]:[c,s],y=a,p},p.toNumber=function(){return+R(this)},p.toPrecision=function(t,e){return null!=t&&Bt(t,1,Ht),C(this,t,e,2)},p.toString=function(t){var e,i=this,o=i.s,n=i.e;return null===n?o?(e="Infinity",o<0&&(e="-"+e)):e="NaN":(null==t?e=n<=g||n>=f?Wt(Ut(i.c),n):Vt(Ut(i.c),n,"0"):10===t&&S?e=Vt(Ut((i=P(new M(i),_+n+1,m)).c),i.e,"0"):(Bt(t,2,x.length,"Base"),e=s(Vt(Ut(i.c),n,"0"),10,t,o,!0)),o<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return R(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),Yt=class{key;left=null;right=null;constructor(t){this.key=t}},Jt=class extends Yt{constructor(t){super(t)}},Zt=class{size=0;modificationCount=0;splayCount=0;splay(t){const e=this.root;if(null==e)return this.compare(t,t),-1;let i=null,s=null,o=null,n=null,r=e;const a=this.compare;let l;for(;;)if(l=a(r.key,t),l>0){let e=r.left;if(null==e)break;if(l=a(e.key,t),l>0&&(r.left=e.right,e.right=r,r=e,e=r.left,null==e))break;null==i?s=r:i.left=r,i=r,r=e}else{if(!(l<0))break;{let e=r.right;if(null==e)break;if(l=a(e.key,t),l<0&&(r.right=e.left,e.left=r,r=e,e=r.right,null==e))break;null==o?n=r:o.right=r,o=r,r=e}}return null!=o&&(o.right=r.left,r.left=n),null!=i&&(i.left=r.right,r.right=s),this.root!==r&&(this.root=r,this.splayCount++),l}splayMin(t){let e=t,i=e.left;for(;null!=i;){const t=i;e.left=t.right,t.right=e,e=t,i=e.left}return e}splayMax(t){let e=t,i=e.right;for(;null!=i;){const t=i;e.right=t.left,t.left=e,e=t,i=e.right}return e}_delete(t){if(null==this.root)return null;if(0!=this.splay(t))return null;let e=this.root;const i=e,s=e.left;if(this.size--,null==s)this.root=e.right;else{const t=e.right;e=this.splayMax(s),e.right=t,this.root=e}return this.modificationCount++,i}addNewRoot(t,e){this.size++,this.modificationCount++;const i=this.root;null!=i?(e<0?(t.left=i,t.right=i.right,i.right=null):(t.right=i,t.left=i.left,i.left=null),this.root=t):this.root=t}_first(){const t=this.root;return null==t?null:(this.root=this.splayMin(t),this.root)}_last(){const t=this.root;return null==t?null:(this.root=this.splayMax(t),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(t){return this.validKey(t)&&0==this.splay(t)}defaultCompare(){return(t,e)=>te?1:0}wrap(){return{getRoot:()=>this.root,setRoot:t=>{this.root=t},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:t=>{this.splayCount=t},splay:t=>this.splay(t),has:t=>this.has(t)}}},Xt=class t extends Zt{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 Jt(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Jt(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 Jt(t.key);return function t(e,i){let s,o;do{if(s=e.left,o=e.right,null!=s){const e=new Jt(s.key);i.left=e,t(s,e)}if(null!=o){const t=new Jt(o.key);i.right=t,e=o,i=t}}while(null!=o)}(t,e),e}toSet(){return this.clone()}entries(){return new ee(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new te(this.wrap())}[Symbol.toStringTag]="[object Set]"},Qt=class{tree;path=new Array;modificationCount=null;splayCount;constructor(t){this.tree=t,this.splayCount=t.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const t=this.path[this.path.length-1];return this.getValue(t)}rebuildPath(t){this.path.splice(0,this.path.length),this.tree.splay(t),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(t){for(;null!=t;)this.path.push(t),t=t.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let t=this.tree.getRoot();for(;null!=t;)this.path.push(t),t=t.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let t=this.path[this.path.length-1],e=t.right;if(null!=e){for(;null!=e;)this.path.push(e),e=e.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===t;)t=this.path.pop();return this.path.length>0}},te=class extends Qt{getValue(t){return t.key}},ee=class extends Qt{getValue(t){return[t.key,t.key]}},ie=t=>()=>t,se=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):ie(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function oe(t){const e=t?(e,i,s,o,n)=>e.exponentiatedBy(2).isLessThanOrEqualTo(o.minus(i).exponentiatedBy(2).plus(n.minus(s).exponentiatedBy(2)).times(t)):ie(!1);return(t,i,s)=>{const o=t.x,n=t.y,r=s.x,a=s.y,l=n.minus(a).times(i.x.minus(r)).minus(o.minus(r).times(i.y.minus(a)));return e(l,o,n,r,a)?0:l.comparedTo(0)}}var ne=t=>t,re=t=>{if(t){const e=new Xt(se(t)),i=new Xt(se(t)),s=(t,e)=>e.addAndReturn(t),o=t=>({x:s(t.x,e),y:s(t.y,i)});return o({x:new Kt(0),y:new Kt(0)}),o}return ne},ae=t=>({set:t=>{le=ae(t)},reset:()=>ae(t),compare:se(t),snap:re(t),orient:oe(t)}),le=ae(),ce=(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),he=(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}}},de=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),pe=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ue=t=>pe(t,t).sqrt(),_e=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return de(o,s).div(ue(o)).div(ue(s))},me=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return pe(o,s).div(ue(o)).div(ue(s))},ge=(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)))},ve=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:Me.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:_e(this.point,t.point,s.point),cosine:me(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:o,cosine:n}=e.get(t),{sine:r,cosine:a}=e.get(s);return o.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?n.isLessThan(a)?1:n.isGreaterThan(a)?-1:0:o.isLessThan(0)&&r.isLessThan(0)?n.isLessThan(a)?-1:n.isGreaterThan(a)?1:0:r.isLessThan(o)?-1:r.isGreaterThan(o)?1:0}}},ye=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,o=e.length;s0&&(t=i)}let e=t.segment.prevInResult(),i=e?e.prevInResult():null;for(;;){if(!e)return null;if(!i)return e.ringOut;if(i.ringOut!==e.ringOut)return i.ringOut?.enclosingRing()!==e.ringOut?e.ringOut:e.ringOut?.enclosingRing();e=i.prevInResult(),i=e?e.prevInResult():null}}},be=class{exteriorRing;interiorRings;constructor(t){this.exteriorRing=t,t.poly=this,this.interiorRings=[]}addInterior(t){this.interiorRings.push(t),t.poly=this}getGeom(){const t=this.exteriorRing.getGeom();if(null===t)return null;const e=[t];for(let t=0,i=this.interiorRings.length;t0?(this.tree.delete(e),i.push(t)):(this.segments.push(e),e.prev=s)}else{if(s&&o){const t=s.getIntersection(o);if(null!==t){if(!s.isAnEndpoint(t)){const e=this._splitSafely(s,t);for(let t=0,s=e.length;t0)return-1;const s=e.comparePoint(t.rightSE.point);return 0!==s?s:-1}if(i.isGreaterThan(s)){if(r.isLessThan(a)&&r.isLessThan(c))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(c))return 1;const i=e.comparePoint(t.leftSE.point);if(0!==i)return i;const s=t.comparePoint(e.rightSE.point);return s<0?1:s>0?-1:1}if(r.isLessThan(a))return-1;if(r.isGreaterThan(a))return 1;if(o.isLessThan(n)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(o.isGreaterThan(n)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!o.eq(n)){const t=l.minus(r),e=o.minus(i),h=c.minus(a),d=n.minus(s);if(t.isGreaterThan(e)&&h.isLessThan(d))return 1;if(t.isLessThan(e)&&h.isGreaterThan(d))return-1}return o.isGreaterThan(n)?1:o.isLessThan(n)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++Se,this.leftSE=t,t.segment=this,t.otherSE=e,this.rightSE=e,e.segment=this,e.otherSE=t,this.rings=i,this.windings=s}static fromRing(e,i,s){let o,n,r;const a=ve.comparePoints(e,i);if(a<0)o=e,n=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);o=i,n=e,r=-1}const l=new ve(o,!0),c=new ve(n,!1);return new t(l,c,[s],[r])}replaceRightSE(t){this.rightSE=t,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const t=this.leftSE.point.y,e=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:t.isLessThan(e)?t:e},ur:{x:this.rightSE.point.x,y:t.isGreaterThan(e)?t:e}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(t){return t.x.eq(this.leftSE.point.x)&&t.y.eq(this.leftSE.point.y)||t.x.eq(this.rightSE.point.x)&&t.y.eq(this.rightSE.point.y)}comparePoint(t){return le.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=he(e,i);if(null===s)return null;const o=this.leftSE.point,n=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=ce(e,r)&&0===this.comparePoint(r),c=ce(i,o)&&0===t.comparePoint(o),h=ce(e,a)&&0===this.comparePoint(a),d=ce(i,n)&&0===t.comparePoint(n);if(c&&l)return d&&!h?n:!d&&h?a:null;if(c)return h&&o.x.eq(a.x)&&o.y.eq(a.y)?null:o;if(l)return d&&n.x.eq(r.x)&&n.y.eq(r.y)?null:r;if(d&&h)return null;if(d)return n;if(h)return a;const p=((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 ge(i,s,t.y);if(s.y.isZero())return ge(t,e,i.y);const o=de(e,s);if(o.isZero())return null;const n={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=de(n,e).div(o),a=de(n,s).div(o),l=t.x.plus(a.times(e.x)),c=i.x.plus(r.times(s.x)),h=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(o,this.vector(),r,t.vector());return null===p?null:ce(s,p)?le.snap(p):null}split(e){const i=[],s=void 0!==e.events,o=new ve(e,!0),n=new ve(e,!1),r=this.rightSE;this.replaceRightSE(n),i.push(n),i.push(o);const a=new t(o,r,this.rings.slice(),this.windings.slice());return ve.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),ve.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(o.checkForConsuming(),n.checkForConsuming()),i}swapEvents(){const t=this.rightSE;this.rightSE=this.leftSE,this.leftSE=t,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let t=0,e=this.windings.length;t0){const t=i;i=s,s=t}if(i.prev===s){const t=i;i=s,s=t}for(let t=0,e=s.rings.length;t1===t.length&&t[0].isSubject;this._isInResult=i(t)!==i(e);break}}return this._isInResult}},Ce=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=le.snap({x:new Kt(t[0][0]),y:new Kt(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let o=s;for(let e=1,i=t.length;exe.run("union",t,e),Re=(t,...e)=>xe.run("intersection",t,e),ze=(t,...e)=>xe.run("difference",t,e);function Ae(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function Ie(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 Ee(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 Ne(t,e,i=1){const s=t[0].toFixed(i),o=t[1].toFixed(i),n=e[0].toFixed(i),r=e[1].toFixed(i),a=s=3?t.poly:t&&null!=t.x&&null!=t.y&&null!=t.w&&null!=t.h?[[t.x,t.y],[t.x+t.w,t.y],[t.x+t.w,t.y+t.h],[t.x,t.y+t.h]]:null}function Oe(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 Le(t){const e=[],i=new Set;for(const s of t||[]){const t=Fe(s);if(t)for(let s=0;s=90?t-=180:t<-90&&(t+=180),s.step&&s.step>0){const i=Math.sqrt(d),n=Math.min(Math.max(s.length||0,0)/2,i/2);let a=Math.round(p*i/s.step)*s.step;Math.abs(p*i-i/2)<=s.step/2&&(a=i/2),a=Math.max(n,Math.min(i-n,a));const l=a/i;o={x:e+l*c,y:r+l*h,angle:t}}else o={x:u[0],y:u[1],angle:t}}}return o}function qe(t,e,i,s,o,n=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,c=n;for(const e of Le(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(a[0]*(e[1]-t[1])-a[1]*(e[0]-t[0]));if(s(i[0])>n||s(i[1])>n)continue;const o=(i[0][0]-t[0])*a[0]+(i[0][1]-t[1])*a[1],r=(i[1][0]-t[0])*a[0]+(i[1][1]-t[1])*a[1],h=Math.min(o,r),d=Math.max(o,r),p=h>0?h:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],g=(h+d)/2;return{wallA:m(h),wallB:m(d),sideA:u,sideB:_,midA:m((h-p)/2),midB:m((p+d)/2),wallCenter:m(g),centered:Math.abs(g)<=o}}function Ue(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 je(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}function Ge(t,e,i){const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;let r=n?((t[0]-e[0])*s+(t[1]-e[1])*o)/n:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*o))}function We(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let o=0;oo&&r<-o||n<-o&&r>o)&&(a>o&&l<-o||a<-o&&l>o)}function Ze(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1;let c=0,h=0,d=0;for(let e=0;e1e-9?[h/(3*c),d/(3*c)]:[(o+n)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!Be([e,i],t))return-1/0;let s=1/0;for(let o=0;om&&(m=c,_=[s,l])}if(_){const[t,i]=_,s=(n-o)/e,l=(a-r)/e;for(let e=-4;e<=4;e++)for(let o=-4;o<=4;o++){const n=t+s*e/4,r=i+l*o/4,a=u(n,r);a>m&&(m=a,_=[n,r])}}return _||Xe(t)||t[0]}function Xe(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(Ke(s,t,e))return s;for(let s=0;sti(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&ti(e,t,i)))}function si(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 ni(t,e,i){for(let s=0;s1&&je(i[0],i[i.length-1],e)&&i.pop(),i}function ai(t,e,i){const[s,o]=t.split(":");return"device"===s?o:"entity"===s?"lg_"+o:e&&e.startsWith("v_")?e:i()}function li(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 hi(t,e,i,s){if(t.length<2)return;const o=e.x+s,n=e.x+e.w-s,r=e.y+s,a=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;st.startsWith("cover."))||null}const wi=["automation","script","scene"];const ki="—",$i="{}";function xi(t){const e=String(t??"").trim();if(!e)return null;let i=e,s="";const o=e.indexOf(":");if(o>=0)i=e.slice(0,o).trim(),s=e.slice(o+1).trim();else{const t=e.split(".");t.length>2&&(i=t.slice(0,2).join("."),s=t.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?o>=0&&!s||s&&!/^[a-zA-Z0-9_.-]+$/.test(s)?null:s?{entity:i,attr:s}:{entity:i}:null}function Si(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),o=xi(s?`${i}:${s}`:i);return o?`{${o.entity}${o.attr?`:${o.attr}`:""}}`:""}function Mi(t,e,i){const s=String(e??"").trim();if(!s)return null;const o=t?.states?.[s];if(!o)return null;const n=String(i??"").trim(),r=t=>t.slice(0,60);if(n){const e=function(t){if(null==t)return null;if(Array.isArray(t)){const e=t.map(t=>null==t?"":String(t)).join(", ");return e?e.slice(0,60):null}if("object"==typeof t)return null;const e=String(t);return""===e?null:e.slice(0,60)}(o.attributes?.[n]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,o,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=o.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function Ci(t,e,i){const s=String(e??"").trim(),o=String(i??"").trim()||s;if(!o)return t.text;const n=t.formatted&&s?function(t,e){if(!e)return t;const i=t.replace(/\s+$/,"");return i.endsWith(e)?i.slice(0,i.length-e.length).replace(/\s+$/,""):t}(t.text,s):t.text;return`${n} ${o}`}function Di(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],o=s?.state;if(!s||null==o||""===o||"unavailable"===o||"unknown"===o)return ki;const n=(e?.attr||"").trim(),r=Mi(t,i,n||null);if(null===r)return ki;return Ci(r,n?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}const Ti=20;function Pi(t){const e=Number(t?.scale);if(Number.isFinite(e)&&e>0)return Math.min(20,Math.max(.15,e));return{s:.7,m:1,l:1.5}[String(t?.size??"")]??1}function Ri(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 zi="#55606c",Ai=.55,Ii={c:"#607d8b",a:.18};function Ei(t,e=Ii){const i=t&&"object"==typeof t?t:null,s=i?.a;return{c:_t(i?.c,e.c),a:"number"==typeof s&&Number.isFinite(s)?Math.min(1,Math.max(0,s)):e.a}}function Ni(t,e){const i=Ei(t),s=e?.settings?.custom_fill;return s&&"object"==typeof s?Ei(s,i):i}function Fi(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:_t(e.room_color,zi),opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):Ai,fill:["lqi","light","temp","custom"].includes(e.fill_mode)?e.fill_mode:"none",customFill:Ei(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:_t(e.bg_color,null),hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function Oi(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return _t(i,"")}const Li={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 Hi(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(Li)){const s=Li[t],o=i[t];e[t]={c:_t(o?.c,s.c),a:o&&"number"==typeof o.a?Math.min(1,Math.max(0,o.a)):s.a}}return e}function qi(t,e,i){const s=Math.min(1,Math.max(0,i)),o=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),n=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=o.map((t,e)=>Math.round(t+(n[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function Ui(t,e,i,s,o,n,r,a=Ii){const l=function(t,e,i,s,o,n,r,a=Ii){if("custom"===t)return Ei(a);if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:qi(r.lqi_low.c,r.lqi_high.c,t),a:r.lqi_low.a+(r.lqi_high.a-r.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?r.light_none.a>0?r.light_none:null:"on"===i?r.light_on:r.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(o,n),e=Math.max(o,n);return se?r.temp_hot:r.temp_ok}return null}(t,e,i,s,o,n,r,a);return l?{color:l.c,opacity:l.a,mode:t}:null}const ji={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"]},Bi=[["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 Gi(t){for(const e of[...Object.values(ji),...Bi])if(t===e[0]||t===e[1])return e;return null}function Wi(t,e,i,s,o){if(!s||"unavailable"===s||"unknown"===s)return t;if(o){const i="cover"===e?Gi(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=ji[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const o=Gi(t);return o?"closed"===s?o[0]:o[1]:t}return"lock"===e?"locked"===s?"mdi:lock":"mdi:lock-open-variant":"light"===e&&"mdi:lightbulb"===t&&"on"===s?"mdi:lightbulb-on":t}function Vi(t){const e=Math.min(4e4,Math.max(1e3,t))/100,i=e<=66?255:329.698727446*Math.pow(e-60,-.1332047592),s=e<=66?99.4708025861*Math.log(e)-161.1195681661:288.1221695283*Math.pow(e-60,-.0755148492),o=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,n=t=>Math.round(Math.min(255,Math.max(0,t)));return[n(i),n(s),n(o)]}const Ki=1/2.2;function Yi(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 Ji(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=_t(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 Zi(t,e,i){const s=Ji(e),o=t?.attributes||{},n=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:n};const r=ft(o.rgb_color);if(r)return{c:Yi(r)||_t(i,"#ffd9a0"),bri:n};const a=Number(o.color_temp_kelvin)||(Number(o.color_temp)>0?1e6/Number(o.color_temp):NaN);return Number.isFinite(a)&&a>0?{c:(l=Vi(a),"#"+l.slice(0,3).map(t=>Math.min(255,Math.max(0,Math.round(Number(t)||0))).toString(16).padStart(2,"0")).join("")),bri:n}:{c:_t(i,"#ffd9a0"),bri:n};var l}function Xi(t,e,i){return"on"===t?.state?Zi(t,e,i):null}function Qi(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,Ki));return Math.max(0,Math.min(1,s))}function ts(t,e,i,s,o=170,n=0){const r=Math.hypot(e[0]-t[0],e[1]-t[1]),a=Math.hypot(i[0]-t[0],i[1]-t[1]);if(r<1e-6||a<1e-6||Math.min(r,a)>=s)return null;let l,c;if(Number.isFinite(n)&&n>1e-6){const s=i[0]-e[0],o=i[1]-e[1],r=Math.hypot(s,o);if(r<1e-6)return null;const a=-(o/r),h=s/r,d=(e[0]+i[0])/2,p=(e[1]+i[1])/2,u=(t[0]-d)*a+(t[1]-p)*h>=0?1:-1,_=n/2,m=[e[0]+a*_*u,e[1]+h*_*u],g=[i[0]+a*_*u,i[1]+h*_*u],f=[e[0]-a*_*u,e[1]-h*_*u],v=[i[0]-a*_*u,i[1]-h*_*u],y=Math.atan2(p-t[1],d-t[0]),b=e=>{let i=Math.atan2(e[1]-t[1],e[0]-t[0]);for(;i-y>Math.PI;)i-=2*Math.PI;for(;i-y<-Math.PI;)i+=2*Math.PI;return i},w=b(m),k=b(g),$=b(f),x=b(v),S=Math.max(Math.min(w,k),Math.min($,x)),M=Math.min(Math.max(w,k),Math.max($,x));if(!(M-S>1e-9))return null;l=S,c=M-S}else{for(l=Math.atan2(e[1]-t[1],e[0]-t[0]),c=Math.atan2(i[1]-t[1],i[0]-t[0])-l;c>Math.PI;)c-=2*Math.PI;for(;c<-Math.PI;)c+=2*Math.PI}const h=o*Math.PI/180;if(Math.abs(c)>h){const t=l+c/2;c=h*Math.sign(c),l=t-c/2}const d=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=l+c*e/8;d.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return d}function es(t,e,i,s,o){const n=e*Math.PI/180,r=[-Math.sin(n),Math.cos(n)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*o*a,t[1]+r[1]*o*a];return s.some(t=>Ke(l,t,1e-9))}function is(t){return t.startsWith("light.")||t.startsWith("switch.")}function ss(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let o=0;op||l>p)continue;const u=(o[0]-n[0])*h+(o[1]-n[1])*d,_=(r[0]-n[0])*h+(r[1]-n[1])*d,m=Math.max(0,Math.min(u,_)),g=Math.min(c,Math.max(u,_));g-m>i&&s.push([n[0]+h*m,n[1]+d*m,n[0]+h*g,n[1]+d*g])}}return s}function os(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let o=!0;for(;o;){o=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const n of e)if(n.id&&i.has(n.id)&&s(t,n)){i.add(t.id),o=!0;break}}return i}function ns(t,e,i=1e-6){const s=[];for(const o of t){const t=[o[0],o[1]],n=[o[2],o[3]],r=n[0]-t[0],a=n[1]-t[1],l=Math.hypot(r,a);if(ln||o>n)continue;const r=(s[0]-t[0])*c+(s[1]-t[1])*h,a=(s[2]-t[0])*c+(s[3]-t[1])*h,p=Math.max(0,Math.min(r,a)),u=Math.min(l,Math.max(r,a));u-p>i&&d.push([p,u])}if(!d.length){s.push([t[0],t[1],n[0],n[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,o]of d)e-p>i&&s.push([t[0]+c*p,t[1]+h*p,t[0]+c*e,t[1]+h*e]),p=Math.max(p,o);l-p>i&&s.push([t[0]+c*p,t[1]+h*p,n[0],n[1]])}return s}function rs(t,e,i=1e-6){const s=[];for(let e=0;e{if("string"!=typeof t||!t)return;const i=hs(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 hs(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 ds(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i||"custom"===i?i:t}function ps(t,e){const i=e?.settings;return"boolean"==typeof i?.glow?i.glow:"glow"===i?.fill_mode||t}function us(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function _s(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function ms(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function gs(t,e){const i=e[2]-e[0],s=e[3]-e[1],o=i*i+s*s;if(!o)return Math.hypot(t[0]-e[0],t[1]-e[1]);let n=((t[0]-e[0])*i+(t[1]-e[1])*s)/o;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+n*i),t[1]-(e[1]+n*s))}const fs=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);function vs(t,e){return"alarm_control_panel"===t||"siren"===t||"binary_sensor"===t&&!!e&&fs.has(e)}function ys(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 bs={availability:"available",status:"alarm",activity:"none"},ws=new Set(["motion","vibration","sound"]),ks=new Set(["occupancy","presence"]),$s=new Set(["door","window","garage_door","opening"]),xs=new Set(["running","power"]),Ss=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Ms=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),Cs=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping"]),Ds=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),Ts=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,Ps=t=>String(t??"").trim().toLowerCase();function Rs(t,e){if(!e.startsWith("switch."))return!1;const i=t?.entities?.[e]||{},s=t?.states?.[e],o=e.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(o))return!0;if([i.translation_key,i.original_name,i.name].map(Ps).some(t=>["power","main power","power switch","питание"].includes(t)))return!0;const n=Ps(s?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(n)||/(?:^|[\s._-])питание$/.test(n)}function zs(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=Ps(t?.[e]);if(Ms.has(i)||Cs.has(i))return i}return""}function As(t,e){const i=t?.states?.[e],s=i?Ps(i.state):"__missing__",o=String(e||"").split(".")[0],n=Ps(i?.attributes?.device_class),r={eid:e,state:s,availability:Ts(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===r.availability)return r;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&vs(t,e)}(o,n,s))return{...r,status:"alarm"};if("binary_sensor"===o)return ws.has(n)?{...r,edge:"rising"}:ks.has(n)?{...r,activity:"on"===s?"presence":"none"}:$s.has(n)?{...r,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===n?{...r,activity:"on"===s?"transition":"none"}:xs.has(n)&&"on"===s?{...r,status:"working",activity:"running"}:r;if("cover"===o)return{...r,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===o)return{...r,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===o)return{...r,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===o){const t=zs(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(Ps):[],o=!Cs.has(s)&&(Ds.has(s)||Ms.has(s)||e.includes(s));return(t?Ms.has(t):o)?{...r,status:"working",activity:"running"}:r}if(["light","switch","fan","humidifier"].includes(o))return"on"===s?{...r,status:"working",activity:"running"}:r;if("media_player"===o)return"off"===s?{...r,availability:"unavailable"}:r;if("vacuum"===o)return"cleaning"===s?{...r,status:"working",activity:"running"}:"returning"===s?{...r,status:"working",activity:"transition"}:r;if("script"===o)return"on"===s?{...r,status:"working",activity:"running"}:r;if("automation"===o)return r;if("button"===o||"event"===o)return{...r,edge:"change"};const a=zs(i.attributes);return Ms.has(a)||Ms.has(s)&&!Cs.has(s)?{...r,status:"working",activity:"running"}:r}function Is(t,e){if(!(t=>!!t&&!Ts(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 Es=new WeakMap,Ns=new WeakMap;let Fs=1;const Os="houseplan.ha-binding-status.v1";let Ls=null;function Hs(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=Es.get(e);return i||(i={revision:0,authoritative:!1,access:"pending",devices:{},entities:{},lastSuccess:0,listeners:new Set,refs:0},Es.set(e,i)),i}function qs(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 Us(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"})]),o=qs(i,"id"),n=qs(s,"entity_id");if(!o||!n)throw new Error("invalid_registry_response");e.devices=o,e.entities=n,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 js(t,e){void 0===e.reloadTimer&&(e.reloadTimer=globalThis.setTimeout(()=>{e.reloadTimer=void 0,Us(t,e)},80))}async function Bs(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,()=>js(t,e),"device_registry_updated")),e.unsubEntity||(e.unsubEntity=await i.call(t.connection,()=>js(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 Gs(t,e){const i=Hs(t);if(!i)return()=>{};const s=0===i.refs;i.refs++,i.listeners.add(e),("pending"===i.access||s)&&Us(t,i),Bs(t,i);let o=!1;return()=>{o||(o=!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 Ws(t){const e=Hs(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 o=e.liveDevices!==i||e.liveEntities!==s,n=void 0!==e.liveDevices||void 0!==e.liveEntities;if(o&&(e.liveDevices=i,e.liveEntities=s,e.projectedRevision=void 0,n&&js(t,e)),e.projectedRevision!==e.revision||!e.projectedDevices||!e.projectedEntities){const t={...e.devices},o={...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(o,t)||(o[t]=e);e.projectedDevices=t,e.projectedEntities=o,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 Vs(t){if(!t||"object"!=typeof t&&"function"!=typeof t)return 0;let e=Ns.get(t);return e||(e=Fs++,Ns.set(t,e)),e}function Ks(t,e=Ws(t)){return[e.revision,e.access,Vs(e.devices),Vs(e.entities)].join(":")}function Ys(t){return!!t&&null==t.disabled_by}function Js(t,e=Ws(t)){const i={},s={},o={};for(const[t,s]of Object.entries(e.devices||{}))Ys(s)&&(i[t]=s);for(const[t,i]of Object.entries(e.entities||{})){if(!Ys(i))continue;const o=i.device_id?e.devices?.[i.device_id]:null;e.authoritative&&i.device_id&&!o||(o&&!Ys(o)||(s[t]=i))}for(const[i,s]of Object.entries(t?.states||{})){const t=e.entities?.[i];if(e.authoritative&&!t)continue;if(t&&!Ys(t))continue;const n=t?.device_id?e.devices?.[t.device_id]:null;e.authoritative&&t?.device_id&&!n||(n&&!Ys(n)||(o[i]=s))}return{...t,devices:i,entities:s,states:o}}function Zs(t,e=Ws(t)){return{...t,devices:e.devices||{},entities:e.entities||{}}}function Xs(t,e){const i=[];for(const[s,o]of Object.entries(t||{}))o?.device_id===e&&i.push(s);return i}function Qs(){if(Ls)return Ls;if("undefined"==typeof localStorage)return{};try{const t=JSON.parse(localStorage.getItem(Os)||"{}");if(!t||"object"!=typeof t||Array.isArray(t))return{};const e=Date.now(),i={};for(const[s,o]of Object.entries(t))!o||"active"!==o.kind&&"ha_disabled"!==o.kind||!Number.isFinite(o.ts)||e-o.ts>7776e6||(i[s]=o);return Ls=i,Ls}catch{return{}}}function to(t,e,i=Ws(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 o=e.slice(0,s),n=e.slice(s+1);if("device"!==o&&"entity"!==o||!n)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const r=i.devices||{},a=i.entities||{};if(i.authoritative){if("device"===o){const t=r[n];if(!t)return{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[]};const e=Xs(a,n);if(!Ys(t))return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:e};const i=e.filter(t=>Ys(a[t]));return e.length&&!i.length?{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:e}:{kind:"active",enabledEntityIds:i,allEntityIds:e}}const e=a[n];return e&&!Ys(e)?{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[n]}:e?.device_id&&!r[e.device_id]?{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[n]}:e?.device_id&&!Ys(r[e.device_id])?{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[n]}:e||t?.states?.[n]?{kind:"active",enabledEntityIds:[n],allEntityIds:[n]}:{kind:"orphaned",reason:"entity_missing",enabledEntityIds:[],allEntityIds:[]}}const l=function(t){return Qs()[t]||null}(e);if("device"===o){const e=r[n],i=Xs(a,n);if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:i};const s=i.filter(t=>{const e=a[t];return null==e?.disabled_by&&(!e.device_id||null==r[e.device_id]?.disabled_by)});if(i.length&&!s.length&&i.every(t=>null!=a[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=a[n];if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[n]};if(e?.device_id&&null!=r[e.device_id]?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[n]};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"entity",enabledEntityIds:[],allEntityIds:[n]};if(e||t?.states?.[n])return{kind:"active",enabledEntityIds:[n],allEntityIds:[n]}}return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]}}function eo(t){const e={};for(const[i,s]of Object.entries(t.entities)){if(!s?.device_id||!Ys(s))continue;const o=t.devices?.[s.device_id];o&&!Ys(o)||(e[s.device_id]=e[s.device_id]||[]).push(i)}return e}function io(t,e,i){if(e.identifiers?.[0]?.[0])return e.identifiers[0][0];for(const e of i){const i=t.entities[e]?.platform;if(i)return i}return""}function so(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 oo=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],no=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function ro(t,e){const i=e.map(e=>({eid:e,reg:t?.entities?.[e]})).filter(t=>!!t.reg);if(!i.length)return[];const s=i.filter(t=>!t.reg.entity_category),o=s.length?s:i;for(const t of oo){const e=o.filter(e=>e.eid.startsWith(t+"."));if(e.length)return no(e).map(t=>t.eid)}const n=o.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=Ps(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return ws.has(i)||ks.has(i)||$s.has(i)||xs.has(i)||"moving"===i||Ss.has(i)}(t,e.eid));if(n.length)return no(n).map(t=>t.eid);const r=o.filter(t=>t.eid.startsWith("switch."));if(r.length){const e=no(r);return[(e.find(e=>Rs(t,e.eid))||e[0]).eid]}const a=[];for(const t of Tt)a.push(...no(o.filter(e=>e.eid.startsWith(t+"."))));return a.push(...no(o.filter(t=>!Tt.includes(t.eid.split(".")[0])))),a.map(t=>t.eid)}function ao(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),o=[s.filter(t=>!t.reg.hidden&&!t.reg.entity_category),s.filter(t=>!t.reg.entity_category),s.filter(t=>!t.reg.hidden),s];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const e of o){const i=e.find(e=>so(t,e.eid));if(i)return i.eid}return ro(t,e)[0]}function lo(t,e,i=[]){return[...new Set(co(t,e,i))].filter(t=>is(t))}function co(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 ho(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 po(t,e){if(!1===e.marker?.is_light)return[];if(!0===e.marker?.is_light){const i=function(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(is),s=t.primary&&is(t.primary)?t.primary:null;return(e&&is(e)?e:null)||s||i[0]||null}(e);return i&&t.states?.[i]?[{eid:i,via:"forced"}]:[]}const i=e.primary||ro(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"}))}function uo(t,e){const i=lo(e.marker?.binding,e.controls??e.marker?.controls,e.entities);return(null!=e.marker?.is_light||!i.length)&&po(t,e).length>0}function _o(t,e){const i=lo(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"})),s=uo(t,e)?po(t,e):[];for(const t of s)i.some(e=>e.eid===t.eid)||i.push(t);return i}function mo(t,e,i){const s=[],o=new Map;for(const n of e){if(n.hidden||null!=i&&!ho(n,i))continue;const e=_o(t,n);for(const{eid:i,via:r}of e){if(!i)continue;const e="controls"!==r,a={eid:i,device:n,via:r,castsGlow:e,on:"on"===t.states[i]?.state},l=o.get(i);null==l?(o.set(i,s.length),s.push(a)):e&&!s[l].castsGlow&&(s[l]=a)}}return s}function go(t){const e=t.filter(t=>t.castsGlow);return e.find(t=>t.on)||e[0]||null}function fo(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function vo(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const o=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===o){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const n=e.attributes?.linkquality??e.attributes?.lqi;if(null!=n){const t=parseFloat(n);isNaN(t)||i.push(t)}}return li(i)}function yo(t,e){for(const i of e){if(!so(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 bo(t,e){for(const i of e){if(!i.startsWith("climate."))continue;const e=t.states[i];if(!e||"unavailable"===e.state||"unknown"===e.state)continue;const s=parseFloat(e.attributes?.current_temperature);if(Number.isFinite(s))return Math.round(10*s)/10}return null}function wo(t,e){if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_humidity$/.test(e);const s=i.attributes||{};return"humidity"===s.device_class||"%"===s.unit_of_measurement&&/_humidity$/.test(e)||/_humidity$/.test(e)}function ko(t,e){for(const i of e){if(!wo(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(s)}return null}function $o(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden||!Ys(s))continue;let o=null;if("group"===s.platform)o=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if(!Ys(e))continue;if("Group"!==e?.model)continue;o=e.area_id||s.area_id||null}}if(!o)continue;const n=t.states[e];i.push({eid:e,name:s.name||n?.attributes?.friendly_name||e,area:o})}return i}function xo(t,e,i,s,o){const n=Dt(e,i,o);if(n!==Ct)return n;const r=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&r.push(i)}return function(t){for(const e of t){const t=Mt[e];if(t)return t}return null}(r)??Ct}function So(t){const e=new Set,i=new Set;for(const s of t||[]){if(!0!==s?.removed)continue;const t=String(s.binding||"").indexOf(":");if(t<1)continue;const o=s.binding.slice(0,t),n=s.binding.slice(t+1);n&&("device"===o?e.add(n):"entity"===o&&i.add(n))}return{devices:e,entities:i}}function Mo(t,e,i){if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function Co(t,e,i,s,o){const n=lo(e.binding,e.controls,t.entities).filter(t=>!Mo(i,t,s)).filter(t=>"active"===to(i,"entity:"+t,o).kind);t.marker=e,t.controls=n,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 Do(t){const e=t.hass,i=t.registry||Ws(e),s=Js(e,i),o=Zs(e,i),{areaToSpace:n,markers:r,settings:a,excluded:l,showAll:c,firstSpaceId:h,loc:d,iconRules:p}=t,u=!1!==a.group_lights,_=So(r),m=$o(s,u).filter(t=>!Mo(s,t.eid,_)),g=new Set(m.map(t=>t.area)),f=eo(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}(o),y=new Set;for(const t of r){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||y.add(t.binding)}const b=(t,e)=>r.find(i=>i.binding===t+":"+e),w={},k=[];for(const t of Object.values(s.devices)){const o=t.area_id;if(!o||!n[o])continue;if("service"===t.entry_type)continue;if(y.has("device:"+t.id))continue;const r=to(e,"device:"+t.id,i);if("active"!==r.kind)continue;const h=b("device",t.id);if(h&&h.hidden&&!a.filter_seeded)continue;const _=f[t.id]||[],m=io(s,t,_),v=!a.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=$+"|"+o;let S=xo(s,$,t.model,_,p);if(_.some(t=>t.startsWith("lock."))&&(S="mdi:lock"),v&&!c&&u&&"mdi:lightbulb"===S&&g.has(o))continue;w[x]=(w[x]||0)+1;const M=w[x]>1?$+" "+w[x]:$,C={id:t.id,name:M,model:t.model||"",area:o,space:n[o],icon:S,entities:_,allEntities:r.allEntityIds,bindingStatus:r,bindingKind:"device",bindingRef:t.id,pdfs:[]};C.primary=ao(s,_,S),"mdi:thermometer"!==S&&"mdi:air-filter"!==S||(C.temp=yo(s,_)),C.primary&&wo(s,C.primary)&&(C.hum=ko(s,_)),k.push(C)}for(const t of m)n[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:n[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 r){if(t.removed)continue;const[r,l]=t.binding.split(":"),c="device"===r||"entity"===r?to(e,t.binding,i):null;if(!t.hidden||a.filter_seeded||"ha_disabled"===c?.kind)if("device"===r){const e=c;if("unverified"===e.kind)continue;const r=o.devices[l],a=t.area||r?.area_id||"",u=a&&n[a]||t.space||h,m="active"===e.kind?e.enabledEntityIds:[];let g=r?xo(s,r.name_by_user||r.name||"",r.model,m,p):"mdi:help-circle";m.some(t=>t.startsWith("lock."))&&(g="mdi:lock");const f={id:t.id,name:r?.name_by_user||r?.name||d("device.fallback"),model:r?.model||"",area:a,space:u,icon:g,entities:m,allEntities:e.allEntityIds.length?e.allEntityIds:r&&v[r.id]||[],bindingStatus:e,bindingKind:"device",bindingRef:l};f.primary=ao(s,m,g),"mdi:thermometer"!==g&&"mdi:air-filter"!==g||(f.temp=yo(s,m)),f.primary&&wo(s,f.primary)&&(f.hum=ko(s,m)),Co(f,t,o,_,i),k.push(f)}else if("entity"===r){if(Mo(o,l,_))continue;const e=c;if("unverified"===e.kind)continue;const r=o.entities[l],a=t.area||r?.area_id||r?.device_id&&o.devices[r.device_id]?.area_id||"",d=a&&n[a]||t.space||h,u=s.states[l],m=r?.name||u?.attributes?.friendly_name||l;let g=xo(s,m,"",[l],p);l.startsWith("lock.")&&(g="mdi:lock");const f={id:t.id,name:m,model:"",area:a,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=yo(s,f.entities)),f.entities.length&&wo(s,l)&&(f.hum=ko(s,f.entities)),Co(f,t,o,_,i),k.push(f)}else{const e=t.area||"",s=t.space||e&&n[e]||h,r={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};Co(r,t,o,_,i),k.push(r)}}return k}function To(t,e,i,s){if(!e)return null;if(function(t,e,i){if(!e)return!1;const s=e.indexOf(":");if(s<1)return!1;const o=e.slice(0,s),n=e.slice(s+1),r=So(i);return"device"===o?r.devices.has(n):"entity"===o&&Mo(t,n,r)}(t,e,s))return null;const o=e.indexOf(":");if(o<0)return null;const n=e.slice(0,o),r=e.slice(o+1);if(!r)return null;if("entity"===n){const e=parseFloat(t.states[r]?.state);return Number.isFinite(e)?"temp"===i?Math.round(10*e)/10:Math.round(e):null}if("device"===n){const e=Object.entries(t.entities).filter(([,t])=>t.device_id===r).map(([t])=>t);return"temp"===i?yo(t,e):ko(t,e)}return null}const Po=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 Ro(t,e,i){const s=[];for(const o of e){if(o.area!==i||o.virtual)continue;const e=vo(t,o.entities);null!=e&&s.push(e)}return li(s)}function zo(t,e,i){const s=[];for(const o of e){if(o.area!==i)continue;if("mdi:thermometer"!==o.icon&&"mdi:air-filter"!==o.icon)continue;const e=yo(t,o.entities);null!=e&&s.push(e)}return s.length?Math.round(s.reduce((t,e)=>t+e,0)/s.length*10)/10:null}const Ao={sourceKind:"none",visualSources:[],criticalSources:[],samples:[]};function Io(t,e){const i=t?.entities?.[e],s=t?.states?.[e];return String(i?.name||i?.original_name||s?.attributes?.friendly_name||e)}function Eo(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 No(t,e,i,s){return{eid:e,role:i,name:Io(t,e),state:String(t?.states?.[e]?.state??""),stateText:Eo(t,e),integrationDomain:t?.entities?.[e]?.platform||null,sample:s||As(t,e)}}function Fo(t,e){const i=e.hidden&&e.userHidden?{...e,hidden:!1}:e;let s="none",o=[];const n="cover"===i.tapAction?bi(i.entities):null,r=mo(t,[i]);if(n)s="cover",o=[No(t,n,"cover")];else if(r.length)s=r.some(t=>"controls"===t.via)?"controls":"light",o=r.map(e=>No(t,e.eid,"controls"===e.via?"control":"forced"===e.via?"forced_light":"light"));else{const e=ro(t,i.entities);if(e.length){s="device_role";const n=function(t,e,i){const s=e.map(e=>As(t,e)),o=i.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category);return 1===e.length&&o.length>1&&Rs(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,e,i.entities);o=e.map((e,i)=>No(t,e,"device_role",n[i]))}else i.primary&&(s="primary",o=[No(t,i.primary,"primary")])}const a=[];for(const e of i.entities||[]){const i=As(t,e);"alarm"!==i.status||o.some(t=>t.eid===e)||a.push(No(t,e,"critical",i))}return{sourceKind:s,visualSources:o,criticalSources:a,samples:[...o,...a].map(t=>t.sample)}}function Oo(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(!so(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 o=function(t,e){if(!e.primary||!wo(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(o)return{source:{kind:"humidity",eid:o.eid,text:o.text},text:o.text,fallback:null}}if(1!==i.visualSources.length)return{source:null,text:null,fallback:i.visualSources.length?"value_ambiguous_sources":"value_no_state"};const o=i.visualSources[0].eid,n=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 o=String(s).trim().toLowerCase();if("unknown"===o||"unavailable"===o)return{text:"",fallback:"value_no_state"};const n=Mi(t,e);return n?{text:Ci(n,String(i.attributes?.unit_of_measurement||"")),fallback:null}:{text:"",fallback:"value_no_state"}}(t,o);return n.fallback?{source:null,text:null,fallback:n.fallback}:{source:{kind:"entity",eid:o,text:n.text},text:n.text,fallback:null}}function Lo(t,e,i){const s=t.marker?.binding||(t.bindingKind&&t.bindingRef?`${t.bindingKind}:${t.bindingRef}`:t.virtual?"virtual":""),o=[...e.visualSources.map(t=>`${t.role}:${t.eid}`),...e.criticalSources.map(t=>`critical:${t.eid}`)].sort(),n=i?`${i.kind}:${i.eid}:${i.attribute||""}`:"none";return[s,e.sourceKind,...o,`value:${n}`].join("\n")}function Ho(t,e,i=!0,s){const o=s||Fo(t,e);return Lo(e,o,Oo(t,e,o,i).source)}function qo(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 Uo(t,e,i){const s=ui(e.marker?.display),o="static_icon"===s,n=o&&!1===i.sourceDetails?Ao:Fo(t,e),r=e.bindingStatus,a="ha_disabled"===r?.kind,l="orphaned"===r?.kind,c=!0===e.userHidden||!0===e.marker?.hidden,h=a||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 bs;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}}(n.samples);let p=d;h||o?p={availability:"available",status:"neutral",activity:"none"}:"alarm"===d.status||i.liveStates||(p={availability:"available",status:"neutral",activity:"none"});const u=o&&!1===i.sourceDetails?{source:null,text:null,fallback:null}:Oo(t,e,n,i.showTemperature),_=Lo(e,n,u.source),m=i.activityRuntime,g=i.now??Date.now();!o&&!h&&i.liveStates&&"alarm"!==d.status&&m?.sources===_&&m.flashTs&&m.flashKind&&g-m.flashTs<3300&&(p={...p,activity:m.flashKind});const f="icon_ripple"===s&&!h&&i.liveStates&&"alarm"!==p.status?p.activity:"none",v=o||h||!i.showTemperature?null:!0===e.marker?.use_climate_temp?bo(t,e.entities):"mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon?yo(t,e.entities):null,y=!o&&!h&&i.showTemperature&&e.primary&&wo(t,e.primary)?ko(t,e.entities):null,b=o||h||!i.showSignal||e.virtual?null:vo(t,e.entities),w="cover"===n.sourceKind?n.visualSources[0]?.eid:e.primary||n.visualSources[0]?.eid,k=w?t?.states?.[w]:null,$=!i.liveStates||o||h?e.icon:Wi(e.icon,w?.split(".")[0],k?.attributes?.device_class,k?.state,!!e.marker?.icon),x=!i.liveStates||o||h?null:mo(t,[{...e,hidden:!1}]).map(e=>function(t){return t&&"on"===t.state?ft(t.attributes?.rgb_color):null}(t?.states?.[e.eid])).find(t=>!!t)||null,S=Number(e.marker?.size)>0?Number(e.marker.size):1,M=Number(e.marker?.angle)||0,C=Number(e.marker?.ripple_size)>0?Number(e.marker.ripple_size):3,D=_t(e.marker?.ripple_color,null),T=o?null:D||x||null,P="value"!==s||h?null:u.text,R="ha_disabled"===r?.kind?r.reason:null,z=function(t,e,i,s,o,n,r,a){return n?"ha_disabled":r?"orphaned":"static_icon"===a?"static_icon":"alarm"===e.status?"alarm":o?"unavailable"===e.availability?"unavailable":"cover"===s?"cover_icon_state":"presence"===i?"presence":"event"===i?"event":"transition"===i?"transition":"working"===e.status?"icon_ripple"===a&&"none"!==i?"working_activity":"working":"open"===e.status?"open":(t.primary||"").startsWith("media_player.")?"media_neutral":"neutral":"live_states_disabled"}(e,p,f,n.sourceKind,i.liveStates,a,l,s),A=[];i.designPreview&&c&&A.push("hidden_design_preview"),o||!0!==e.marker?.vacuum?.live||A.push("vacuum_live_plan_only");const I=1===n.visualSources.length&&Rs(t,n.visualSources[0].eid),E=e.entities.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category).length;I&&E>1&&A.push("composite_power_source"),"icon_ripple"!==s&&"static_icon"!==s&&"alarm"!==d.status&&"none"!==d.activity&&A.push("activity_display_disabled");const N={binding:e.marker?.binding||(e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:e.virtual?"virtual":""),sourceKind:n.sourceKind,visualSources:n.visualSources,criticalSources:n.criticalSources,valueSource:u.source,sourceSignature:_,visual:p,display:s,icon:$,valueText:P,valueFullText:P,fallbackReason:"value"!==s||P?null:u.fallback,activity:f,activityGeneration:m?.gen||1,classes:[],tempText:null==v?null:String(v),humText:null==y?null:String(y),lqiText:null==b?null:String(b),lqiColor:null==b?null:Ae(b),lightColor:x,scale:S,angle:M,rippleScale:C,rippleColor:T,userHidden:c,effectiveHidden:h,haDisabled:a,disabledReason:R,orphaned:l,vacuumLive:!o&&!0===e.marker?.vacuum?.live,explanation:{reason:z,notices:A}};return{...N,classes:qo(N)}}const jo=new WeakMap;function Bo(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=jo.get(e);return i||(i={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},jo.set(e,i)),i}function Go(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function Wo(t,e){const i=Bo(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,Go(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Go(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 o={...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 n=t?.type||t?.action||t?.data?.action;"removed"===n||"remove"===n?delete o[i]:o[i]=e,s=!0}s&&(e.configEntries=o,Go(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 Vo(t){const e=Bo(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function Ko(t,e,i=Vo(t),s){const o=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(o?.name||"")||String(s||"")||e.replace(/_/g," ")}function Yo(t,e,i,s,o){const n=String(e||"").trim();if(!n)return null;const r=o?s.configEntries[o]:null;return{domain:n,label:Ko(t,n,s,r?.title),configEntryId:o,configEntryTitle:r?.title,confidence:i}}function Jo(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 Zo(t,e,i,s=Vo(t)){const o=i.entities?.[e]||t?.entities?.[e];if(!o)return null;const n=o.config_entry_id||void 0,r=n?s.configEntries[n]?.domain:"";return Yo(t,o.platform||r,"entity-platform",s,n)}var Xo={"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 (lights/switches)","marker.tap_label":"Tap action for this device","tap.toggle_note":"Toggle never applies to locks and alarms; hold the icon to open the info card.","import.title":"Create spaces from HA floors","import.hint":"Your Home Assistant already knows these floors. Pick the ones to turn into plan spaces — you will upload a floor-plan image for each one next. Rooms are then outlined by hand on the plan.","import.start":"Create {n} space(s)","import.manual":"Start from scratch","import.progress":"Floor {i} of {n}","import.done":"Spaces created. Outline the rooms: click grid dots and close the contour.","btn.skip":"Skip","space.scale_label":"Scale (grid cell size)","space.scale_unit":"cm per cell","space.display_section":"Display","space.show_borders":"Always show room borders","space.show_names":"Show room names (drag to move)","space.room_color":"Border & name color","space.opacity":"Opacity","space.fill_label":"Room fill","fill.none":"None","fill.lqi":"Zigbee signal","fill.light":"Lights","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…","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_hint":"empty = default from general settings","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set how thick it is. Empty or zero removes the thickness.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new room-wall segment (1–100 cm). Shared walls keep the neighbour's value.","toast.wallthick_pick":"Click a wall (not an open boundary)","toast.wallthick_open":"Open boundaries have no thickness","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from 1 to {max} {unit}","toast.physical_angle":"Enter a rotation angle from 0° up to, but not including, 90°","toast.physical_limit":"The space has reached the limit for this type of geometry","markup.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":"Use the device's temperature sensor","marker.use_climate_temp_tip":"Air conditioners and thermostats know the room temperature (current_temperature). Shows it as a badge next to the icon and counts it in the room average — like a regular thermometer.","marker.light_role_label":"Is this device a light source?","marker.light_role_tip":"Controls whether this marker itself is a spatial light source. Linked lights above remain independent room-light controls.","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.glow_color_label":"Glow colour and brightness","marker.glow_color_tip":"Use live source values, override only its colour, or fix both 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_brightness_hint":"Minimum 1%. To disable this marker as a light source, choose “Never” above.","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.","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.","run.automation":"automation","run.script":"script","run.scene":"scene","confirm.tap_run":'Run "{name}"?',"confirm.tap_toggle":'Toggle "{name}"?',"confirm.tap_cover":'Open/close "{name}"?',"toast.run_started":"Started: {name}","toast.run_target_missing":"Run target not found — check the device settings","toast.run_target_required":"Pick an automation, script or scene","btn.run":"Run","vac.section":"Robot vacuum: live position","vac.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"};const Qo={en:Xo,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":"Toggle никогда не применяется к замкам и сигнализациям; долгое нажатие всегда открывает инфо-карточку.","import.title":"Создать пространства из этажей HA","import.hint":"Home Assistant уже знает эти этажи. Отметьте, какие превратить в пространства плана — далее для каждого попросим картинку плана. Комнаты затем обводятся вручную по плану.","import.start":"Создать: {n}","import.manual":"Начать с нуля","import.progress":"Этаж {i} из {n}","import.done":"Пространства созданы. Обведите комнаты: кликайте по точкам сетки и замкните контур.","btn.skip":"Пропустить","space.scale_label":"Масштаб (размер клетки сетки)","space.scale_unit":"см на клетку","space.display_section":"Отображение","space.show_borders":"Всегда отображать границы комнат","space.show_names":"Отображать названия комнат (перетаскиваются)","space.room_color":"Цвет границ и названий","space.opacity":"Прозрачность","space.fill_label":"Заливка комнат","fill.none":"Нет","fill.lqi":"По силе зигби-сигнала","fill.light":"По освещению","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":"Загрузка…","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_hint":"пусто = по умолчанию из общих настроек","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены комнаты (1–100 см). Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от 1 до {max} {unit}","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","markup.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.light_role_label":"Является источником света","marker.light_role_tip":"Определяет, является ли сам маркер пространственным источником света. Привязанные выше лампы остаются независимыми источниками для комнаты.","marker.light_role_auto_yes":"Автоматически (источник света)","marker.light_role_auto_no":"Автоматически (не источник света)","marker.light_role_always":"Всегда источник света","marker.light_role_never":"Никогда не источник света","marker.glow_color_label":"Цвет и яркость свечения","marker.glow_color_tip":"Использовать данные источника, задать только цвет либо зафиксировать цвет и яркость.","marker.glow_mode_auto":"Из источника","marker.glow_mode_color":"Задать цвет","marker.glow_mode_fixed":"Задать цвет и яркость","marker.glow_color":"Цвет свечения","marker.glow_brightness":"Яркость","marker.glow_brightness_hint":"Минимум 1%. Чтобы отключить источник света, выберите «Никогда» выше.","marker.glow_disabled_never":"Настройки свечения недоступны: маркер явно исключён из источников света.","marker.glow_disabled_auto":"В автоматическом режиме у этого маркера не найден пространственный источник света.","marker.glow_disabled_no_entity":"Для пространственного источника нет активной управляемой сущности 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":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","run.automation":"автоматизация","run.script":"скрипт","run.scene":"сцена","confirm.tap_run":"Запустить «{name}»?","confirm.tap_toggle":"Переключить «{name}»?","confirm.tap_cover":"Открыть/закрыть «{name}»?","toast.run_started":"Запущено: {name}","toast.run_target_missing":"Цель запуска не найдена — проверьте настройки устройства","toast.run_target_required":"Выберите автоматизацию, скрипт или сцену","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.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":"Применить"}};function tn(t,e){if(e&&e in Qo)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function en(t,e,i){return Ri(Qo[t][e]??Xo[e]??e,i)}class sn extends lt{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=Wo(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0}get _lang(){return tn(this.hass)}_t(t,e){return en(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=Vo(t)){if("virtual"===e)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const o=e.indexOf(":");if(o<1)return[];const n=e.slice(0,o),r=e.slice(o+1);if("entity"===n){const e=Zo(t,r,i,s);return e?[e]:[]}if("device"!==n)return[];const a=i.devices?.[r]||t?.devices?.[r];if(!a)return[];const l=[a.config_entry_id,...Array.isArray(a.config_entries)?a.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const i=s.configEntries[e];return Yo(t,i?.domain,"registry-owner",s,e)});if(l.some(Boolean))return Jo(l);const c=[];for(const[e,o]of Object.entries(i.entities||{}))o?.device_id===r&&null==o?.disabled_by&&c.push(Zo(t,e,i,s));return c.some(Boolean)?Jo(c):Jo((Array.isArray(a.identifiers)?a.identifiers:[]).map(e=>{const i=Array.isArray(e)?e[0]:null;return Yo(t,i,"identifier-fallback",s)}))}(this.hass,this.presentation.binding,this.registry,Vo(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=Zo(this.hass,t,this.registry,Vo(this.hass));return e?this._providerText(e):this._t("marker.preview.unknown_provider")}render(){const t=this.presentation;if(!t)return W;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:qo(i)}}(t,this._demoGeneration):t,s=this._providers(),o="alarm"===t.visual.status||"none"!==t.activity,n=i.scale*("none"!==i.activity?i.rippleScale:1),r=Math.min(1,2.5/Math.max(1,n)),a=Math.round(100*r),l=["dev",...i.classes,"virtual"===i.binding?"virtual":"",i.haDisabled?"ghost ha-disabled":"",null!=i.valueText?"valonly":""].filter(Boolean).join(" "),c=bt(i).join(";"),h=e?this._t("marker.preview.demo_notice"):this._reason(t.explanation.reason),d=[this.deviceName,this._providerSummary(s),this._stateSummary(t),h,t.valueFullText||""].filter(Boolean).join(". ");return j`
${this._t("marker.preview.title")} @@ -3191,7 +3220,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f
`)}
- `:G} + `:W} ${"icon_ripple"===t.display?j``:G} -
`}}Vn.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},Vn.styles=[yt,o` + `:W} +
`}}sn.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},sn.styles=[yt,n` :host { display: block; min-width: 0; } .devicepreview { margin: 4px 0 14px; @@ -3292,74 +3321,74 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f .previewstage { min-height: 142px; } .previewfacts > div { grid-template-columns: minmax(0, 1fr); gap: 2px; } } - `],customElements.get("hp-device-preview")||customElements.define("hp-device-preview",Vn);const Kn=(t,e)=>[t[0]-e[0],t[1]-e[1]],Yn=(t,e)=>[t[0]+e[0],t[1]+e[1]],Jn=(t,e)=>t[0]*e[0]+t[1]*e[1],Zn=t=>Math.hypot(t[0],t[1]);function Xn(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function eo(t,e,i,s){const n=s||to(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 io(t,e,i,s){const n=[],o=Kn(i,e),r=Zn(o);if(rs||h>s)continue;const d=Jn(Kn(o,e),a),p=Jn(Kn(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&n.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return n}function so(t,e,i,s,n){const o=io(t,e,i,n);if(!o.length)return null;const r=t=>o.some(([e,i])=>Qn(t,e,i)<=n),a=t.length,l=[];for(let o=0;on||f>n)continue;const v=Jn(Kn(e,c),u),y=Jn(Kn(i,c),u),b=Math.max(0,Math.min(v,y)),w=Math.min(p,Math.max(v,y));if(!(w-b<=n)){if(b>n&&bn&&wZn(Kn(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const n=i[(s-1+i.length)%i.length],o=i[(s+1)%i.length],r=(t[0]-n[0])*(o[1]-n[1])-(t[1]-n[1])*(o[0]-n[0]),a=Zn(Kn(o,n))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function oo(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-n[0])*a[1]-(t[1]-n[1])*a[0],c=t=>(t[0]-n[0])*a[0]+(t[1]-n[1])*a[1],h=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const n=c(e);n<=h||n>=d||tMath.abs(r+(t-p)/g*(a-r));s=Math.min(s,f(_),f(m))}}return s}function ao(t,e,i){if(ei(t,e,i))return!0;if(ti(t,e,i)||ti(e,t,i))return!1;let s=0;try{const i=Re([[...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+=si(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function lo(t,e,i,s,n){const o=[i.n[0]*s,i.n[1]*s],r={polys:{},openings:{},movedSpans:{}};if(Math.abs(s)<1e-9)return r;for(const e of t){if(e.id===i.roomId){r.polys[e.id]=eo(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[Yn(i.a,o),Yn(i.b,o)]];continue}const t=io(e.poly,i.a,i.b,n);if(!t.length)continue;const a=so(e.poly,i.a,i.b,o,n);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[Yn(t,o),Yn(e,o)]))}for(const t of e)Qn([t.x,t.y],i.a,i.b)<=n&&(r.openings[t.id]=[t.x+o[0],t.y+o[1]]);return r}function co(t,e,i){for(const s of e)for(let e=0;ei)continue;const c=(t.x-n[0])*l[0]+(t.y-n[1])*l[1];if(c-t.length/2>=-i&&c+t.length/2<=a+i)return!0}return!1}function ho(t,e,i){return t.filter(t=>e.some(e=>{for(let s=0;sa.polys[t.id]||t.poly;for(const e of l){const s=t.find(t=>t.id===e),n=a.polys[e];if(!oo(n))return!1;const l=Xn(s.poly),h=Xn(n);if(Math.abs(h)t.find(t=>t.id===e).poly),d=t.map(c);for(const t of ho(e,h,2*r)){const e=a.openings[t.id];if(!co(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function uo(t,e,i,s,n,o){const r=t=>[s[0]+(t[0]-s[0])*n,s[1]+(t[1]-s[1])*n],a={poly:t.poly.map(r),openings:{}};for(const s of e){let e=!1;for(let i=0;i{for(let e=0;et.id===i);if(!l)return!1;const c=t.filter(t=>t.id!==i).map(t=>t.poly),h=uo(l,e,c,s,n,2*a),d=h.poly,p=function(t){const e=function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<3)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const n=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;n.length>=2&&i(n[n.length-2],n[n.length-1],s)<=0;)n.pop();n.push(s)}return s.pop(),n.pop(),s.concat(n)}(t);if(e.length<3)return 0;let i=1/0;for(let t=0;tt.id===i?d:t.poly);for(const t of ho(e,[l.poly],2*a)){const e=h.openings[t.id];if(!co(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function mo(t,e,i){const s=i/e;return si(t)*s*s/1e4}function go(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const fo=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],vo=t=>Math.min(1,Math.max(0,t));function yo(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=fo[fo.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&&We(o,t.poly))||null},r=o(1),a=o(-1);return r&&a?null:r||a?r?{normal:[-n[0],-n[1]],roomId:r.id}:{normal:n,roomId:a.id}:null}function wo(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function ko(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 $o(t,e){try{const i=Re([[...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 xo(t,e,i,s,n,o,r){if(!(s>0))return[];const a=function(t,e){const i=function(t,e){return function(t){const e=t%360;return e<0?e+360:e}(t-e)}(t,e)*Math.PI/180;return[Math.sin(i),-Math.cos(i)]}(i,n),l=[-a[0],-a[1]],c=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),h=[];for(const i of e){if(!(i.length>0))continue;const e=bo(i,t);if(!e||!wo(e.normal,a,s))continue;const n=t.find(t=>t.id===e.roomId);if(!n)continue;const d=o&&o[e.roomId]||n.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],m=Math.max(0,r?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(p)*u,y=Math.sin(p)*u,b=[g-v,f-y],w=[g+v,f+y],k=c*i.length,$=$o(ko(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 So(t){return Math.round(10*(Number(t)||0))/10}const Mo=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Co(t,e){const i=Mo(e?.north_deg);return null!==i?i:Mo(t?.north_deg)}function Do(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function To(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Po(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 Ro={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},zo=(t,e=1)=>{const i=Number(t);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):e},Ao=t=>{let e=Number(t);return Number.isFinite(e)?(e=(e%360+360)%360,e>180?e-360:e):0};function Io(t,e,i){return Ao(e)?[t.x,t.y]:i([t.x,t.y])}const Eo=(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},No=(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},Fo=(t,e,i,s=Ro.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?No(o,e,i):s};function Oo(t,e){return{color:t.color,opacity:zo(t.opacity),width_cm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),...e?{fill:t.fill,fill_color:t.fillColor,fill_opacity:zo(t.fillOpacity,.25)}:{}}}function Lo(t){const e=t.x+t.w/2,i=t.y+t.h/2,s=Ao(t.angle)*Math.PI/180,n=Math.cos(s),o=Math.sin(s),r=(t,s)=>{const r=t-e,a=s-i;return[e+r*n-a*o,i+r*o+a*n]};return[r(t.x,t.y),r(t.x+t.w,t.y),r(t.x+t.w,t.y+t.h),r(t.x,t.y+t.h)]}function Ho(t){const e=Lo(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 qo(t,e,i,s,n,o,r,a){const l=Ao(t.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l),d=-Math.sin(l),p=Math.cos(l),u=t.x+t.w/2,_=t.y+t.h/2,m=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,f=u+m*c+g*d,v=_+m*h+g*p,y=s-f,b=n-v;let w=(y*c+b*h)*(e>0?1:-1),k=(y*d+b*p)*(i>0?1:-1),$=null;if(o){const e=w/Math.max(t.w,a),i=k/Math.max(t.h,a);$=Math.max(a/Math.max(t.w,a),a/Math.max(t.h,a),e,i),w=t.w*$,k=t.h*$}if(r>0)if(o){const e=Math.max(1,Math.round(t.w/r)),i=Math.max(1,Math.round(t.h/r));if(Math.abs(t.w-e*r)<1e-6&&Math.abs(t.h-i*r)<1e-6){const s=(t,e)=>{let i=Math.abs(t),s=Math.abs(e);for(;s;)[i,s]=[s,i%s];return Math.max(1,i)},n=1/s(e,i),o=Math.max(a/t.w,a/t.h),r=Math.max(o,Math.round(($??1)/n)*n);w=t.w*r,k=t.h*r}else{t.w>=t.h?(w=Math.round(w/r)*r,k=w*(t.h/Math.max(t.w,a))):(k=Math.round(k/r)*r,w=k*(t.w/Math.max(t.h,a)))}}else w=Math.round(w/r)*r,k=Math.round(k/r)*r;w=Math.max(a,w),k=Math.max(a,k);return{x:f+(e>0?w/2:-w/2)*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)*p-k/2,w:w,h:k,angle:Ao(t.angle)||void 0}}const Uo=(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 r=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*n)/o));return[e[0]+s*r,e[1]+n*r]};const jo=15;function Wo(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function Bo(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function Go(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function Vo(t,e,i){const s=Number(e)>0?Number(e):5;return Bo(t)/s*i}function Ko(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function Yo(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 Jo(t,e,i){const s=Ko((t[0]+e[0])/2,i),n=Ko((t[1]+e[1])/2,i),[o,r]=Yo(t,e);let a=Math.atan2(r,o);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,c=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(c)},${n.toFixed(c)}@${l.toFixed(4)}`}function Zo(t,e,i,s){return 1===s?Jo(t,e,i):Jo([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function Xo(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 Qo(t,e,i,s,n){const o=n>0?n:1;return{key:Zo(t,e,s,o),cm:Bo(i),a:[t[0]/o,t[1]/o],b:[e[0]/o,e[1]/o]}}function tr(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[n,o]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[n,o,r].every(Number.isFinite)&&s.push({w:e,x:n*i,y:o*i,ang:r})}return s}function er(t,e){const[i,s]=Yo(t,e);let n=Math.atan2(s,i);return n<0&&(n+=Math.PI),n}function ir(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function sr(t,e,i,s,n=1){if(!t?.length)return null;const o=Zo(e,i,s,n),r=t.find(t=>t.key===o);if(r)return r;const a=n>0?n:1,l=(e[0]+i[0])/2,c=(e[1]+i[1])/2,h=er(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of tr(t,a))if(ir(e.ang,h)&&Math.hypot(e.x-l,e.y-c)<=d)return e.w;return null}function nr(t,e,i,s,n=1){const o=sr(t,e,i,s,n);return o&&o.cm>0?Bo(o.cm):0}function or(t,e,i,s=1,n=[]){if(!t?.length)return[];const o=new Set,r=Le(e);for(const t of r)o.add(Zo([t[0],t[1]],[t[2],t[3]],i,s));const a=e||[],l=Math.max(i*s*.02,1e-9);for(let t=0;t(o.has(t.key)||(t=>{const e=Xo(t,s);if(!e)return!1;const[i,o]=e,a=o[0]-i[0],c=o[1]-i[1],h=Math.hypot(a,c);if(h<=l)return!1;if(!r.some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];return ir(er(i,o),er(e,s))&&Sr(i[0],i[1],e[0],e[1],s[0],s[1])<=l&&Sr(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(!ir(er(i,o),er(e,s)))return!1;const n=t=>Math.abs((t[0]-i[0])*c-(t[1]-i[1])*a)/h;if(n(e)>l||n(s)>l)return!1;const r=h*h,d=((e[0]-i[0])*a+(e[1]-i[1])*c)/r,p=((s[0]-i[0])*a+(s[1]-i[1])*c)/r;return Math.min(1,Math.max(d,p))-Math.max(0,Math.min(d,p))>l/h});return!d})(t))&&t.cm>=1&&t.cm<=100)}function rr(t,e,i,s=8){const[n,o]=Yo(t,e);let r=Math.atan2(o,n);r<0&&(r+=Math.PI);let a=i*Math.PI/180%Math.PI;a<0&&(a+=Math.PI);let l=Math.abs(r-a);return l>Math.PI/2&&(l=Math.PI-l),l<=s*Math.PI/180}function ar(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,a=Math.max(.5*s,1e-9)*r,l=new Set,c=[];for(const n of t){let t="",h=null;const d=Xo(n,r);if(d)for(let n=0;na||Sr(d[1][0],d[1][1],o[0],o[1],l[0],l[1])>a)continue;const u=l[0]-o[0],_=l[1]-o[1],m=u*u+_*_;if(m<1e-18)continue;const g=t=>{const e=Math.max(0,Math.min(1,((t[0]-o[0])*u+(t[1]-o[1])*_)/m));return[c[0]+(p[0]-c[0])*e,c[1]+(p[1]-c[1])*e]};h=[g(d[0]),g(d[1])],t=Zo(h[0],h[1],s,r);break}if(d||(t=o.get(n.key)||""),!t){const o=tr([n],r)[0];if(o)for(let n=0;n1.000001)continue;if(Sr(o.x,o.y,l[0],l[1],c[0],c[1])>a)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]=Yo(h,d),b=Math.max(s*r,1e-6);t=Zo([g-v*b,f-y*b],[g+v*b,f+y*b],s,r);break}}t||(t=n.key),l.has(t)||(l.add(t),c.push(h?Qo(h[0],h[1],n.cm,s,r):{...n,key:t,cm:Bo(n.cm)}))}return c}function lr(t,e,i,s,n,o=1){const r=Zo(e,i,n,o),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,Qo(e,i,s,n,o)]}function cr(t,e,i,s,n,o=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,n=1,o=[]){const r=yr(t,e,i,s,n,o);if(!r)return[];const a=[];for(let t=0;t0)||!t||t.length<2)return"";if(i&&t.length>=3){let i=t;const s=t[t.length-1];if(t.length>=4&&Math.hypot(t[0][0]-s[0],t[0][1]-s[1])<1e-9&&(i=t.slice(0,-1)),i.length>=3){const t=i.map(()=>e),s=Tr(i,t),n=gr(i,t);if(s&&n)return`${fr(s)} ${fr(vr(n))}`}}let s="";for(let i=0;i.05)&&Sr(r,a,t[0],t[1],t[2],t[3])<=o)return!0}return!1}function pr(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function ur(t,e){const i=t[e],s=t[(e+1)%t.length],n=s[0]-i[0],o=s[1]-i[1],r=Math.hypot(n,o)||1;let a=-o/r,l=n/r;const c=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,n=e.length-1;st[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r+0)+o&&(i=!i)}return i}([c[0]+.001*a,c[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function _r(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 mr(t,e,i,s){const n=e[0]*s[1]-e[1]*s[0];if(Math.abs(n)<1e-12)return null;const o=[i[0]-t[0],i[1]-t[1]],r=(o[0]*s[1]-o[1]*s[0])/n;return[t[0]+r*e[0],t[1]+r*e[1]]}function gr(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(_r(b,w)){const e=t[n],i=[e[0]+p*h,e[1]+u*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=mr(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]+p*h,t[n][1]+u*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 fr(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),a=Fe(r);if(!a||a.length<3)return null;const l=pr(s,n),c=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Fe(i);if(t)for(const e of Zi(a,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=Xo(t,n);e&&c.push(e[0],e[1])}const h=[],d=[];for(let t=0;tl)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=n||r>=1-n||(o.some(t=>Math.abs(t-r)*s<=2*l)||o.push(r))}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:a}}function br(t,e,i,s){const n=e.poly.length,o=new Array(n).fill(0);if(!t?.length)return o;const r=new Set,a=[];for(let l=0;l0?(o[l]=Bo(c.cm),r.add(c.key)):a.push(l)}if(!a.length)return o;const l=s>0?s:1,c=Math.max(.5*i,1e-9)*l,h=tr(t,l).filter(t=>t.w.cm>0),d=new Map;for(const t of a){const i=e.parent[t],s=d.get(i);s?s.push(t):d.set(i,[t])}for(const[t,i]of d){const s=e.orig[t],n=e.orig[(t+1)%e.orig.length],a=er(s,n),d=(s[0]+n[0])/2,p=(s[1]+n[1])/2;let u=null;const _=Math.hypot(n[0]-s[0],n[1]-s[1]);for(const t of h){if(r.has(t.w.key))continue;if(!ir(t.ang,a))continue;const e=Xo(t.w,l);let i=!1,o=0;if(e){if(!ir(er(e[0],e[1]),a))continue;if(Sr(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>c||Sr(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(Sr(t.x,t.y,s[0],s[1],n[0],n[1])>c)continue;o=Math.hypot(t.x-d,t.y-p)}(!u||i&&!u.exact||i===u.exact&&ot?.id===e),n=Fe(s);if(!n)return[];const o=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Fe(s);if(t)for(const e of Zi(n,t,i))o.push(e)}return o}(t,e,pr(n,a)),h=function(t,e,i,s,n){const o=pr(s,n),r=[];for(let a=0;aSr(h,d,t[0],t[1],t[2],t[3])<=o);r.push(p?"shared":"outer")}return r}(l.poly,c,s,n,a),d=br(i,l,n,a),p=d.map((t,e)=>h[e]&&t>0?Vo(t,o,r)/2:0);return{...l,kinds:h,cms:d,offsets:p}}function kr(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const c=wr(t,l.id,e,i,s,n,o,r);if(c)for(let t=0;t0&&!l.has(c.key)&&(l.add(c.key),a.push(c));const c=[];for(const a of t||[]){if(!a?.id)continue;const l=wr(t,a.id,e,i,s,n,o,r);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:Zo(h,d,s,r),cm:n,len:p}),t=o+1}}}c.sort((t,e)=>e.len-t.len||t.key.localeCompare(e.key));const h=[],d=new Set,p=new Set,u=4*pr(s,r);for(const t of c){const e=a.filter(e=>!p.has(e.key)&&e.cm===t.cm&&ir(er(e.a,e.b),er(t.a,t.b))&&Sr(e.a[0],e.a[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u&&Sr(e.b[0],e.b[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u);if(e.length){for(const t of e)p.add(t.key);d.has(t.key)||(d.add(t.key),h.push(Qo(t.a,t.b,t.cm,s,r)))}}for(const t of a)p.has(t.key)||d.has(t.key)||(d.add(t.key),h.push(Qo(t.a,t.b,t.cm,s,r)));return h}function xr(t,e,i,s,n,o,r,a=1){const l=pr(n,a),c=(s[0]+s[2])/2,h=(s[1]+s[3])/2,d=er([s[0],s[1]],[s[2],s[3]]);let p=null;for(const s of kr(t,e,i,n,o,r,a)){if(!ir(er(s.a,s.b),d))continue;const t=Sr(c,h,s.a[0],s.a[1],s.b[0],s.b[1]);t>4*l||(!p||tt?.id===e),c=Fe(l);if(!c||c.length<3)return null;if(!i?.length)return c.map(t=>[t[0],t[1]]);const h=wr(t,e,i,s,n,o,r,a);return h&&h.offsets.some(t=>t>0)&&gr(h.poly,h.offsets)||c.map(t=>[t[0],t[1]])}function Cr(t){const e=t.map(t=>[t[0],t[1]]);return e.push([t[0][0],t[0][1]]),[e]}function Dr(t,e,i,s=[],n,o,r,a=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=wr(t,s.id,e,i,n,o,r,a);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=Tr(l.poly,l.offsets),p=gr(l.poly,l.offsets);d&&c.push({outset:d,inset:p})}for(const t of l){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);if(e.length){const s=Math.min(Math.max(...e)-Math.min(...e),Math.max(...i)-Math.min(...i)),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,r){if(!e?.length||!i?.length)return[];const a=4*pr(s,r),l=new Map;for(const a of kr(t,e,i,s,n,o,r))!a.open&&a.half>0&&!l.has(a.key)&&l.set(a.key,a);const c=[...l.values()];if(c.length<2)return[];const h=[];for(const t of i)for(const e of[[t[0],t[1]],[t[2],t[3]]])h.some(t=>Math.hypot(t[0]-e[0],t[1]-e[1])<=a)||h.push(e);const d=[],p=(t,e)=>{let i=0,s=0;if(Math.hypot(t.a[0]-e[0],t.a[1]-e[1])<=a)i=t.b[0]-t.a[0],s=t.b[1]-t.a[1];else{if(!(Math.hypot(t.b[0]-e[0],t.b[1]-e[1])<=a))return null;i=t.a[0]-t.b[0],s=t.a[1]-t.b[1]}const n=Math.hypot(i,s);return n>a?[i/n,s/n]:null};for(const t of h){const e=c.map(e=>({iv:e,u:p(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(r>0?[t.slice(),h,u,p]:[t.slice(),p,u,h])}}return d}(t,e,i,n,o,r,a),p=s.length?Rr(t,e,i,n,o,r,a):null;try{const t=t=>{const e=Cr(t.outset);return t.inset?ze(e,Cr(t.inset)):e};let e=c.length?t(c[0]):null;for(let i=1;i0))continue;const i=Ir(p,t,!0);if(!i.negative&&!i.positive)continue;const s=t.angle*Math.PI/180,o=Math.cos(s),r=Math.sin(s),l=-r,c=o,d=t.length/2,u=1.25*Math.max(h,n*a),_=[[t.x-o*d-l*u,t.y-r*d-c*u],[t.x+o*d-l*u,t.y+r*d-c*u],[t.x+o*d+l*u,t.y+r*d+c*u],[t.x-o*d+l*u,t.y-r*d+c*u]];e&&(e=ze(e,Cr(_)))}for(const t of l)t.length<3||(e=e?Pe(e,Cr(t)):[Cr(t)]);const i=function(t){if(!t)return"";let e="";for(const i of t)if(Array.isArray(i))for(const t of i){if(!Array.isArray(t)||t.length<4)continue;const i=t.slice(0,t.length-1);i.length<3||(e+=(e?" ":"")+fr(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:h,fillRule:"evenodd"}:null}catch{const s=function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const c=wr(t,l.id,e,i,s,n,o,r);if(!c||c.poly.length<3||!c.offsets.some(t=>t>0))continue;const h=Tr(c.poly,c.offsets),d=gr(c.poly,c.offsets);if(!h||!d)continue;const p=`${fr(h)} ${fr(vr(d))}`;let u="",_="outer",m=0,g=0;for(let t=0;t0){u=Zo(c.poly[t],c.poly[(t+1)%c.poly.length],s,r),_=c.kinds[t]||"outer",m=c.cms[t],g=Vo(m,n,o);break}a.push({d:p,key:u,kind:_,cm:m,depthUnits:g})}return a}(t,e,i,n,o,r,a),c=l.map(t=>fr(t)).join(" ");return s.length||c?{d:[s.map(t=>t.d).join(" "),c].filter(Boolean).join(" "),depthUnits:h,fillRule:"nonzero"}:null}}function Tr(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]]);vr(t),e.slice().reverse();const s=[];for(let n=0;n0||a>0)){s.push([t[n][0],t[n][1]]);continue}if(_r(b,w)){const e=t[n],i=[e[0]-l*r,e[1]-c*r],o=[e[0]-h*a,e[1]-d*a];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=mr(k,b,$,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[n][0]-l*r,t[n][1]-c*r]),a>0&&s.push([t[n][0]-h*a,t[n][1]-d*a])}return s.length>=3?s:null}function Pr(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){const c=Fe(l);if(c&&c.length>=3){const h=wr(t,l.id,e,i,s,n,o,r),d=h&&h.offsets.some(t=>t>0)?Tr(h.poly,h.offsets):null,p=d||c;a.push({poly:p.map(t=>t.join(",")).join(" ")})}else l&&null!=l.x&&null!=l.y&&null!=l.w&&null!=l.h&&a.push({rect:{x:l.x,y:l.y,w:l.w,h:l.h,rx:.03*Math.min(l.w,l.h)}})}return a}function Rr(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const c=wr(t,l.id,e,i,s,n,o,r);if(!c)continue;const h=Math.abs(si(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],r=n[0][1],a=0,l=o<=e+s;for(let t=1;t=i-s,{coverage:a,full:l}}function Ar(t,e){return Number(e.full)-Number(t.full)||t.faceDistance-e.faceDistance||t.area-e.area||t.roomId.localeCompare(e.roomId)}function Ir(t,e,i=!1){const s=Number(e?.x),n=Number(e?.y),o=Number(e?.angle),r=Number(e?.length);if(!([s,n,o,r].every(Number.isFinite)&&r>0))return{negative:null,positive:null};const a=o*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=-c,d=l,p=r/2,u=Math.max(1e-9,t.adjacencyEps),_=new Map;let m=0;for(const e of t.edges){if(i&&!(e.half>0))continue;if(!rr(e.a,e.b,o))continue;const[t,r]=Yo(e.a,e.b);if(Math.abs((s-e.a[0])*r-(n-e.a[1])*t)>u)continue;const a=(e.a[0]-s)*l+(e.a[1]-n)*c,g=(e.b[0]-s)*l+(e.b[1]-n)*c,f=Math.max(-p,Math.min(a,g)),v=Math.min(p,Math.max(a,g));if(v-f<=u)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,r]},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=zr(t.pieces,-p,p,u);t.coverage=e.coverage,t.full=e.full}const g=t=>{const e=[..._.values()].filter(e=>e.side===t&&e.coverage>u);return e.sort(Ar),e[0]||null};return{negative:g(-1),positive:g(1)}}function Er(t,e){const i=Ir(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,r=(-1===o?i.negative:i.positive)||n,a=[...r.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(!(a.half>0&&a.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*a.half,oy:h*o*a.half,cm:a.cm,side:o}}function Nr(t,e){const i=1e-9,s=[];for(const t of[...e].filter(t=>t.x1>t.x0&&t.half>0).sort((t,e)=>t.x0-e.x0||t.x1-e.x1||t.half-e.half)){const e=s[s.length-1];e&&t.x0<=e.x1+i&&Math.abs(t.half-e.half)<=i?e.x1=Math.max(e.x1,t.x1):s.push({...t})}const n=[];for(let e=0;e=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 a}function Or(t,e){const i=new Map;return e.map(e=>function(t,e,i){const s=Ir(t,e,!0),n=s.negative,o=s.positive;if(!n&&!o)return null;let r;if(n&&o)r=[{candidate:n,side:-1},{candidate:o,side:1}];else{const t=n||o;r=[{candidate:t,side:-1},{candidate:t,side:1}]}const a=r.map(({candidate:t,side:s})=>({candidate:t,side:s,pieces:Fr(e,s,t.pieces,i)})),l=a.map(({candidate:t,side:e,pieces:i})=>({side:e,roomId:t.roomId,d:Nr(e,i)})),c=a.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 Lr=150;function Hr(t){return Number.isFinite(t)?Math.max(1,Math.min(Lr,t)):1}function qr(t){return((Number.isFinite(Number(t))?Number(t):0)%90+90)%90}const Ur=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 Wr(t,e,i,s,n){const o=e[0]-t[0],r=e[1]-t[1],a=Math.hypot(o,r);if(!(a>1e-9))return null;const l=Vo(i,s,n)/2,c=-r/a*l,h=o/a*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 Br(t,e,i){const s=Number(e)>0?Number(e):5,n=Hr(t.cm)/s*i,o=t.center[0],r=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,r+Math.sin(s)*t]})}const a=n/2,l=qr(t.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l);return[[-a,-a],[a,-a],[a,a],[-a,a]].map(([t,e])=>[o+t*c-e*h,r+t*h+e*c])}function Gr(t,e,i){const s=[];for(let n=0;n+1t.length>=3).map(t=>Ur(t));return e.length?Pe(e[0],...e.slice(1)):null}catch{return null}}(e);if(i)return ze(Ur(t),i)}catch{}let i=[Ur(t)];for(const t of e)if(!(t.length<3))try{i=ze(i,Ur(t))}catch{}return i}function Kr(t){let e=0;for(const i of t||[])if(i?.length){e+=si(i[0]||[]);for(let t=1;t=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]))}return e}function Jr(t,e,i){if(!(i>0))return[];const s=Math.max(1e-9,1e-9*i);if(t.some(t=>Zr(e,t)||(t=>t.some((i,n)=>{const o=t[(n+1)%t.length],r=o[0]-i[0],a=o[1]-i[1],l=r*r+a*a;if(!(l>0))return Math.hypot(e[0]-i[0],e[1]-i[1])<=s;const c=Math.max(0,Math.min(1,((e[0]-i[0])*r+(e[1]-i[1])*a)/l));return Math.hypot(e[0]-(i[0]+c*r),e[1]-(i[1]+c*a))<=s}))(t)))return[Array.from({length:128},(t,s)=>{const n=s/128*Math.PI*2;return[e[0]+Math.cos(n)*i*1.01,e[1]+Math.sin(n)*i*1.01]})];const n=[];for(const o of t)if(!(o.length<2)){n.push(o);for(let t=0;ts&&u>s))continue;const _=Math.max(-1,Math.min(1,(l*h+c*d)/(p*u))),m=Math.sqrt(Math.max(0,(1+_)/2)),g=Math.max(2*i,1.02*i/Math.max(m,.001)),f=[e[0]+l/p*g,e[1]+c/p*g],v=[e[0]+h/u*g,e[1]+d/u*g];n.push([r,a,v,f])}}return n}function Zr(t,e){let i=!1;for(let s=0,n=e.length-1;st[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r||1e-12)+o&&(i=!i)}return i}function Xr(t,e,i){if(Math.hypot(t.center[0]-e.center[0],t.center[1]-e.center[1])>i)return!1;if(Math.abs(Hr(t.cm)-Hr(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(qr(t.angle)-qr(e.angle));return Math.min(s,90-s)<=1e-6}const Qr=1e3;function ta(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 ea=.01,ia=100;function sa(t,e=1e3){const i=ta(t?.plan_aspect,e),s=Number(t?.plan_scale),n=Number.isFinite(s)&&s>0?Math.min(ia,Math.max(ea,s)):1,o=Number(t?.plan_scale_x),r=Number(t?.plan_scale_y),a=Number.isFinite(o)&&o>0?Math.min(ia,Math.max(ea,o)):n,l=Number.isFinite(r)&&r>0?Math.min(ia,Math.max(ea,r)):n,c=Number(t?.plan_x),h=Number(t?.plan_y),d=Ao(t?.plan_angle);return{x:i.x+(Number.isFinite(c)?_a(c):0)*e,y:i.y+(Number.isFinite(h)?_a(h):0)*e,w:i.w*a,h:i.h*l,...d?{angle:d}:{}}}function na(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 oa(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=Qr,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]*Qr,i[1]*e,i[2]*Qr,i[3]*e],bg:t.plan_url?{href:ns(t.plan_url),...sa(t,Qr)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...na(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*Qr:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*Qr:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*Qr,t[1]*e]):void 0}}),room_drafts:(t.room_drafts||[]).map(t=>({id:t.id,points:(t.points||[]).map(t=>[t[0]*Qr,t[1]*e]),segments:(t.segments||[]).map(t=>({cm:Number(t.cm)}))})),partitions:(t.partitions||[]).map(t=>({id:t.id,a:[t.a[0]*Qr,t.a[1]*e],b:[t.b[0]*Qr,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]*Qr,t.center[1]*e],cm:Number(t.cm),..."circle"===t.shape?{}:{angle:qr(t.angle)}}))}}):[]}const ra=5e3,aa=ra*Qr,la=240,ca=Qr/la,ha=1/la;function da(t){if(!Number.isFinite(t))return t;const e=Math.round(t*la/Qr)*Qr/la;return Math.abs(e-t)<=1e-9*ca?t:e}function pa(t){return{x:da(t.x),y:da(t.y)}}function ua(t){return Number.isFinite(t)?Math.min(aa,Math.max(-aa,t)):0}function _a(t){return Number.isFinite(t)?Math.min(ra,Math.max(-ra,t)):0}const ma=1/3,ga=200;function fa(t){let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t){const t=Number(o[0]),r=Number(o[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>n&&(n=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:n}}function va(t){return t.poly&&t.poly.length?fa(t.poly):null==t.x||null==t.y?null:fa([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function ya(t,e){const i=[];for(const e of t.rooms||[]){const t=va(e);t&&i.push(t)}if(t.bg){const e=fa(Lo(t.bg));e&&i.push(e)}for(const t of e||[])if(Array.isArray(t)){const e=fa([t]);e&&i.push(e)}else i.push(t);return i}const ba=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},wa=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 ka(t,e){let{x:i,y:s,w:n,h:o}=t;n<30&&(i=i+n/2-100,n=ga),o<30&&(s=s+o/2-100,o=ga);const r=Math.max(n,o)*e;return{x:i-r,y:s-r,w:n+2*r,h:o+2*r}}function $a(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)<=aa&&Math.abs(t.maxX)<=aa&&Math.abs(t.minY)<=aa&&Math.abs(t.maxY)<=aa);if(!o.length)return{core:null,all:null,outliers:0};const r=wa(o);if(o.length<4){const t=ka(r,i);return{core:t,all:t,outliers:0}}const a=o.map(t=>(t.minX+t.maxX)/2),l=o.map(t=>(t.minY+t.maxY)/2),c=ba(a),h=ba(l),d=o.map((t,e)=>Math.max(Math.abs(a[e]-c),Math.abs(l[e]-h))),p=Math.max(((t,e)=>t.length?t[Math.min(t.length-1,Math.max(0,Math.round(e*(t.length-1))))]:0)([...d].sort((t,e)=>t-e),.75),n),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,m=_&&_<=o.length*ma?o.filter((t,e)=>!u[e]):o;return{core:ka(wa(m)||r,i),all:ka(r,i),outliers:m===o?0:_}}function xa(t,e,i=.05){const s=$a(ya(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,Qr,Qr];return{x:n[0],y:n[1],w:n[2],h:n[3]}}function Sa(t){const e=xa(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function Ma(t){const e=[];for(const i of t.rooms||[]){const t=va(i);t&&e.push(t)}const i=$a(e,{pad:0}).core;return i?Math.max(Qr,Math.min(aa,Math.max(i.w,i.h))):Qr}function Ca(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*Ma(e)*o/n}const Da=[1,2,5,10,20,50,100,200,500,1e3];function Ta(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 Pa(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 Ra=["furniture","appliance","sanitary","other"],za=[{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]]}],Aa=new Map(za.map(t=>[t.id,t]));function Ia(t){return t&&Aa.get(t)||null}function Ea(t){return za.filter(e=>e.group===t)}function Na(t,e,i=ca,s=1e3){const n=Number(e)>0?Number(e):5;return(Number(t)||0)/n*i/s}const Fa=5e-4,Oa=ra;function La(t){return Number.isFinite(t)?Math.max(Fa,Math.min(Oa,t)):Fa}const Ha=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function qa(t,e,i){const s=Ia(t);if(!(s&&e>0&&i>0))return"";const n=t=>Ha(t*e),o=t=>Ha(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}H${n(e+s)}V${o(i+a)}H${n(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}L${n(s)} ${o(a)}`)}else if("e"===t[0]){const[,s,a,l,c]=t;r.push(`M${n(s-l)} ${o(a)}A${Ha(l*e)} ${Ha(c*i)} 0 0 1 ${n(s+l)} ${o(a)}A${Ha(l*e)} ${Ha(c*i)} 0 0 1 ${n(s-l)} ${o(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${n(e[0])} ${o(e[1])}`;for(let t=2;t+1{let e=(t%360+360)%360;return e>180&&(e-=360),e};function ja(t,e,i,s,n,o=0){let r=null,a=n;for(const n of s){const[s,l,c,h]=n,d=c-s,p=h-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let m=((t-s)*d+(e-l)*p)/u;m=Math.max(0,Math.min(1,m));let g=s+m*d,f=l+m*p;const v=Math.hypot(t-g,e-f);if(!(v0){const t=Math.round(m*_/o)*o;g=s+t/_*d,f=l+t/_*p}r={cx:g+y*(i/2),cy:f+b*(i/2),angle:Ua(180*Math.atan2(-y,b)/Math.PI),dist:v}}return r}function Wa(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function Ba(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function Ga(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function Va(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!Ga(t.a)||!Ga(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 Ka(t,e){const i=e[0],s=e[1],n=e[2]-i,o=e[3]-s,r=n*n+o*o;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*n+(t[1]-s)*o)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*n,s+a*o];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function Ya(t,e){return Ka(t,e).q}function Ja(t,e,i,s,n){const o=Ka(t,e).q;let r=o,a=1/0;for(const t of i){const e=Math.hypot(o[0]-t[0],o[1]-t[1]);e<=n&&e0?s:1,m=Math.round(u/_)*_,g=Math.max(0,Math.min(p,m))/p;return[l+h*g,c+d*g]}function Za(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt?.id),r=Va(e);if(r.length)return rl(r,t,i,s).map(t=>Ba(t,i));if(!n)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;tWa([t[0],t[1]],[t[2],t[3]],e))}function tl(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const n=(e||[]).filter(t=>t?.id);new Map(n.map(t=>[t.id,t]));const o=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=o.get(t),s=o.get(e);i&&s&&((i.open_to||[]).includes(e)||(i.open_to=[...i.open_to||[],e]),(s.open_to||[]).includes(t)||(s.open_to=[...s.open_to||[],t]))};for(let t=0;ths(i,t)<4*s)){r(n[t].id,n[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}function el(t,e,i,s=0){const n=e[2]-e[0],o=e[3]-e[1],r=Math.hypot(n,o);if(!(r>1e-9))return null;const a=n/r,l=o/r,c=(t[0]-e[0])*a+(t[1]-e[1])*l;if(c<-s||c>r+s)return null;return Math.abs((t[0]-e[0])*l-(t[1]-e[1])*a)>i?null:hs(t,e)}function il(t,e,i){const s=t[2]-t[0],n=t[3]-t[1],o=e[2]-e[0],r=e[3]-e[1],a=Math.hypot(s,n),l=Math.hypot(o,r);return a>1e-9&&l>1e-9&&(!(Math.abs(s/a*(r/l)-n/a*(o/l))>1e-6)&&(hs([e[0],e[1]],t)<=4*i||hs([t[0],t[1]],e)<=4*i))}function sl(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||!il(s.seg,o.seg,i))return"ambiguous"}return s}function nl(t,e,i,s,n=1,o=15){const r=function(t,e,i,s,n=1,o=15){const[r,a]=Yo([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]=Yo([e[0],e[1]],[e[2],e[3]]);if(Math.abs(r*o-a*i)>.05)continue;const d=nr(t,[e[0],e[1]],[e[2],e[3]],s,n);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-h[0],p[1]-h[1]);u0?l:o}(t,e,i,s,n,o);return lr(t,[e[0],e[1]],[e[2],e[3]],r,s,n)}function ol(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(!(hs(n,o)>Math.hypot(t[2]-t[0],t[3]-t[1])&&hs([o[0],o[1]],t)>i))for(const e of[[o[0],o[1]],[o[2],o[3]]])hs(e,t)<=2*i&&s.push(e);return s}function rl(t,e,i,s){const n=Va(t);if(!n.length)return[];const o=Za(e,s);if(!o.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of n){const e=Ba(t,i),n=e[0],l=e[1],c=e[2]-n,h=e[3]-l,d=Math.hypot(c,h);if(d4*s||o>4*s)continue;const r=(t[0]-n)*p+(t[1]-l)*u,c=(t[2]-n)*p+(t[3]-l)*u,h=Math.max(0,Math.min(r,c)),m=Math.min(d,Math.max(r,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+a?e.hi=Math.max(e.hi,t.hi):i.push({...t})}for(const e of i){const i=[n+p*e.lo,l+u*e.lo],s=[n+p*e.hi,l+u*e.hi];Math.hypot(s[0]-i[0],s[1]-i[1])i.pair===t&&Math.abs(i.ux*r-i.uy*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:r,ranges:[]},l.push(h));const d=(e[0]-h.origin[0])*h.ux+(e[1]-h.origin[1])*h.uy,p=(e[2]-h.origin[0])*h.ux+(e[3]-h.origin[1])*h.uy;h.ranges.push({lo:Math.min(d,p),hi:Math.max(d,p)})}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+a?t.hi=Math.max(t.hi,i.hi):e.push({...i})}for(const s of e){const e=[t.origin[0]+t.ux*s.lo,t.origin[1]+t.uy*s.lo],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])>=a&&h.push(Wa(e,n,i))}}return h}function al(t,e,i,s){const n=Va(t);if(!n.length)return[];if(e.length!==i.length)return n;const o=[];for(const t of n){const n=Ba(t,s),r=[n[0],n[1]],a=[n[2],n[3]];let l=r,c=a;for(let t=0;t{const e=Ka(t,[s[0],s[1],n[0],n[1]]);if(e.d>.001)return t;const i=h[0]-o[0],r=h[1]-o[1];return[o[0]+i*e.t,o[1]+r*e.t]};if(Ka([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],n[0],n[1]]).d<.01){l=d(r),c=d(a);break}}o.push(Wa(l,c,s))}return o}class ll{constructor(t,e=()=>Date.now()){this.onUpdate=t,this.now=e,this.signed={},this.queued=new Set,this.inFlight=new Map,this.retry=new Map,this.disposed=!1}start(t,e){this.disposed=!1,this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(t(),e()),288e5)}dispose(){this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.queued.clear(),this.inFlight.clear()}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(t,e){const i=ns(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],n=s?this.now()-s.at:1/0;return n{const e=[...this.queued];this.queued.clear(),this.sign(t,e)},30))}sign(t,e){if(e.length&&t?.callWS)for(const i of function(t,e){const i=Math.max(1,Math.floor(e)),s=[];for(let e=0;e{if(this.disposed)return;const e=this.now(),s={...this.signed};let n=0;for(const o of i){const i=t?.urls?.[o];"string"==typeof i&&i?(s[o]={url:i,at:e},this.retry.delete(o),n++):this.backOff(o)}n&&(this.signed=s,this.onUpdate())}).catch(()=>{for(const t of i)this.backOff(t)}).finally(()=>{for(const t of i)this.inFlight.get(t)===e&&this.inFlight.delete(t)})}}backOff(t){const e=this.retry.get(t)?.delay||0,i=Math.min(6e4,e?2*e:2e3);this.retry.set(t,{notBefore:this.now()+i,delay:i})}resign(t,e){const i=this.now(),s={};for(const[t,n]of Object.entries(this.signed))e.has(t)&&i-n.at{if(null==t||""===t)return null;const e=Number(t);return Number.isFinite(e)?e:null},pl=t=>{const e=t,i=dl(Array.isArray(e)?e[0]:e?.x),s=dl(Array.isArray(e)?e[1]:e?.y);return null==i||null==s?null:[i,s]};function ul(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=pl(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 _l(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=pl(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=ul(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 ml(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(pl).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),r=n.map(t=>s*t/o),a=r.map(Math.floor);let l=s-a.reduce((t,e)=>t+e,0);const c=r.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 vl(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=dl(e.x)&&null!=dl(e.y)?{x:dl(e.x),y:dl(e.y),a:dl(e.a??e.angle??e.theta)}:null,s=ml(t.path?.path??t.path?.points??t.path),n=[],o=t.rooms,r=Array.isArray(o)?o.map((t,e)=>[String(t?.id??e),t]):o&&"object"==typeof o?Object.entries(o):[];for(const[t,e]of r){if(!e||"object"!=typeof e)continue;const i=String(e.name??e.label??"").trim(),s=[[dl(e.cx),dl(e.cy)],[dl(e.center?.x),dl(e.center?.y)],[dl(e.x),dl(e.y)]].find(([t,e])=>null!=t&&null!=e);let o=s?.[0]??null,r=s?.[1]??null;const a=_l(e.outline),l=dl(e.x0),c=dl(e.y0),h=dl(e.x1),d=dl(e.y1),p=(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)||a?.bbox||null;if(null!=o&&null!=r||!a||([o,r]=a.centroid),null!=o&&null!=r||!p||(o=(p[0]+p[2])/2,r=(p[1]+p[3])/2),i&&null!=o&&null!=r){const e={id:t,name:i,cx:o,cy:r};p&&([e.x0,e.y0,e.x1,e.y1]=p),n.push(e)}}const a=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:a}:null}function yl(t,e,i){if(!t||!e)return null;const s=e.attributes||{},n=t.split(".")[0]||"",o=null!=i?.platform?String(i.platform):null,r=!!pl(s.vacuum_position??s.robot_position),a=!(!s.rooms||"object"!=typeof s.rooms),l=ml(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=a||l||c;let p=null;if(r?p="compatible":h?p="known_xcme_incomplete":d?p="partial":"camera"===n&&(p="camera"),!p)return null;const u=r?"camera"===n?300:200:h?100:d?50:0;return{entityId:t,name:String(s.friendly_name||t),platform:o,category:p,hasPosition:r,hasRooms:a,hasPath:l,hasMapId:c,score:u}}const bl=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function wl(t,e){const i=new Map(e.map(t=>[bl(t.name),t])),s=[],n=[];for(const e of t){const t=i.get(bl(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),n.push(e.name))}if(s.length<3)return null;const o=function(t){if(t.length<3)return null;let e=0,i=0,s=0,n=0,o=0,r=0,a=0,l=0,c=0,h=0,d=0,p=0;for(const[[u,_],[m,g]]of t){if(![u,_,m,g].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,n+=_*_,o+=_,r+=1,a+=u*m,l+=_*m,c+=m,h+=u*g,d+=_*g,p+=g}const u=[e,i,s,i,n,o,s,o,r],_=t=>{const[e,i,s,n,o,r,a,l,c]=u,h=e*(o*c-r*l)-i*(n*c-r*a)+s*(n*l-o*a);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-r*l)/h,(s*l-i*c)/h,(i*r-s*o)/h,(r*a-n*c)/h,(e*c-s*a)/h,(s*n-e*r)/h,(n*l-o*a)/h,(i*a-e*l)/h,(e*o-i*n)/h];return[d[0]*t[0]+d[1]*t[1]+d[2]*t[2],d[3]*t[0]+d[4]*t[1]+d[5]*t[2],d[6]*t[0]+d[7]*t[1]+d[8]*t[2]]},m=_([a,l,c]),g=_([h,d,p]);if(!m||!g)return null;const f=[m[0],m[1],m[2],g[0],g[1],g[2]];return f.every(Number.isFinite)?f:null}(s);return o?{matrix:o,matched:n,residual:hl(o,s)}:null}function kl(t,e,i){const s=t[t.length-1];if(s&&s[0]===e[0]&&s[1]===e[1])return t;if(t.push(e),t.length<=600)return t;let n=function(t,e){if(t.length<3)return t.slice();const i=new Uint8Array(t.length);i[0]=i[t.length-1]=1;const s=[[0,t.length-1]];for(;s.length;){const[n,o]=s.pop(),[r,a]=t[n],[l,c]=t[o],h=l-r,d=c-a,p=Math.hypot(h,d)||1e-9;let u=0,_=-1;for(let e=n+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([n,_],[_,o]))}const n=[];for(let e=0;e600&&(n=n.filter((t,e)=>e%2==0||e===n.length-1)),n}function $l(t){return"cleaning"===t||"returning"===t||"on"===t}const xl={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function Sl(t){const[e,i]=xl[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 Ml(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 Cl(t,e,i,s){const[n,o]=cl(Sl(e),i,s),r=Sl({...t,ox:0,oy:0}),[a,l]=cl(r,i,s);return{...t,ox:n-a,oy:o-l}}function Dl(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}class Tl extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return Bn(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:Gn(e,"editor.lang_auto")},{value:"en",label:Gn(e,"editor.lang_en")},{value:"ru",label:Gn(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:Gn(t,"editor.title"),default_floor:Gn(t,"editor.default_floor"),language:Gn(t,"editor.language"),icon_size:Gn(t,"editor.icon_size"),show_temperature:Gn(t,"editor.show_temperature"),live_states:Gn(t,"editor.live_states"),show_signal:Gn(t,"editor.show_signal"),kiosk:Gn(t,"editor.kiosk"),cycle:Gn(t,"editor.cycle")};return j`[t[0]-e[0],t[1]-e[1]],nn=(t,e)=>[t[0]+e[0],t[1]+e[1]],rn=(t,e)=>t[0]*e[0]+t[1]*e[1],an=t=>Math.hypot(t[0],t[1]);function ln(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function dn(t,e,i,s){const o=s||hn(t,e),n=(e+1)%t.length;return t.map((t,s)=>s===e||s===n?[t[0]+o[0]*i,t[1]+o[1]*i]:[...t])}function pn(t,e,i,s){const o=[],n=on(i,e),r=an(n);if(rs||h>s)continue;const d=rn(on(n,e),a),p=rn(on(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&o.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return o}function un(t,e,i,s,o){const n=pn(t,e,i,o);if(!n.length)return null;const r=t=>n.some(([e,i])=>cn(t,e,i)<=o),a=t.length,l=[];for(let n=0;no||f>o)continue;const v=rn(on(e,c),u),y=rn(on(i,c),u),b=Math.max(0,Math.min(v,y)),w=Math.min(p,Math.max(v,y));if(!(w-b<=o)){if(b>o&&bo&&wan(on(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const o=i[(s-1+i.length)%i.length],n=i[(s+1)%i.length],r=(t[0]-o[0])*(n[1]-o[1])-(t[1]-o[1])*(n[0]-o[0]),a=an(on(n,o))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function mn(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-o[0])*a[1]-(t[1]-o[1])*a[0],c=t=>(t[0]-o[0])*a[0]+(t[1]-o[1])*a[1],h=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const o=c(e);o<=h||o>=d||tMath.abs(r+(t-p)/g*(a-r));s=Math.min(s,f(_),f(m))}}return s}function fn(t,e,i){if(ei(t,e,i))return!0;if(ti(t,e,i)||ti(e,t,i))return!1;let s=0;try{const i=Re([[...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+=si(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function vn(t,e,i,s,o){const n=[i.n[0]*s,i.n[1]*s],r={polys:{},openings:{},movedSpans:{}};if(Math.abs(s)<1e-9)return r;for(const e of t){if(e.id===i.roomId){r.polys[e.id]=dn(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[nn(i.a,n),nn(i.b,n)]];continue}const t=pn(e.poly,i.a,i.b,o);if(!t.length)continue;const a=un(e.poly,i.a,i.b,n,o);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[nn(t,n),nn(e,n)]))}for(const t of e)cn([t.x,t.y],i.a,i.b)<=o&&(r.openings[t.id]=[t.x+n[0],t.y+n[1]]);return r}function yn(t,e,i){for(const s of e)for(let e=0;ei)continue;const c=(t.x-o[0])*l[0]+(t.y-o[1])*l[1];if(c-t.length/2>=-i&&c+t.length/2<=a+i)return!0}return!1}function bn(t,e,i){return t.filter(t=>e.some(e=>{for(let s=0;sa.polys[t.id]||t.poly;for(const e of l){const s=t.find(t=>t.id===e),o=a.polys[e];if(!mn(o))return!1;const l=ln(s.poly),h=ln(o);if(Math.abs(h)t.find(t=>t.id===e).poly),d=t.map(c);for(const t of bn(e,h,2*r)){const e=a.openings[t.id];if(!yn(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function kn(t,e,i,s,o,n){const r=t=>[s[0]+(t[0]-s[0])*o,s[1]+(t[1]-s[1])*o],a={poly:t.poly.map(r),openings:{}};for(const s of e){let e=!1;for(let i=0;i{for(let e=0;et.id===i);if(!l)return!1;const c=t.filter(t=>t.id!==i).map(t=>t.poly),h=kn(l,e,c,s,o,2*a),d=h.poly,p=function(t){const e=function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<3)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.pop(),o.pop(),s.concat(o)}(t);if(e.length<3)return 0;let i=1/0;for(let t=0;tt.id===i?d:t.poly);for(const t of bn(e,[l.poly],2*a)){const e=h.openings[t.id];if(!yn(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function xn(t,e,i){const s=i/e;return si(t)*s*s/1e4}function Sn(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const Mn=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],Cn=t=>Math.min(1,Math.max(0,t));function Dn(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=Mn[Mn.length-1][1];for(let t=1;t{const n=[t.x+o[0]*i*s,t.y+o[1]*i*s];return e.find(t=>t.poly.length>=3&&Be(n,t.poly))||null},r=n(1),a=n(-1);return r&&a?null:r||a?r?{normal:[-o[0],-o[1]],roomId:r.id}:{normal:o,roomId:a.id}:null}function Pn(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function Rn(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 zn(t,e){try{const i=Re([[...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 An(t,e,i,s,o,n,r){if(!(s>0))return[];const a=function(t,e){const i=function(t,e){return function(t){const e=t%360;return e<0?e+360:e}(t-e)}(t,e)*Math.PI/180;return[Math.sin(i),-Math.cos(i)]}(i,o),l=[-a[0],-a[1]],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=Tn(i,t);if(!e||!Pn(e.normal,a,s))continue;const o=t.find(t=>t.id===e.roomId);if(!o)continue;const d=n&&n[e.roomId]||o.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],m=Math.max(0,r?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(p)*u,y=Math.sin(p)*u,b=[g-v,f-y],w=[g+v,f+y],k=c*i.length,$=zn(Rn(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 In(t){return Math.round(10*(Number(t)||0))/10}const En=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Nn(t,e){const i=En(e?.north_deg);return null!==i?i:En(t?.north_deg)}function Fn(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function On(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Ln(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 Hn={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},qn=(t,e=1)=>{const i=Number(t);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):e},Un=t=>{let e=Number(t);return Number.isFinite(e)?(e=(e%360+360)%360,e>180?e-360:e):0};function jn(t,e,i){return Un(e)?[t.x,t.y]:i([t.x,t.y])}const Bn=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&s>0?s/o*i:0},Gn=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&i>0?s/i*o:0},Wn=(t,e,i,s=Hn.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return o;const n=Number(t?.width);return Number.isFinite(n)&&n>0?Gn(n,e,i):s};function Vn(t,e){return{color:t.color,opacity:qn(t.opacity),width_cm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),...e?{fill:t.fill,fill_color:t.fillColor,fill_opacity:qn(t.fillOpacity,.25)}:{}}}function Kn(t){const e=t.x+t.w/2,i=t.y+t.h/2,s=Un(t.angle)*Math.PI/180,o=Math.cos(s),n=Math.sin(s),r=(t,s)=>{const r=t-e,a=s-i;return[e+r*o-a*n,i+r*n+a*o]};return[r(t.x,t.y),r(t.x+t.w,t.y),r(t.x+t.w,t.y+t.h),r(t.x,t.y+t.h)]}function Yn(t){const e=Kn(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 Jn(t,e,i,s,o,n,r,a){const l=Un(t.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l),d=-Math.sin(l),p=Math.cos(l),u=t.x+t.w/2,_=t.y+t.h/2,m=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,f=u+m*c+g*d,v=_+m*h+g*p,y=s-f,b=o-v;let w=(y*c+b*h)*(e>0?1:-1),k=(y*d+b*p)*(i>0?1:-1),$=null;if(n){const e=w/Math.max(t.w,a),i=k/Math.max(t.h,a);$=Math.max(a/Math.max(t.w,a),a/Math.max(t.h,a),e,i),w=t.w*$,k=t.h*$}if(r>0)if(n){const e=Math.max(1,Math.round(t.w/r)),i=Math.max(1,Math.round(t.h/r));if(Math.abs(t.w-e*r)<1e-6&&Math.abs(t.h-i*r)<1e-6){const s=(t,e)=>{let i=Math.abs(t),s=Math.abs(e);for(;s;)[i,s]=[s,i%s];return Math.max(1,i)},o=1/s(e,i),n=Math.max(a/t.w,a/t.h),r=Math.max(n,Math.round(($??1)/o)*o);w=t.w*r,k=t.h*r}else{t.w>=t.h?(w=Math.round(w/r)*r,k=w*(t.h/Math.max(t.w,a))):(k=Math.round(k/r)*r,w=k*(t.w/Math.max(t.h,a)))}}else w=Math.round(w/r)*r,k=Math.round(k/r)*r;w=Math.max(a,w),k=Math.max(a,k);return{x:f+(e>0?w/2:-w/2)*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)*p-k/2,w:w,h:k,angle:Un(t.angle)||void 0}}const Zn=(t,e,i)=>{const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;if(n<1e-12)return[...e];const r=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*o)/n));return[e[0]+s*r,e[1]+o*r]};const Xn=15;function Qn(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function tr(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function er(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function ir(t,e,i){const s=Number(e)>0?Number(e):5;return tr(t)/s*i}function sr(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function or(t,e){let i=e[0]-t[0],s=e[1]-t[1];const o=Math.hypot(i,s);return o<1e-12?[1,0]:(i/=o,s/=o,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function nr(t,e,i){const s=sr((t[0]+e[0])/2,i),o=sr((t[1]+e[1])/2,i),[n,r]=or(t,e);let a=Math.atan2(r,n);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,c=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(c)},${o.toFixed(c)}@${l.toFixed(4)}`}function rr(t,e,i,s){return 1===s?nr(t,e,i):nr([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function ar(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 lr(t,e,i,s,o){const n=o>0?o:1;return{key:rr(t,e,s,n),cm:tr(i),a:[t[0]/n,t[1]/n],b:[e[0]/n,e[1]/n]}}function cr(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[o,n]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[o,n,r].every(Number.isFinite)&&s.push({w:e,x:o*i,y:n*i,ang:r})}return s}function hr(t,e){const[i,s]=or(t,e);let o=Math.atan2(s,i);return o<0&&(o+=Math.PI),o}function dr(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function pr(t,e,i,s,o=1){if(!t?.length)return null;const n=rr(e,i,s,o),r=t.find(t=>t.key===n);if(r)return r;const a=o>0?o:1,l=(e[0]+i[0])/2,c=(e[1]+i[1])/2,h=hr(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of cr(t,a))if(dr(e.ang,h)&&Math.hypot(e.x-l,e.y-c)<=d)return e.w;return null}function ur(t,e,i,s,o=1){const n=pr(t,e,i,s,o);return n&&n.cm>0?tr(n.cm):0}function _r(t,e,i,s=1,o=[]){if(!t?.length)return[];const n=new Set,r=Le(e);for(const t of r)n.add(rr([t[0],t[1]],[t[2],t[3]],i,s));const a=e||[],l=Math.max(i*s*.02,1e-9);for(let t=0;t(n.has(t.key)||(t=>{const e=ar(t,s);if(!e)return!1;const[i,n]=e,a=n[0]-i[0],c=n[1]-i[1],h=Math.hypot(a,c);if(h<=l)return!1;if(!r.some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];return dr(hr(i,n),hr(e,s))&&Ir(i[0],i[1],e[0],e[1],s[0],s[1])<=l&&Ir(n[0],n[1],e[0],e[1],s[0],s[1])<=l}))return!1;const d=(o||[]).some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];if(!dr(hr(i,n),hr(e,s)))return!1;const o=t=>Math.abs((t[0]-i[0])*c-(t[1]-i[1])*a)/h;if(o(e)>l||o(s)>l)return!1;const r=h*h,d=((e[0]-i[0])*a+(e[1]-i[1])*c)/r,p=((s[0]-i[0])*a+(s[1]-i[1])*c)/r;return Math.min(1,Math.max(d,p))-Math.max(0,Math.min(d,p))>l/h});return!d})(t))&&t.cm>=1&&t.cm<=100)}function mr(t,e,i,s=8){const[o,n]=or(t,e);let r=Math.atan2(n,o);r<0&&(r+=Math.PI);let a=i*Math.PI/180%Math.PI;a<0&&(a+=Math.PI);let l=Math.abs(r-a);return l>Math.PI/2&&(l=Math.PI-l),l<=s*Math.PI/180}function gr(t,e,i,s,o=1){if(!t?.length)return[];if(e.length!==i.length)return t.slice();const n=new Map;for(let t=0;t0?o:1,a=Math.max(.5*s,1e-9)*r,l=new Set,c=[];for(const o of t){let t="",h=null;const d=ar(o,r);if(d)for(let o=0;oa||Ir(d[1][0],d[1][1],n[0],n[1],l[0],l[1])>a)continue;const u=l[0]-n[0],_=l[1]-n[1],m=u*u+_*_;if(m<1e-18)continue;const g=t=>{const e=Math.max(0,Math.min(1,((t[0]-n[0])*u+(t[1]-n[1])*_)/m));return[c[0]+(p[0]-c[0])*e,c[1]+(p[1]-c[1])*e]};h=[g(d[0]),g(d[1])],t=rr(h[0],h[1],s,r);break}if(d||(t=n.get(o.key)||""),!t){const n=cr([o],r)[0];if(n)for(let o=0;o1.000001)continue;if(Ir(n.x,n.y,l[0],l[1],c[0],c[1])>a)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]=or(h,d),b=Math.max(s*r,1e-6);t=rr([g-v*b,f-y*b],[g+v*b,f+y*b],s,r);break}}t||(t=o.key),l.has(t)||(l.add(t),c.push(h?lr(h[0],h[1],o.cm,s,r):{...o,key:t,cm:tr(o.cm)}))}return c}function fr(t,e,i,s,o,n=1){const r=rr(e,i,o,n),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,lr(e,i,s,o,n)]}function vr(t,e,i,s,o,n=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,o=1,n=[]){const r=Dr(t,e,i,s,o,n);if(!r)return[];const a=[];for(let t=0;t0)||!t||t.length<2)return"";if(i&&t.length>=3){let i=t;const s=t[t.length-1];if(t.length>=4&&Math.hypot(t[0][0]-s[0],t[0][1]-s[1])<1e-9&&(i=t.slice(0,-1)),i.length>=3){const t=i.map(()=>e),s=Or(i,t),o=Sr(i,t);if(s&&o)return`${Mr(s)} ${Mr(Cr(o))}`}}let s="";for(let i=0;i.05)&&Ir(r,a,t[0],t[1],t[2],t[3])<=n)return!0}return!1}function wr(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function kr(t,e){const i=t[e],s=t[(e+1)%t.length],o=s[0]-i[0],n=s[1]-i[1],r=Math.hypot(o,n)||1;let a=-n/r,l=o/r;const c=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r+0)+n&&(i=!i)}return i}([c[0]+.001*a,c[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function $r(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 xr(t,e,i,s){const o=e[0]*s[1]-e[1]*s[0];if(Math.abs(o)<1e-12)return null;const n=[i[0]-t[0],i[1]-t[1]],r=(n[0]*s[1]-n[1]*s[0])/o;return[t[0]+r*e[0],t[1]+r*e[1]]}function Sr(t,e){const i=t?.length||0;if(i<3||e.length!==i)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);const s=[];for(let o=0;o0||d>0)){s.push([t[o][0],t[o][1]]);continue}if($r(b,w)){const e=t[o],i=[e[0]+p*h,e[1]+u*h],n=[e[0]+_*d,e[1]+m*d];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=xr(k,b,$,w),S=Math.max(h,d,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}h>0&&s.push([t[o][0]+p*h,t[o][1]+u*h]),d>0&&s.push([t[o][0]+_*d,t[o][1]+m*d]),h>0||d>0||s.push([t[o][0],t[o][1]])}return s.length>=3?s:null}function Mr(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),a=Fe(r);if(!a||a.length<3)return null;const l=wr(s,o),c=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Fe(i);if(t)for(const e of ss(a,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 n||[]){const e=ar(t,o);e&&c.push(e[0],e[1])}const h=[],d=[];for(let t=0;tl)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=o||r>=1-o||(n.some(t=>Math.abs(t-r)*s<=2*l)||n.push(r))}n.sort((t,e)=>t-e);for(const s of n)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:a}}function Tr(t,e,i,s){const o=e.poly.length,n=new Array(o).fill(0);if(!t?.length)return n;const r=new Set,a=[];for(let l=0;l0?(n[l]=tr(c.cm),r.add(c.key)):a.push(l)}if(!a.length)return n;const l=s>0?s:1,c=Math.max(.5*i,1e-9)*l,h=cr(t,l).filter(t=>t.w.cm>0),d=new Map;for(const t of a){const i=e.parent[t],s=d.get(i);s?s.push(t):d.set(i,[t])}for(const[t,i]of d){const s=e.orig[t],o=e.orig[(t+1)%e.orig.length],a=hr(s,o),d=(s[0]+o[0])/2,p=(s[1]+o[1])/2;let u=null;const _=Math.hypot(o[0]-s[0],o[1]-s[1]);for(const t of h){if(r.has(t.w.key))continue;if(!dr(t.ang,a))continue;const e=ar(t.w,l);let i=!1,n=0;if(e){if(!dr(hr(e[0],e[1]),a))continue;if(Ir(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>c||Ir(o[0],o[1],e[0][0],e[0][1],e[1][0],e[1][1])>c)continue;i=!0,n=Math.max(0,Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1])-_)}else{if(Ir(t.x,t.y,s[0],s[1],o[0],o[1])>c)continue;n=Math.hypot(t.x-d,t.y-p)}(!u||i&&!u.exact||i===u.exact&&nt?.id===e),o=Fe(s);if(!o)return[];const n=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Fe(s);if(t)for(const e of ss(o,t,i))n.push(e)}return n}(t,e,wr(o,a)),h=function(t,e,i,s,o){const n=wr(s,o),r=[];for(let a=0;aIr(h,d,t[0],t[1],t[2],t[3])<=n);r.push(p?"shared":"outer")}return r}(l.poly,c,s,o,a),d=Tr(i,l,o,a),p=d.map((t,e)=>h[e]&&t>0?ir(t,n,r)/2:0);return{...l,kinds:h,cms:d,offsets:p}}function Rr(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Pr(t,l.id,e,i,s,o,n,r);if(c)for(let t=0;t0&&!l.has(c.key)&&(l.add(c.key),a.push(c));const c=[];for(const a of t||[]){if(!a?.id)continue;const l=Pr(t,a.id,e,i,s,o,n,r);if(l)for(let t=0;t0)||null===l.kinds[i]){t++;continue}let n=t;for(;n+10&&c.push({a:[h[0],h[1]],b:[d[0],d[1]],key:rr(h,d,s,r),cm:o,len:p}),t=n+1}}}c.sort((t,e)=>e.len-t.len||t.key.localeCompare(e.key));const h=[],d=new Set,p=new Set,u=4*wr(s,r);for(const t of c){const e=a.filter(e=>!p.has(e.key)&&e.cm===t.cm&&dr(hr(e.a,e.b),hr(t.a,t.b))&&Ir(e.a[0],e.a[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u&&Ir(e.b[0],e.b[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u);if(e.length){for(const t of e)p.add(t.key);d.has(t.key)||(d.add(t.key),h.push(lr(t.a,t.b,t.cm,s,r)))}}for(const t of a)p.has(t.key)||d.has(t.key)||(d.add(t.key),h.push(lr(t.a,t.b,t.cm,s,r)));return h}function Ar(t,e,i,s,o,n,r,a=1){const l=wr(o,a),c=(s[0]+s[2])/2,h=(s[1]+s[3])/2,d=hr([s[0],s[1]],[s[2],s[3]]);let p=null;for(const s of Rr(t,e,i,o,n,r,a)){if(!dr(hr(s.a,s.b),d))continue;const t=Ir(c,h,s.a[0],s.a[1],s.b[0],s.b[1]);t>4*l||(!p||tt?.id===e),c=Fe(l);if(!c||c.length<3)return null;if(!i?.length)return c.map(t=>[t[0],t[1]]);const h=Pr(t,e,i,s,o,n,r,a);return h&&h.offsets.some(t=>t>0)&&Sr(h.poly,h.offsets)||c.map(t=>[t[0],t[1]])}function Nr(t){const e=t.map(t=>[t[0],t[1]]);return e.push([t[0][0],t[0][1]]),[e]}function Fr(t,e,i,s=[],o,n,r,a=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=Pr(t,s.id,e,i,o,n,r,a);if(!l||l.poly.length<3||!l.offsets.some(t=>t>0))continue;for(const t of l.offsets)t>0&&(h=Math.max(h,2*t));const d=Or(l.poly,l.offsets),p=Sr(l.poly,l.offsets);d&&c.push({outset:d,inset:p})}for(const t of l){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);if(e.length){const s=Math.min(Math.max(...e)-Math.min(...e),Math.max(...i)-Math.min(...i)),o=Math.min(...t.map((e,i)=>{const s=t[(i+1)%t.length];return Math.hypot(s[0]-e[0],s[1]-e[1])}));h=Math.max(h,t.length>16?s:o)}}const d=function(t,e,i,s,o,n,r){if(!e?.length||!i?.length)return[];const a=4*wr(s,r),l=new Map;for(const a of Rr(t,e,i,s,o,n,r))!a.open&&a.half>0&&!l.has(a.key)&&l.set(a.key,a);const c=[...l.values()];if(c.length<2)return[];const h=[];for(const t of i)for(const e of[[t[0],t[1]],[t[2],t[3]]])h.some(t=>Math.hypot(t[0]-e[0],t[1]-e[1])<=a)||h.push(e);const d=[],p=(t,e)=>{let i=0,s=0;if(Math.hypot(t.a[0]-e[0],t.a[1]-e[1])<=a)i=t.b[0]-t.a[0],s=t.b[1]-t.a[1];else{if(!(Math.hypot(t.b[0]-e[0],t.b[1]-e[1])<=a))return null;i=t.a[0]-t.b[0],s=t.a[1]-t.b[1]}const o=Math.hypot(i,s);return o>a?[i/o,s/o]:null};for(const t of h){const e=c.map(e=>({iv:e,u:p(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(r>0?[t.slice(),h,u,p]:[t.slice(),p,u,h])}}return d}(t,e,i,o,n,r,a),p=s.length?Hr(t,e,i,o,n,r,a):null;try{const t=t=>{const e=Nr(t.outset);return t.inset?ze(e,Nr(t.inset)):e};let e=c.length?t(c[0]):null;for(let i=1;i0))continue;const i=jr(p,t,!0);if(!i.negative&&!i.positive)continue;const s=t.angle*Math.PI/180,n=Math.cos(s),r=Math.sin(s),l=-r,c=n,d=t.length/2,u=1.25*Math.max(h,o*a),_=[[t.x-n*d-l*u,t.y-r*d-c*u],[t.x+n*d-l*u,t.y+r*d-c*u],[t.x+n*d+l*u,t.y+r*d+c*u],[t.x-n*d+l*u,t.y-r*d+c*u]];e&&(e=ze(e,Nr(_)))}for(const t of l)t.length<3||(e=e?Pe(e,Nr(t)):[Nr(t)]);const i=function(t){if(!t)return"";let e="";for(const i of t)if(Array.isArray(i))for(const t of i){if(!Array.isArray(t)||t.length<4)continue;const i=t.slice(0,t.length-1);i.length<3||(e+=(e?" ":"")+Mr(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:h,fillRule:"evenodd"}:null}catch{const s=function(t,e,i,s,o,n,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Pr(t,l.id,e,i,s,o,n,r);if(!c||c.poly.length<3||!c.offsets.some(t=>t>0))continue;const h=Or(c.poly,c.offsets),d=Sr(c.poly,c.offsets);if(!h||!d)continue;const p=`${Mr(h)} ${Mr(Cr(d))}`;let u="",_="outer",m=0,g=0;for(let t=0;t0){u=rr(c.poly[t],c.poly[(t+1)%c.poly.length],s,r),_=c.kinds[t]||"outer",m=c.cms[t],g=ir(m,o,n);break}a.push({d:p,key:u,kind:_,cm:m,depthUnits:g})}return a}(t,e,i,o,n,r,a),c=l.map(t=>Mr(t)).join(" ");return s.length||c?{d:[s.map(t=>t.d).join(" "),c].filter(Boolean).join(" "),depthUnits:h,fillRule:"nonzero"}:null}}function Or(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]]);Cr(t),e.slice().reverse();const s=[];for(let o=0;o0||a>0)){s.push([t[o][0],t[o][1]]);continue}if($r(b,w)){const e=t[o],i=[e[0]-l*r,e[1]-c*r],n=[e[0]-h*a,e[1]-d*a];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=xr(k,b,$,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[o][0]-l*r,t[o][1]-c*r]),a>0&&s.push([t[o][0]-h*a,t[o][1]-d*a])}return s.length>=3?s:null}function Lr(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){const c=Fe(l);if(c&&c.length>=3){const h=Pr(t,l.id,e,i,s,o,n,r),d=h&&h.offsets.some(t=>t>0)?Or(h.poly,h.offsets):null,p=d||c;a.push({poly:p.map(t=>t.join(",")).join(" ")})}else l&&null!=l.x&&null!=l.y&&null!=l.w&&null!=l.h&&a.push({rect:{x:l.x,y:l.y,w:l.w,h:l.h,rx:.03*Math.min(l.w,l.h)}})}return a}function Hr(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Pr(t,l.id,e,i,s,o,n,r);if(!c)continue;const h=Math.abs(si(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(!o.length)return{coverage:0,full:!1};let n=o[0][0],r=o[0][1],a=0,l=n<=e+s;for(let t=1;t=i-s,{coverage:a,full:l}}function Ur(t,e){return Number(e.full)-Number(t.full)||t.faceDistance-e.faceDistance||t.area-e.area||t.roomId.localeCompare(e.roomId)}function jr(t,e,i=!1){const s=Number(e?.x),o=Number(e?.y),n=Number(e?.angle),r=Number(e?.length);if(!([s,o,n,r].every(Number.isFinite)&&r>0))return{negative:null,positive:null};const a=n*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=-c,d=l,p=r/2,u=Math.max(1e-9,t.adjacencyEps),_=new Map;let m=0;for(const e of t.edges){if(i&&!(e.half>0))continue;if(!mr(e.a,e.b,n))continue;const[t,r]=or(e.a,e.b);if(Math.abs((s-e.a[0])*r-(o-e.a[1])*t)>u)continue;const a=(e.a[0]-s)*l+(e.a[1]-o)*c,g=(e.b[0]-s)*l+(e.b[1]-o)*c,f=Math.max(-p,Math.min(a,g)),v=Math.min(p,Math.max(a,g));if(v-f<=u)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-o)*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,r]},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=qr(t.pieces,-p,p,u);t.coverage=e.coverage,t.full=e.full}const g=t=>{const e=[..._.values()].filter(e=>e.side===t&&e.coverage>u);return e.sort(Ur),e[0]||null};return{negative:g(-1),positive:g(1)}}function Br(t,e){const i=jr(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 o=s[0],n=e.flip_v?-o.side:o.side,r=(-1===n?i.negative:i.positive)||o,a=[...r.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(!(a.half>0&&a.cm>0))return{ox:0,oy:0,cm:0,side:n};const l=e.angle*Math.PI/180,c=-Math.sin(l),h=Math.cos(l);return{ox:c*n*a.half,oy:h*n*a.half,cm:a.cm,side:n}}function Gr(t,e){const i=1e-9,s=[];for(const t of[...e].filter(t=>t.x1>t.x0&&t.half>0).sort((t,e)=>t.x0-e.x0||t.x1-e.x1||t.half-e.half)){const e=s[s.length-1];e&&t.x0<=e.x1+i&&Math.abs(t.half-e.half)<=i?e.x1=Math.max(e.x1,t.x1):s.push({...t})}const o=[];for(let e=0;e=o-l?i.push([s,o]):(t>s+l&&i.push([s,Math.min(o,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 a}function Vr(t,e){const i=new Map;return e.map(e=>function(t,e,i){const s=jr(t,e,!0),o=s.negative,n=s.positive;if(!o&&!n)return null;let r;if(o&&n)r=[{candidate:o,side:-1},{candidate:n,side:1}];else{const t=o||n;r=[{candidate:t,side:-1},{candidate:t,side:1}]}const a=r.map(({candidate:t,side:s})=>({candidate:t,side:s,pieces:Wr(e,s,t.pieces,i)})),l=a.map(({candidate:t,side:e,pieces:i})=>({side:e,roomId:t.roomId,d:Gr(e,i)})),c=a.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 Kr=150;function Yr(t){return Number.isFinite(t)?Math.max(1,Math.min(Kr,t)):1}function Jr(t){return((Number.isFinite(Number(t))?Number(t):0)%90+90)%90}const Zr=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 Xr(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 Qr(t,e,i,s,o){const n=e[0]-t[0],r=e[1]-t[1],a=Math.hypot(n,r);if(!(a>1e-9))return null;const l=ir(i,s,o)/2,c=-r/a*l,h=n/a*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 ta(t,e,i){const s=Number(e)>0?Number(e):5,o=Yr(t.cm)/s*i,n=t.center[0],r=t.center[1];if("circle"===t.shape){const t=o/2;return Array.from({length:96},(e,i)=>{const s=i/96*Math.PI*2;return[n+Math.cos(s)*t,r+Math.sin(s)*t]})}const a=o/2,l=Jr(t.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l);return[[-a,-a],[a,-a],[a,a],[-a,a]].map(([t,e])=>[n+t*c-e*h,r+t*h+e*c])}function ea(t,e,i){const s=[];for(let o=0;o+1t.length>=3).map(t=>Zr(t));return e.length?Pe(e[0],...e.slice(1)):null}catch{return null}}(e);if(i)return ze(Zr(t),i)}catch{}let i=[Zr(t)];for(const t of e)if(!(t.length<3))try{i=ze(i,Zr(t))}catch{}return i}function sa(t){let e=0;for(const i of t||[])if(i?.length){e+=si(i[0]||[]);for(let t=1;t=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]))}return e}function na(t,e,i){if(!(i>0))return[];const s=Math.max(1e-9,1e-9*i);if(t.some(t=>ra(e,t)||(t=>t.some((i,o)=>{const n=t[(o+1)%t.length],r=n[0]-i[0],a=n[1]-i[1],l=r*r+a*a;if(!(l>0))return Math.hypot(e[0]-i[0],e[1]-i[1])<=s;const c=Math.max(0,Math.min(1,((e[0]-i[0])*r+(e[1]-i[1])*a)/l));return Math.hypot(e[0]-(i[0]+c*r),e[1]-(i[1]+c*a))<=s}))(t)))return[Array.from({length:128},(t,s)=>{const o=s/128*Math.PI*2;return[e[0]+Math.cos(o)*i*1.01,e[1]+Math.sin(o)*i*1.01]})];const o=[];for(const n of t)if(!(n.length<2)){o.push(n);for(let t=0;ts&&u>s))continue;const _=Math.max(-1,Math.min(1,(l*h+c*d)/(p*u))),m=Math.sqrt(Math.max(0,(1+_)/2)),g=Math.max(2*i,1.02*i/Math.max(m,.001)),f=[e[0]+l/p*g,e[1]+c/p*g],v=[e[0]+h/u*g,e[1]+d/u*g];o.push([r,a,v,f])}}return o}function ra(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r||1e-12)+n&&(i=!i)}return i}function aa(t,e,i){if(Math.hypot(t.center[0]-e.center[0],t.center[1]-e.center[1])>i)return!1;if(Math.abs(Yr(t.cm)-Yr(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(Jr(t.angle)-Jr(e.angle));return Math.min(s,90-s)<=1e-6}const la=1e3;function ca(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,o=s>=1?e:e*s,n=s>=1?e/s:e;return{x:(e-o)/2,y:(e-n)/2,w:o,h:n}}const ha=.01,da=100;function pa(t,e=1e3){const i=ca(t?.plan_aspect,e),s=Number(t?.plan_scale),o=Number.isFinite(s)&&s>0?Math.min(da,Math.max(ha,s)):1,n=Number(t?.plan_scale_x),r=Number(t?.plan_scale_y),a=Number.isFinite(n)&&n>0?Math.min(da,Math.max(ha,n)):o,l=Number.isFinite(r)&&r>0?Math.min(da,Math.max(ha,r)):o,c=Number(t?.plan_x),h=Number(t?.plan_y),d=Un(t?.plan_angle);return{x:i.x+(Number.isFinite(c)?$a(c):0)*e,y:i.y+(Number.isFinite(h)?$a(h):0)*e,w:i.w*a,h:i.h*l,...d?{angle:d}:{}}}function ua(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 _a(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=la,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]*la,i[1]*e,i[2]*la,i[3]*e],bg:t.plan_url?{href:hs(t.plan_url),...pa(t,la)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...ua(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*la:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*la:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*la,t[1]*e]):void 0}}),room_drafts:(t.room_drafts||[]).map(t=>({id:t.id,points:(t.points||[]).map(t=>[t[0]*la,t[1]*e]),segments:(t.segments||[]).map(t=>({cm:Number(t.cm)}))})),partitions:(t.partitions||[]).map(t=>({id:t.id,a:[t.a[0]*la,t.a[1]*e],b:[t.b[0]*la,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]*la,t.center[1]*e],cm:Number(t.cm),..."circle"===t.shape?{}:{angle:Jr(t.angle)}}))}}):[]}const ma=5e3,ga=ma*la,fa=240,va=la/fa,ya=1/fa;function ba(t){if(!Number.isFinite(t))return t;const e=Math.round(t*fa/la)*la/fa;return Math.abs(e-t)<=1e-9*va?t:e}function wa(t){return{x:ba(t.x),y:ba(t.y)}}function ka(t){return Number.isFinite(t)?Math.min(ga,Math.max(-ga,t)):0}function $a(t){return Number.isFinite(t)?Math.min(ma,Math.max(-ma,t)):0}const xa=1/3,Sa=200;function Ma(t){let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t){const t=Number(n[0]),r=Number(n[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>o&&(o=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:o}}function Ca(t){return t.poly&&t.poly.length?Ma(t.poly):null==t.x||null==t.y?null:Ma([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function Da(t,e){const i=[];for(const e of t.rooms||[]){const t=Ca(e);t&&i.push(t)}if(t.bg){const e=Ma(Kn(t.bg));e&&i.push(e)}for(const t of e||[])if(Array.isArray(t)){const e=Ma([t]);e&&i.push(e)}else i.push(t);return i}const Ta=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},Pa=t=>{let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t)n.minXs&&(s=n.maxX),n.maxY>o&&(o=n.maxY);return e>s||i>o?null:{x:e,y:i,w:s-e,h:o-i}};function Ra(t,e){let{x:i,y:s,w:o,h:n}=t;o<30&&(i=i+o/2-100,o=Sa),n<30&&(s=s+n/2-100,n=Sa);const r=Math.max(o,n)*e;return{x:i-r,y:s-r,w:o+2*r,h:n+2*r}}function za(t,e={}){const i=e.pad??.05,s=e.k??10,o=e.minSpread??50,n=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=ga&&Math.abs(t.maxX)<=ga&&Math.abs(t.minY)<=ga&&Math.abs(t.maxY)<=ga);if(!n.length)return{core:null,all:null,outliers:0};const r=Pa(n);if(n.length<4){const t=Ra(r,i);return{core:t,all:t,outliers:0}}const a=n.map(t=>(t.minX+t.maxX)/2),l=n.map(t=>(t.minY+t.maxY)/2),c=Ta(a),h=Ta(l),d=n.map((t,e)=>Math.max(Math.abs(a[e]-c),Math.abs(l[e]-h))),p=Math.max(((t,e)=>t.length?t[Math.min(t.length-1,Math.max(0,Math.round(e*(t.length-1))))]:0)([...d].sort((t,e)=>t-e),.75),o),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,m=_&&_<=n.length*xa?n.filter((t,e)=>!u[e]):n;return{core:Ra(Pa(m)||r,i),all:Ra(r,i),outliers:m===n?0:_}}function Aa(t,e,i=.05){const s=za(Da(t,e),{pad:i});if(s.core)return s.core;const o=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,la,la];return{x:o[0],y:o[1],w:o[2],h:o[3]}}function Ia(t){const e=Aa(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function Ea(t){const e=[];for(const i of t.rooms||[]){const t=Ca(i);t&&e.push(t)}const i=za(e,{pad:0}).core;return i?Math.max(la,Math.min(ga,Math.max(i.w,i.h))):la}function Na(t,e,i,s=1){const o=Number(i),n=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(o)||o<=0?t*n:t*Ea(e)*n/o}const Fa=[1,2,5,10,20,50,100,200,500,1e3];function Oa(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function La(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 Ha=["furniture","appliance","sanitary","other"],qa=[{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]]}],Ua=new Map(qa.map(t=>[t.id,t]));function ja(t){return t&&Ua.get(t)||null}function Ba(t){return qa.filter(e=>e.group===t)}function Ga(t,e,i=va,s=1e3){const o=Number(e)>0?Number(e):5;return(Number(t)||0)/o*i/s}const Wa=5e-4,Va=ma;function Ka(t){return Number.isFinite(t)?Math.max(Wa,Math.min(Va,t)):Wa}const Ya=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function Ja(t,e,i){const s=ja(t);if(!(s&&e>0&&i>0))return"";const o=t=>Ya(t*e),n=t=>Ya(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}H${o(e+s)}V${n(i+a)}H${o(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}L${o(s)} ${n(a)}`)}else if("e"===t[0]){const[,s,a,l,c]=t;r.push(`M${o(s-l)} ${n(a)}A${Ya(l*e)} ${Ya(c*i)} 0 0 1 ${o(s+l)} ${n(a)}A${Ya(l*e)} ${Ya(c*i)} 0 0 1 ${o(s-l)} ${n(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${o(e[0])} ${n(e[1])}`;for(let t=2;t+1{let e=(t%360+360)%360;return e>180&&(e-=360),e};function Xa(t,e,i,s,o,n=0){let r=null,a=o;for(const o of s){const[s,l,c,h]=o,d=c-s,p=h-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let m=((t-s)*d+(e-l)*p)/u;m=Math.max(0,Math.min(1,m));let g=s+m*d,f=l+m*p;const v=Math.hypot(t-g,e-f);if(!(v0){const t=Math.round(m*_/n)*n;g=s+t/_*d,f=l+t/_*p}r={cx:g+y*(i/2),cy:f+b*(i/2),angle:Za(180*Math.atan2(-y,b)/Math.PI),dist:v}}return r}function Qa(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function tl(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function el(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function il(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!el(t.a)||!el(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],o=[Number(t.b[0]),Number(t.b[1])];Math.hypot(o[0]-s[0],o[1]-s[1])<.001||e.push({a:s,b:o})}return e}function sl(t,e){const i=e[0],s=e[1],o=e[2]-i,n=e[3]-s,r=o*o+n*n;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*o+(t[1]-s)*n)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*o,s+a*n];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function ol(t,e){return sl(t,e).q}function nl(t,e,i,s,o){const n=sl(t,e).q;let r=n,a=1/0;for(const t of i){const e=Math.hypot(n[0]-t[0],n[1]-t[1]);e<=o&&e0?s:1,m=Math.round(u/_)*_,g=Math.max(0,Math.min(p,m))/p;return[l+h*g,c+d*g]}function rl(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt?.id),r=il(e);if(r.length)return ml(r,t,i,s).map(t=>tl(t,i));if(!o)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;tQa([t[0],t[1]],[t[2],t[3]],e))}function cl(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const o=(e||[]).filter(t=>t?.id);new Map(o.map(t=>[t.id,t]));const n=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=n.get(t),s=n.get(e);i&&s&&((i.open_to||[]).includes(e)||(i.open_to=[...i.open_to||[],e]),(s.open_to||[]).includes(t)||(s.open_to=[...s.open_to||[],t]))};for(let t=0;tgs(i,t)<4*s)){r(o[t].id,o[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}function hl(t,e,i,s=0){const o=e[2]-e[0],n=e[3]-e[1],r=Math.hypot(o,n);if(!(r>1e-9))return null;const a=o/r,l=n/r,c=(t[0]-e[0])*a+(t[1]-e[1])*l;if(c<-s||c>r+s)return null;return Math.abs((t[0]-e[0])*l-(t[1]-e[1])*a)>i?null:gs(t,e)}function dl(t,e,i){const s=t[2]-t[0],o=t[3]-t[1],n=e[2]-e[0],r=e[3]-e[1],a=Math.hypot(s,o),l=Math.hypot(n,r);return a>1e-9&&l>1e-9&&(!(Math.abs(s/a*(r/l)-o/a*(n/l))>1e-6)&&(gs([e[0],e[1]],t)<=4*i||gs([t[0],t[1]],e)<=4*i))}function pl(t,e,i){if(!t.length)return null;t.sort((t,e)=>t.distance-e.distance);const s=t[0];for(let o=1;oe)break;if(n.semantic!==s.semantic||!dl(s.seg,n.seg,i))return"ambiguous"}return s}function ul(t,e,i,s,o=1,n=15){const r=function(t,e,i,s,o=1,n=15){const[r,a]=or([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,n]=or([e[0],e[1]],[e[2],e[3]]);if(Math.abs(r*n-a*i)>.05)continue;const d=ur(t,[e[0],e[1]],[e[2],e[3]],s,o);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-h[0],p[1]-h[1]);u0?l:n}(t,e,i,s,o,n);return fr(t,[e[0],e[1]],[e[2],e[3]],r,s,o)}function _l(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],o=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const n of e)if(!(gs(o,n)>Math.hypot(t[2]-t[0],t[3]-t[1])&&gs([n[0],n[1]],t)>i))for(const e of[[n[0],n[1]],[n[2],n[3]]])gs(e,t)<=2*i&&s.push(e);return s}function ml(t,e,i,s){const o=il(t);if(!o.length)return[];const n=rl(e,s);if(!n.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of o){const e=tl(t,i),o=e[0],l=e[1],c=e[2]-o,h=e[3]-l,d=Math.hypot(c,h);if(d4*s||n>4*s)continue;const r=(t[0]-o)*p+(t[1]-l)*u,c=(t[2]-o)*p+(t[3]-l)*u,h=Math.max(0,Math.min(r,c)),m=Math.min(d,Math.max(r,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+a?e.hi=Math.max(e.hi,t.hi):i.push({...t})}for(const e of i){const i=[o+p*e.lo,l+u*e.lo],s=[o+p*e.hi,l+u*e.hi];Math.hypot(s[0]-i[0],s[1]-i[1])i.pair===t&&Math.abs(i.ux*r-i.uy*n)<=1e-6&&Math.abs((e[0]-i.origin[0])*i.uy-(e[1]-i.origin[1])*i.ux)<=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:n,uy:r,ranges:[]},l.push(h));const d=(e[0]-h.origin[0])*h.ux+(e[1]-h.origin[1])*h.uy,p=(e[2]-h.origin[0])*h.ux+(e[3]-h.origin[1])*h.uy;h.ranges.push({lo:Math.min(d,p),hi:Math.max(d,p)})}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+a?t.hi=Math.max(t.hi,i.hi):e.push({...i})}for(const s of e){const e=[t.origin[0]+t.ux*s.lo,t.origin[1]+t.uy*s.lo],o=[t.origin[0]+t.ux*s.hi,t.origin[1]+t.uy*s.hi];Math.hypot(o[0]-e[0],o[1]-e[1])>=a&&h.push(Qa(e,o,i))}}return h}function gl(t,e,i,s){const o=il(t);if(!o.length)return[];if(e.length!==i.length)return o;const n=[];for(const t of o){const o=tl(t,s),r=[o[0],o[1]],a=[o[2],o[3]];let l=r,c=a;for(let t=0;t{const e=sl(t,[s[0],s[1],o[0],o[1]]);if(e.d>.001)return t;const i=h[0]-n[0],r=h[1]-n[1];return[n[0]+i*e.t,n[1]+r*e.t]};if(sl([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],o[0],o[1]]).d<.01){l=d(r),c=d(a);break}}n.push(Qa(l,c,s))}return n}class fl{constructor(t,e=()=>Date.now()){this.onUpdate=t,this.now=e,this.signed={},this.queued=new Set,this.inFlight=new Map,this.retry=new Map,this.disposed=!1}start(t,e){this.disposed=!1,this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(t(),e()),288e5)}dispose(){this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.queued.clear(),this.inFlight.clear()}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(t,e){const i=hs(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],o=s?this.now()-s.at:1/0;return o{const e=[...this.queued];this.queued.clear(),this.sign(t,e)},30))}sign(t,e){if(e.length&&t?.callWS)for(const i of function(t,e){const i=Math.max(1,Math.floor(e)),s=[];for(let e=0;e{if(this.disposed)return;const e=this.now(),s={...this.signed};let o=0;for(const n of i){const i=t?.urls?.[n];"string"==typeof i&&i?(s[n]={url:i,at:e},this.retry.delete(n),o++):this.backOff(n)}o&&(this.signed=s,this.onUpdate())}).catch(()=>{for(const t of i)this.backOff(t)}).finally(()=>{for(const t of i)this.inFlight.get(t)===e&&this.inFlight.delete(t)})}}backOff(t){const e=this.retry.get(t)?.delay||0,i=Math.min(6e4,e?2*e:2e3);this.retry.set(t,{notBefore:this.now()+i,delay:i})}resign(t,e){const i=this.now(),s={};for(const[t,o]of Object.entries(this.signed))e.has(t)&&i-o.at{if(null==t||""===t)return null;const e=Number(t);return Number.isFinite(e)?e:null},wl=t=>{const e=t,i=bl(Array.isArray(e)?e[0]:e?.x),s=bl(Array.isArray(e)?e[1]:e?.y);return null==i||null==s?null:[i,s]};function kl(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=wl(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,o=0;for(let t=0;tt+e[0],0)/e.length,e.reduce((t,e)=>t+e[1],0)/e.length]:[s/(3*i),o/(3*i)]}function $l(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=wl(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=kl(e);if(!i)return null;const s=e.map(t=>t[0]),o=e.map(t=>t[1]);return{centroid:i,bbox:[Math.min(...s),Math.min(...o),Math.max(...s),Math.max(...o)]}}function xl(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(wl).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,o=i.map(t=>t.length-2),n=o.reduce((t,e)=>t+e,0),r=o.map(t=>s*t/n),a=r.map(Math.floor);let l=s-a.reduce((t,e)=>t+e,0);const c=r.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 Cl(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=bl(e.x)&&null!=bl(e.y)?{x:bl(e.x),y:bl(e.y),a:bl(e.a??e.angle??e.theta)}:null,s=xl(t.path?.path??t.path?.points??t.path),o=[],n=t.rooms,r=Array.isArray(n)?n.map((t,e)=>[String(t?.id??e),t]):n&&"object"==typeof n?Object.entries(n):[];for(const[t,e]of r){if(!e||"object"!=typeof e)continue;const i=String(e.name??e.label??"").trim(),s=[[bl(e.cx),bl(e.cy)],[bl(e.center?.x),bl(e.center?.y)],[bl(e.x),bl(e.y)]].find(([t,e])=>null!=t&&null!=e);let n=s?.[0]??null,r=s?.[1]??null;const a=$l(e.outline),l=bl(e.x0),c=bl(e.y0),h=bl(e.x1),d=bl(e.y1),p=(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)||a?.bbox||null;if(null!=n&&null!=r||!a||([n,r]=a.centroid),null!=n&&null!=r||!p||(n=(p[0]+p[2])/2,r=(p[1]+p[3])/2),i&&null!=n&&null!=r){const e={id:t,name:i,cx:n,cy:r};p&&([e.x0,e.y0,e.x1,e.y1]=p),o.push(e)}}const a=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||o.length||s.length?{pos:i,path:s,rooms:o,mapId:a}:null}function Dl(t,e,i){if(!t||!e)return null;const s=e.attributes||{},o=t.split(".")[0]||"",n=null!=i?.platform?String(i.platform):null,r=!!wl(s.vacuum_position??s.robot_position),a=!(!s.rooms||"object"!=typeof s.rooms),l=xl(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"===n,d=a||l||c;let p=null;if(r?p="compatible":h?p="known_xcme_incomplete":d?p="partial":"camera"===o&&(p="camera"),!p)return null;const u=r?"camera"===o?300:200:h?100:d?50:0;return{entityId:t,name:String(s.friendly_name||t),platform:n,category:p,hasPosition:r,hasRooms:a,hasPath:l,hasMapId:c,score:u}}const Tl=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function Pl(t,e){const i=new Map(e.map(t=>[Tl(t.name),t])),s=[],o=[];for(const e of t){const t=i.get(Tl(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),o.push(e.name))}if(s.length<3)return null;const n=function(t){if(t.length<3)return null;let e=0,i=0,s=0,o=0,n=0,r=0,a=0,l=0,c=0,h=0,d=0,p=0;for(const[[u,_],[m,g]]of t){if(![u,_,m,g].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,o+=_*_,n+=_,r+=1,a+=u*m,l+=_*m,c+=m,h+=u*g,d+=_*g,p+=g}const u=[e,i,s,i,o,n,s,n,r],_=t=>{const[e,i,s,o,n,r,a,l,c]=u,h=e*(n*c-r*l)-i*(o*c-r*a)+s*(o*l-n*a);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(n*c-r*l)/h,(s*l-i*c)/h,(i*r-s*n)/h,(r*a-o*c)/h,(e*c-s*a)/h,(s*o-e*r)/h,(o*l-n*a)/h,(i*a-e*l)/h,(e*n-i*o)/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=_([a,l,c]),g=_([h,d,p]);if(!m||!g)return null;const f=[m[0],m[1],m[2],g[0],g[1],g[2]];return f.every(Number.isFinite)?f:null}(s);return n?{matrix:n,matched:o,residual:yl(n,s)}:null}function Rl(t,e,i){const s=t[t.length-1];if(s&&s[0]===e[0]&&s[1]===e[1])return t;if(t.push(e),t.length<=600)return t;let o=function(t,e){if(t.length<3)return t.slice();const i=new Uint8Array(t.length);i[0]=i[t.length-1]=1;const s=[[0,t.length-1]];for(;s.length;){const[o,n]=s.pop(),[r,a]=t[o],[l,c]=t[n],h=l-r,d=c-a,p=Math.hypot(h,d)||1e-9;let u=0,_=-1;for(let e=o+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([o,_],[_,n]))}const o=[];for(let e=0;e600&&(o=o.filter((t,e)=>e%2==0||e===o.length-1)),o}function zl(t){return"cleaning"===t||"returning"===t||"on"===t}const Al={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function Il(t){const[e,i]=Al[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 El(t){const e=t[0]*t[4]-t[1]*t[3];if(!Number.isFinite(e)||Math.abs(e)<1e-12)return null;const i=e<0,s=Math.sqrt(Math.abs(e));let o=180*Math.atan2(-t[1],t[4])/Math.PI;return o=(90*Math.round(o/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:o,mir:i}}function Nl(t,e,i,s){const[o,n]=vl(Il(e),i,s),r=Il({...t,ox:0,oy:0}),[a,l]=vl(r,i,s);return{...t,ox:o-a,oy:n-l}}function Fl(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}class Ol extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return tn(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:en(e,"editor.lang_auto")},{value:"en",label:en(e,"editor.lang_en")},{value:"ru",label:en(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 W;this._loadSpaces();const t=this._lang,e={title:en(t,"editor.title"),default_floor:en(t,"editor.default_floor"),language:en(t,"editor.language"),icon_size:en(t,"editor.icon_size"),show_temperature:en(t,"editor.show_temperature"),live_states:en(t,"editor.live_states"),show_signal:en(t,"editor.show_signal"),kiosk:en(t,"editor.kiosk"),cycle:en(t,"editor.cycle")};return j`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 Pl(t){const e=oa(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):kt,n=xt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:$t);return yn({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=>Gn(t.lang,e),iconRules:n})}function Rl(t){const e=oa(t.cfg).find(e=>e.id===t.spaceId);if(!e)return null;const i=Fi(t.cfg.spaces.find(e=>e.id===t.spaceId)),s=Hi(t.cfg.settings),n=t.iconSize??2.5,o=n>8?2.5:n,r=t.registry?Ws(t.hass,t.registry):t.hass,a=(t.devices||Pl(t)).filter(e=>e.space===t.spaceId),l=a.filter(t=>!t.hidden),c=function(t,e,i){const s={},n=i/100*Ma(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=Ta(i),r=.1*Math.min(o.w,o.h),a=o.w-2*r,l=o.h-2*r,c=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),h=a/c,d=l/Math.max(Math.ceil(e.length/c),1),p=e.map((t,e)=>({x:o.x+r+h*(e%c+.5),y:o.y+r+d*(Math.floor(e/c)+.5)}));hi(p,o,n,.5*r),e.forEach((t,e)=>s[t.id]=pa(p[e]))}return s}(a,e,o),h=[];for(const e of l){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*Qr,e=i.y*Qr;h.push({minX:t,minY:e,maxX:t,maxY:e})}}const d=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},p=Array.isArray(d.walls)?d.walls:[],u=Number(d.cell_cm)>0?Number(d.cell_cm):5,_=function(t,e,i){const s=[];for(const n of t.partitions||[]){const t=Wr(n.a,n.b,n.cm,e,i);t&&s.push(t)}for(const n of t.room_drafts||[])s.push(...Gr(n,e,i));for(const n of t.wall_columns||[])s.push(Br(n,e,i));return s}(e,u,ca);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=xa(e,h),g=[m.x,m.y,m.w,m.h],f=new Map(e.rooms.map(t=>{const e=os(i.fill,t);return[t,Ui(e,"lqi"===e&&t.area?kn(r,a,t.area):null,"light"===e?ln(an(r,a,t)):"none","temp"===e&&t.area?$n(r,a,t.area):null,i.tempMin,i.tempMax,s,Ni(i.customFill,t))]})),v=e.rooms.filter(t=>t.area||i.showBorders||"none"!==os(i.fill,t)).map(t=>{let s="room "+(e.bg?"overlay":"yard"),n="";const o=os(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 r=t.id||G,a=t.area||G,l=t.poly?W``:W``;return l}),y=e.rooms.filter(t=>{const e=f.get(t);return rs(i.glow,t)&&(!e||e.opacity<=0)}).map(t=>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 Ll(t){const e=_a(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):kt,o=xt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:$t);return Do({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=>en(t.lang,e),iconRules:o})}function Hl(t){const e=_a(t.cfg).find(e=>e.id===t.spaceId);if(!e)return null;const i=Fi(t.cfg.spaces.find(e=>e.id===t.spaceId)),s=Hi(t.cfg.settings),o=t.iconSize??2.5,n=o>8?2.5:o,r=t.registry?Js(t.hass,t.registry):t.hass,a=(t.devices||Ll(t)).filter(e=>e.space===t.spaceId),l=a.filter(t=>!t.hidden),c=function(t,e,i){const s={},o=i/100*Ea(e)*1.3;for(const i of e.rooms){if(!i.area)continue;const e=t.filter(t=>t.area===i.area);if(!e.length)continue;const n=Oa(i),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,c=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),h=a/c,d=l/Math.max(Math.ceil(e.length/c),1),p=e.map((t,e)=>({x:n.x+r+h*(e%c+.5),y:n.y+r+d*(Math.floor(e/c)+.5)}));hi(p,n,o,.5*r),e.forEach((t,e)=>s[t.id]=wa(p[e]))}return s}(a,e,n),h=[];for(const e of l){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*la,e=i.y*la;h.push({minX:t,minY:e,maxX:t,maxY:e})}}const d=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},p=Array.isArray(d.walls)?d.walls:[],u=Number(d.cell_cm)>0?Number(d.cell_cm):5,_=function(t,e,i){const s=[];for(const o of t.partitions||[]){const t=Qr(o.a,o.b,o.cm,e,i);t&&s.push(t)}for(const o of t.room_drafts||[])s.push(...ea(o,e,i));for(const o of t.wall_columns||[])s.push(ta(o,e,i));return s}(e,u,va);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=Aa(e,h),g=[m.x,m.y,m.w,m.h],f=new Map(e.rooms.map(t=>{const e=ds(i.fill,t);return[t,Ui(e,"lqi"===e&&t.area?Ro(r,a,t.area):null,"light"===e?fo(mo(r,a,t)):"none","temp"===e&&t.area?zo(r,a,t.area):null,i.tempMin,i.tempMax,s,Ni(i.customFill,t))]})),v=e.rooms.filter(t=>t.area||i.showBorders||"none"!==ds(i.fill,t)).map(t=>{let s="room "+(e.bg?"overlay":"yard"),o="";const n=ds(i.fill,t);if(i.showBorders||"none"!==n){s+=" styled";const e=[`--room-stroke:${i.color}`,`--room-stroke-op:${i.showBorders?i.opacity:0}`],n=f.get(t)||null;n?(s+=" filled",e.push(`--room-fill:${n.color}`,`--room-fill-op:${n.opacity.toFixed(3)}`)):e.push("--room-fill:transparent","--room-fill-op:0"),o=e.join(";")}const r=t.id||W,a=t.area||W,l=t.poly?B``:B``;return l}),y=e.rooms.filter(t=>{const e=f.get(t);return ps(i.glow,t)&&(!e||e.opacity<=0)}).map(t=>t.poly?B``:W``:W}`}if("ellipse"===o.kind){const i=(o.x+o.w/2)*t,h=(o.y+o.h/2)*e,d=Un(o.angle);return B` - ${s?W``:G}`}if("furniture"===n.kind){const i=n.w*t,h=n.h*e,d=qa(n.symbol,i,h);if(!d)return G;const p=Number(n.angle)||0,u=n.x*t+i/2,_=n.y*e+h/2,m=`${p?`rotate(${p} ${u} ${_}) `:""}translate(${n.x*t} ${n.y*e})`;return W``:W}`}if("furniture"===o.kind){const i=o.w*t,h=o.h*e,d=Ja(o.symbol,i,h);if(!d)return W;const p=Number(o.angle)||0,u=o.x*t+i/2,_=o.y*e+h/2,m=`${p?`rotate(${p} ${u} ${_}) `:""}translate(${o.x*t} ${o.y*e})`;return B` - ${s?W``:G}`}if("text"===n.kind){const i=this._decorTextUnits(n),s=function(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}(function(t,e,i,s=()=>!0){const n=t??"";let o=!1;const r=n.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const n=xi(e);return n?(o=!0,s(n.entity||"")?Di(i,n):ki):t});if(o)return r;const a=(e?.entity||"").trim();if(!a)return n;if(!s(a)){const t=n.indexOf($i);return t>=0?n.slice(0,t)+ki+n.slice(t+2):n?`${n} ${ki}`:ki}const l=Di(i,e),c=n.indexOf($i);return c>=0?n.slice(0,c)+l+n.slice(c+2):n?`${n} ${l}`:l}(n.text,n,this.hass,t=>this._planEntityAvailable(t))),a=n.x*t,h=n.y*e,d=Number(n.angle)||0,p=h-(s.length-1)*i*1.2/2;return W``:W}`}if("text"===o.kind){const i=this._decorTextUnits(o),s=function(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}(function(t,e,i,s=()=>!0){const o=t??"";let n=!1;const r=o.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const o=xi(e);return o?(n=!0,s(o.entity||"")?Di(i,o):ki):t});if(n)return r;const a=(e?.entity||"").trim();if(!a)return o;if(!s(a)){const t=o.indexOf($i);return t>=0?o.slice(0,t)+ki+o.slice(t+2):o?`${o} ${ki}`:ki}const l=Di(i,e),c=o.indexOf($i);return c>=0?o.slice(0,c)+l+o.slice(c+2):o?`${o} ${l}`:l}(o.text,o,this.hass,t=>this._planEntityAvailable(t))),a=o.x*t,h=o.y*e,d=Number(o.angle)||0,p=h-(s.length-1)*i*1.2/2;return B`${s.map((t,e)=>W`${t}`)}`}return G});let o=G;const r=this._decorDraft;if(r){const t=this._decorStyle,e=Eo(t.widthCm,this._cellCm,this._gridPitch);if("line"===r.kind)o=W``;else{const i=Math.min(r.a[0],r.b[0]),s=Math.min(r.a[1],r.b[1]),n=Math.abs(r.b[0]-r.a[0]),a=Math.abs(r.b[1]-r.a[1]);o="rect"===r.kind?W``:W``}}return W`${n}${o}`}get _editorToolbarGroups(){return[]}_renderEditorGroupLauncher(t){return this._editorSecondary.renderGroupLauncher(t,this._editorToolbarGroups,this._editorSecondaryCopy)}get _editorSecondaryContextId(){const t=`editor:${this._mode}:${this._space}:${this._cfgEpoch}`,e=this._editorSecondary.activeGroup(this._editorToolbarGroups);if(e)return`${t}:group:${e.id}:${this._editorSecondary.groupGeneration}`;if("plan"===this._mode){const e=this._physicalSel;return e?`${t}:selection:${e.kind}:${e.id}:${e.segment??""}`:`${t}:tool:${this._tool}:${this._path.length}:${this._openWallAnchor?1:0}`}return"decor"===this._mode?"furniture"===this._decorTool?`${t}:palette:furniture:${this._furnPalette?.symbol||"none"}`:"select"===this._decorTool&&this._decorSel?`${t}:selection:decor:${this._decorSel}`:`${t}:tool:${this._decorTool}:${this._bdMoved?1:0}:${this._bdDrag?1:0}`:`${t}:none`}_runEditorContext(t,e){this._editorSecondary.runContext(t,this._editorSecondaryContextId,e)}_renderEditorGroupModel(t){return this._editorSecondary.renderGroupModel(t,this._editorSecondaryContextId,this._editorSecondaryCopy)}_renderDrawWallControl(){return j`
`:W} + `:W} ${this._kiosk&&this._kioskDots&&this._model.length>1?j`
${this._model.map(t=>j``)} -
`:G} - ${this._kioskDialog?this._renderKioskDialog():G} + `:W} + ${this._kioskDialog?this._renderKioskDialog():W} ${this._vacFit?j`
${this._t("vac.fit_hint")} -
`:G} + `:W} ${this._tapConfirm?j`this._tapConfirm=null}>

${this._tapConfirm.text}

@@ -4556,30 +4587,30 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f ${this._t("btn.run")} -
`:G} - ${this._toast?j``:G} + `:W} + ${this._toast?j``:W} - `}_vacCandidateStatus(t,e){const i=this._bindingStatus("entity:"+t);if("ha_disabled"===i.kind)return"disabled";if("orphaned"===i.kind)return"missing";if("unverified"===i.kind)return"unverified";const s=this.hass?.states?.[t];return"unavailable"===s?.state?"unavailable":e?.hasPosition?"ok":"unsupported"}_vacOpenAllCameras(t){const e=this._haRegistry.entities||{},i=[];for(const[t,s]of Object.entries(this.hass?.states||{})){if(!t.startsWith("camera."))continue;const n=yl(t,s,e[t]);n&&i.push(n)}this._vacAllCameraCache={devId:t.id,candidates:i},this._vacAllCamerasFor=t.id}_vacSourceResolution(t,e=!1){const i=t.marker?.vacuum,s="string"==typeof i?.source&&!!i.source,n=new Set(t.entities||[]),o=new Set(n);s&&o.add(i.source);const r=this._haRegistry.entities||{},a=[];for(const t of o){const e=this.hass?.states?.[t],n=yl(t,e,r[t]);n?a.push(n):s&&t===i.source&&a.push({entityId:t,name:String(e?.attributes?.friendly_name||t),platform:r[t]?.platform?String(r[t].platform):null,category:t.startsWith("camera.")?"camera":"partial",hasPosition:!1,hasRooms:!1,hasPath:!1,hasMapId:!1,score:0})}if(e&&this._vacAllCameraCache?.devId===t.id){const t=new Set(a.map(t=>t.entityId));for(const e of this._vacAllCameraCache.candidates)t.has(e.entityId)||a.push(e)}const l={};for(const t of a)l[t.entityId]=this._vacCandidateStatus(t.entityId,t);return function(t,e,i,s){const n="string"==typeof t&&t.length>0,o=new Set(e),r=Array.from(i).sort((e,i)=>n&&e.entityId===t?-1:n&&i.entityId===t?1:i.score-e.score||e.entityId.localeCompare(i.entityId));if(n){const e=r.find(e=>e.entityId===t);return{entityId:t,status:s[t]||(e?.hasPosition?"ok":"unverified"),pinned:!0,candidates:r}}const a=r.find(t=>o.has(t.entityId)&&t.hasPosition&&"ok"===s[t.entityId]);return a?{entityId:a.entityId,status:"ok",pinned:!1,candidates:r}:{entityId:null,status:"none",pinned:!1,candidates:r}}(i?.source,n,a,l)}_vacSource(t){if(!1===t.marker?.vacuum?.live)return null;const e=this._vacSourceResolution(t);return"ok"===e.status||"unsupported"===e.status?e.entityId:null}_vacEntity(t){return t.primary?.startsWith("vacuum.")?t.primary:(t.entities||[]).find(t=>t.startsWith("vacuum."))||null}_isVacDev(t){return!!this._vacEntity(t)}_activitySourceKey(t){return Rn(this._planHass,t,!1!==this._config?.show_temperature)}_activitySnapshot(t){const e=Dn(this._planHass,t);return{samples:e.samples,sourceKey:Rn(this._planHass,t,!1!==this._config?.show_temperature,e)}}_stampActivity(t,e,i){let s=this._activityRt.get(t);s||(s={sources:i||"",last:{},flashTs:0,flashKind:null,timer:0,gen:0},this._activityRt.set(t,s)),null!=i&&(s.sources=i),s.flashTs&&Date.now()-s.flashTs<3300&&"event"===s.flashKind&&"transition"===e||(s.flashTs=Date.now(),s.flashKind=e,s.gen++,clearTimeout(s.timer),s.timer=window.setTimeout(()=>this.requestUpdate(),3360))}_syncActivityRuntime(){const t=new Map;if(!this.hass)return t;const e=new Set;for(const i of this._devices){if(i.hidden)continue;if("static_icon"===ui(i.marker?.display))continue;e.add(i.id);const s=this._activitySnapshot(i);t.set(i.id,s);const{samples:n,sourceKey:o}=s;let r=this._activityRt.get(i.id);if(r){if(r.sources!==o){clearTimeout(r.timer),r.sources=o,r.last={},r.flashTs=0,r.flashKind=null;for(const t of n)r.last[t.eid]=t.state}}else{r={sources:o,last:{},flashTs:0,flashKind:null,timer:0,gen:0};for(const t of n)r.last[t.eid]=t.state;this._activityRt.set(i.id,r)}}for(const[t,i]of this._activityRt)e.has(t)||(clearTimeout(i.timer),this._activityRt.delete(t));return t}_activityTick(){if(!this.hass)return;const t=this._syncActivityRuntime();for(const e of this._devices){if(e.hidden)continue;if("static_icon"===ui(e.marker?.display))continue;const i=t.get(e.id)||this._activitySnapshot(e),{samples:s,sourceKey:n}=i,o=this._activityRt.get(e.id);if(!o||o.sources!==n)continue;"transition"===o.flashKind&&s.some(t=>"transition"===t.activity)&&(clearTimeout(o.timer),o.flashTs=0,o.flashKind=null);let r=null;for(const t of s){const e=Ds(o.last[t.eid],t);("event"===e||!r&&e)&&(r=e),o.last[t.eid]=t.state}r&&this._stampActivity(e.id,r,n)}}_vacTick(){if(this.hass)for(const t of this._devices){if(t.hidden||!this._isVacDev(t))continue;if("static_icon"===ui(t.marker?.display)){this._vacRt.delete(t.id);continue}const e=this._vacSource(t);if(!e)continue;const i=this._vacEntity(t),s=$l(this.hass.states[i||""]?.state),n=vl(this.hass.states[e]?.attributes);let o=this._vacRt.get(t.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(t.id,o)),s&&!o.moving&&(o.trail=[],o.lastPos=null);const r="never"!==Dl(t.marker?.vacuum)&&!n?.path.length;!s&&o.moving&&(o.endedTs=Date.now(),r&&o.lastPos&&(o.trail=kl(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=s;const a=n?.pos;if(s&&a){const t=a.x+":"+a.y;if(t!==o.lastKey){const e=Date.now();o.jump=o.lastTs>0&&e-o.lastTs>1e4,o.lastKey=t,o.lastTs=e,r&&o.lastPos&&(o.trail=kl(o.trail,o.lastPos,40)),o.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(t){const e=this._serverCfg;if(!e)return null;e.markers=e.markers||[];const i=e.markers.find(e=>e.id===t.id);if(i)return i;if("device"!==t.bindingKind&&"entity"!==t.bindingKind||!t.bindingRef)return null;const s={id:t.id,binding:t.bindingKind+":"+t.bindingRef,space:t.space||null,area:t.area||null,hidden:!!t.hidden};return e.markers.push(s),s}_renderVacSection(t){const e=this._devices.find(e=>e.id===t.devId);if(!e||!this._isVacDev(e))return G;const i=e.marker?.vacuum||{},s=this._vacAllCamerasFor===e.id,n=this._vacSourceResolution(e,s),o=n.entityId,r=n.candidates.find(t=>t.entityId===o)||null,a=o?vl(this.hass?.states[o]?.attributes):null,l=!1!==i.live&&("ok"===n.status||"unsupported"===n.status),c=this._vacPlanRoomAnchors(e.space),h=a?function(t,e){const i=new Set(e.map(bl).filter(Boolean)),s=new Set;for(const e of t){const t=bl(e.name||"");t&&i.has(t)&&s.add(t)}return s.size}(a.rooms,c.map(t=>t.name)):0,d=!!(l&&a&&h>=3),p=Object.keys(i.calibration||{}),u=t=>{const i=this._vacEnsureMarker(e);i&&(i.vacuum={...i.vacuum||{},...t},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate())},_=new Set(e.entities||[]),m=n.candidates.filter(t=>_.has(t.entityId)||t.entityId===i.source),g=s?n.candidates.filter(t=>t.entityId.startsWith("camera.")&&!m.some(e=>e.entityId===t.entityId)):[],f=t=>[t.hasPosition?this._t("vac.cap_position"):"",t.hasRooms?this._t("vac.cap_rooms_short"):"",t.hasPath?this._t("vac.cap_path"):"",t.hasMapId?this._t("vac.cap_map"):""].filter(Boolean).join(" · ")||this._t("vac.cap_none"),v=t=>j` - `,y=n.candidates.some(t=>_.has(t.entityId)&&"known_xcme_incomplete"===t.category)||!!n.pinned&&!!o?.startsWith("camera.")&&!r?.hasPosition;return j` + `,y=o.candidates.some(t=>_.has(t.entityId)&&"known_xcme_incomplete"===t.category)||!!o.pinned&&!!n?.startsWith("camera.")&&!r?.hasPosition;return j`
-
${this._t("vac.diag_source")}${o||this._t("vac.source_none")}
- ${r?.platform?j`
${this._t("vac.diag_platform")}${r.platform}
`:G} -
${this._t("vac.diag_status")}${this._t(`vac.source_status_${n.status}`)}
+
${this._t("vac.diag_source")}${n||this._t("vac.source_none")}
+ ${r?.platform?j`
${this._t("vac.diag_platform")}${r.platform}
`:W} +
${this._t("vac.diag_status")}${this._t(`vac.source_status_${o.status}`)}
${this._t("vac.diag_position")}${a?.pos?this._t("common.yes"):this._t("common.no")}
${this._t("vac.diag_rooms")}${Ri(this._t("vac.diag_rooms_value"),{total:String(a?.rooms.length||0),matched:String(h),readiness:this._t(h>=3?"vac.autocal_ready":"vac.autocal_not_ready")})}
${this._t("vac.diag_path")}${a?.path.length?this._t("common.yes"):this._t("common.no")}
${this._t("vac.diag_map")}${a?.mapId??"default"}
- ${"missing"===n.status||"disabled"===n.status||"unverified"===n.status?j`
- ${this._t(`vac.source_banner_${n.status}`)} + ${"missing"===o.status||"disabled"===o.status||"unverified"===o.status?j`
+ ${this._t(`vac.source_banner_${o.status}`)} -
`:G} +
`:W} ${y?j`
${this._t("vac.xcme_hint")}
attributes:
@@ -4587,11 +4618,11 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f
   - rooms
   - path
   - map_name
-
`:G} +
`:W}
${this._t("vac.choose_source")}
- @@ -4600,13 +4631,13 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f @toggle=${t=>{t.currentTarget.open?this._vacOpenAllCameras(e):(this._vacAllCamerasFor=null,this._vacAllCameraCache=null)}}> ${this._t("vac.all_cameras")}
${this._t("vac.all_cameras_warn")}
- ${s?g.length?g.map(v):j`
${this._t("vac.all_cameras_empty")}
`:G} + ${s?g.length?g.map(v):j`
${this._t("vac.all_cameras_empty")}
`:W}
@@ -4619,35 +4650,35 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f - ${p.length?j`
${Ri(this._t("vac.cal_maps"),{maps:p.join(", ")})}
`:G} - `:G} - `}_vacMapId(t,e){const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return n=e.mapId,o=s,"default"!==n?n:null!=o?String(o):"default";var n,o}_vacSaveMatrix(t,e,i,s){const n=this._devices.find(e=>e.id===t),o=n?this._vacEnsureMarker(n):this._serverCfg?.markers?.find(e=>e.id===t);if(!o)return!1;const r={...o.vacuum||{}};return r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},o.vacuum=r,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_vacPlanRoomAnchors(t){return(this._spaceModel(t||void 0)?.rooms||[]).map(t=>({room:t,poly:Fe(t)})).filter(({room:t,poly:e})=>t.name&&e).map(({room:t,poly:e})=>{const i=ul(e);return i?{name:String(t.name),cx:i[0],cy:i[1]}:null}).filter(Boolean)}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?vl(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._vacPlanRoomAnchors(t.space),n=wl(i.rooms,s);if(!n)return void this._showToast(this._t("vac.autocal_no_match"));const o=this._serverCfg?.spaces?.find(e=>e.id===t.space),r=Number(o?.cell_cm),a=Number.isFinite(r)&&r>0?r:5,l=function(t,e,i){return![t,e,i].every(Number.isFinite)||t<0||e<=0||i<=0?Number.POSITIVE_INFINITY:t/e*i}(n.residual,this._gridPitch,a),c=this._vacMapId(t,i);l>40?this._vacCalConfirm={markerId:t.id,source:e,mapId:c,matrix:n.matrix,rooms:n.matched.length,error:Ee(l,"mi"===this.hass?.config?.unit_system?.length)}:this._vacSaveMatrix(t.id,e,c,n.matrix)&&this._showToast(Ri(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))}_vacApplyCalibrationProposal(t){const e=this._vacCalConfirm;if(e){if(this._vacCalConfirm=null,t){const t=this._devices.find(t=>t.id===e.markerId),i=Ml(e.matrix);if(!t||!i)return;return this._markerDialog=null,t.space!==this._space&&(this._space=t.space),void(this._vacFit={markerId:e.markerId,source:e.source,mapId:e.mapId,p:i,drag:null})}this._vacSaveMatrix(e.markerId,e.source,e.mapId,e.matrix)&&this._showToast(Ri(this._t("vac.autocal_done"),{rooms:String(e.rooms)}))}}_vacStartFit(t){const e=this._vacSource(t),i=e?vl(this.hass?.states[e]?.attributes):null;if(!e||!i)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._vacMapId(t,i),n=t.marker?.vacuum?.calibration?.[s],o=this._spaceModel(t.space),r=o?.vb||[0,0,Cc,Cc],a=n&&6===n.length&&Ml(n)||function(t,e){const i=[],s=[];for(const e of t)null!=e.x0?(i.push(e.x0,e.x1),s.push(e.y0,e.y1)):(i.push(e.cx),s.push(e.cy));if(!i.length)return{ox:e[0]+e[2]/2,oy:e[1]+e[3]/2,s:e[2]/1e4,rot:0,mir:!0};const n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1,c={ox:0,oy:0,s:.6*Math.min(e[2],e[3])/l,rot:0,mir:!0},h=Sl(c),[d,p]=cl(h,(n+o)/2,(r+a)/2);return c.ox=e[0]+e[2]/2-d,c.oy=e[1]+e[3]/2-p,c}(i.rooms,r);this._markerDialog=null,t.space!==this._space&&(this._space=t.space),this._vacFit={markerId:t.id,source:e,mapId:s,p:a,drag:null}}_vacFitSave(){const t=this._vacFit;if(!t)return;const e=this._vacSaveMatrix(t.markerId,t.source,t.mapId,Sl(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=vl(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),n={...e.p,...t};this._vacFit={...e,p:Cl(n,e.p,s[0],s[1])}}_vacGhostCentre(t){const e=[],i=[];for(const s of t)e.push(s.x0??s.cx,s.x1??s.cx),i.push(s.y0??s.cy,s.y1??s.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...i)+Math.max(...i))/2]:[0,0]}_vacDelta(t,e,i){const s=this._stageEl,n=s?.clientWidth||1,o=s?.clientHeight||1;return[e/n*t.w,i/o*t.h]}_vacFitPointer(t,e){const i=this._vacFit;if(!i)return;if(t.stopPropagation(),"pointerdown"===t.type){const e=t.target,s=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this._vacFit={...i,drag:s?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:Number(s.split(",")[0]),fy:Number(s.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:0,fy:0}})}const s=i.drag;if(s){if("pointermove"===t.type){const[n,o]=this._vacDelta(e,t.clientX-s.sx,t.clientY-s.sy);if("move"===s.kind)this._vacFit={...i,p:{...s.p0,ox:s.p0.ox+n,oy:s.p0.oy+o}};else{const t=vl(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),r=Sl(s.p0),[a,l]=cl(r,e[0],e[1]),[c,h]=cl(r,s.fx,s.fy),d=Math.hypot(a-c,l-h)||1,[p,u]=[2*a-c,2*l-h],_=Math.hypot(p+2*n-c,u+2*o-h)/2,m=Math.max(.05,_/d),g={...s.p0,s:s.p0.s*m};this._vacFit={...i,p:Cl(g,s.p0,s.fx,s.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this._vacFit={...i,drag:null})}}_renderVacFit(t){const e=this._vacFit;if(!e)return G;const i=vl(this.hass?.states[e.source]?.attributes);if(!i)return G;const s=Sl(e.p),n=[],o=[],r=[];for(const t of i.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>cl(s,t,e));e.forEach(([t,e])=>{o.push(t),r.push(e)});const[i,a]=cl(s,t.cx,t.cy);n.push(W` - ${t.name}`)}let a=G;if(i.pos){const[e,n]=cl(s,i.pos.x,i.pos.y);a=W``}const l=[];if(o.length){const e=(()=>{const t=s[0]*s[4]-s[1]*s[3];return(e,i)=>[(s[4]*(e-s[2])-s[1]*(i-s[5]))/t,(-s[3]*(e-s[2])+s[0]*(i-s[5]))/t]})(),i=Math.min(...o),n=Math.max(...o),a=Math.min(...r),c=Math.max(...r),h=.022*t.w,d=h/4;for(const[t,s,o,r]of[[i,a,n,c],[n,a,i,c],[n,c,i,a],[i,c,n,a]]){const i=e(o,r);l.push(W`${Ri(this._t("vac.cal_maps"),{maps:p.join(", ")})}`:W} + `:W} + `}_vacMapId(t,e){const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return o=e.mapId,n=s,"default"!==o?o:null!=n?String(n):"default";var o,n}_vacSaveMatrix(t,e,i,s){const o=this._devices.find(e=>e.id===t),n=o?this._vacEnsureMarker(o):this._serverCfg?.markers?.find(e=>e.id===t);if(!n)return!1;const r={...n.vacuum||{}};return r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},n.vacuum=r,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_vacPlanRoomAnchors(t){return(this._spaceModel(t||void 0)?.rooms||[]).map(t=>({room:t,poly:Fe(t)})).filter(({room:t,poly:e})=>t.name&&e).map(({room:t,poly:e})=>{const i=kl(e);return i?{name:String(t.name),cx:i[0],cy:i[1]}:null}).filter(Boolean)}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?Cl(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._vacPlanRoomAnchors(t.space),o=Pl(i.rooms,s);if(!o)return void this._showToast(this._t("vac.autocal_no_match"));const n=this._serverCfg?.spaces?.find(e=>e.id===t.space),r=Number(n?.cell_cm),a=Number.isFinite(r)&&r>0?r:5,l=function(t,e,i){return![t,e,i].every(Number.isFinite)||t<0||e<=0||i<=0?Number.POSITIVE_INFINITY:t/e*i}(o.residual,this._gridPitch,a),c=this._vacMapId(t,i);l>40?this._vacCalConfirm={markerId:t.id,source:e,mapId:c,matrix:o.matrix,rooms:o.matched.length,error:Ee(l,"mi"===this.hass?.config?.unit_system?.length)}:this._vacSaveMatrix(t.id,e,c,o.matrix)&&this._showToast(Ri(this._t("vac.autocal_done"),{rooms:String(o.matched.length)}))}_vacApplyCalibrationProposal(t){const e=this._vacCalConfirm;if(e){if(this._vacCalConfirm=null,t){const t=this._devices.find(t=>t.id===e.markerId),i=El(e.matrix);if(!t||!i)return;return this._markerDialog=null,t.space!==this._space&&(this._space=t.space),void(this._vacFit={markerId:e.markerId,source:e.source,mapId:e.mapId,p:i,drag:null})}this._vacSaveMatrix(e.markerId,e.source,e.mapId,e.matrix)&&this._showToast(Ri(this._t("vac.autocal_done"),{rooms:String(e.rooms)}))}}_vacStartFit(t){const e=this._vacSource(t),i=e?Cl(this.hass?.states[e]?.attributes):null;if(!e||!i)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._vacMapId(t,i),o=t.marker?.vacuum?.calibration?.[s],n=this._spaceModel(t.space),r=n?.vb||[0,0,Nc,Nc],a=o&&6===o.length&&El(o)||function(t,e){const i=[],s=[];for(const e of t)null!=e.x0?(i.push(e.x0,e.x1),s.push(e.y0,e.y1)):(i.push(e.cx),s.push(e.cy));if(!i.length)return{ox:e[0]+e[2]/2,oy:e[1]+e[3]/2,s:e[2]/1e4,rot:0,mir:!0};const o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1,c={ox:0,oy:0,s:.6*Math.min(e[2],e[3])/l,rot:0,mir:!0},h=Il(c),[d,p]=vl(h,(o+n)/2,(r+a)/2);return c.ox=e[0]+e[2]/2-d,c.oy=e[1]+e[3]/2-p,c}(i.rooms,r);this._markerDialog=null,t.space!==this._space&&(this._space=t.space),this._vacFit={markerId:t.id,source:e,mapId:s,p:a,drag:null}}_vacFitSave(){const t=this._vacFit;if(!t)return;const e=this._vacSaveMatrix(t.markerId,t.source,t.mapId,Il(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=Cl(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),o={...e.p,...t};this._vacFit={...e,p:Nl(o,e.p,s[0],s[1])}}_vacGhostCentre(t){const e=[],i=[];for(const s of t)e.push(s.x0??s.cx,s.x1??s.cx),i.push(s.y0??s.cy,s.y1??s.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...i)+Math.max(...i))/2]:[0,0]}_vacDelta(t,e,i){const s=this._stageEl,o=s?.clientWidth||1,n=s?.clientHeight||1;return[e/o*t.w,i/n*t.h]}_vacFitPointer(t,e){const i=this._vacFit;if(!i)return;if(t.stopPropagation(),"pointerdown"===t.type){const e=t.target,s=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this._vacFit={...i,drag:s?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:Number(s.split(",")[0]),fy:Number(s.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:0,fy:0}})}const s=i.drag;if(s){if("pointermove"===t.type){const[o,n]=this._vacDelta(e,t.clientX-s.sx,t.clientY-s.sy);if("move"===s.kind)this._vacFit={...i,p:{...s.p0,ox:s.p0.ox+o,oy:s.p0.oy+n}};else{const t=Cl(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),r=Il(s.p0),[a,l]=vl(r,e[0],e[1]),[c,h]=vl(r,s.fx,s.fy),d=Math.hypot(a-c,l-h)||1,[p,u]=[2*a-c,2*l-h],_=Math.hypot(p+2*o-c,u+2*n-h)/2,m=Math.max(.05,_/d),g={...s.p0,s:s.p0.s*m};this._vacFit={...i,p:Nl(g,s.p0,s.fx,s.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this._vacFit={...i,drag:null})}}_renderVacFit(t){const e=this._vacFit;if(!e)return W;const i=Cl(this.hass?.states[e.source]?.attributes);if(!i)return W;const s=Il(e.p),o=[],n=[],r=[];for(const t of i.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>vl(s,t,e));e.forEach(([t,e])=>{n.push(t),r.push(e)});const[i,a]=vl(s,t.cx,t.cy);o.push(B` + ${t.name}`)}let a=W;if(i.pos){const[e,o]=vl(s,i.pos.x,i.pos.y);a=B``}const l=[];if(n.length){const e=(()=>{const t=s[0]*s[4]-s[1]*s[3];return(e,i)=>[(s[4]*(e-s[2])-s[1]*(i-s[5]))/t,(-s[3]*(e-s[2])+s[0]*(i-s[5]))/t]})(),i=Math.min(...n),o=Math.max(...n),a=Math.min(...r),c=Math.max(...r),h=.022*t.w,d=h/4;for(const[t,s,n,r]of[[i,a,o,c],[o,a,i,c],[o,c,i,a],[i,c,o,a]]){const i=e(n,r);l.push(B` `)}}return j`this._vacFitPointer(e,t)} @pointermove=${e=>this._vacFitPointer(e,t)} @pointerup=${e=>this._vacFitPointer(e,t)} - @pointercancel=${e=>this._vacFitPointer(e,t)}>${n}${a}${l}`}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const t=this.renderRoot,e=this._stageEl,i=this._vacLastView,s=t?.querySelectorAll?.(".vacpuck")||[];if(!e||!i||!s.length)return void(this._vacRaf=0);const n=e.getBoundingClientRect();for(const e of s){const s=e.getAttribute("data-mid"),o=e.getBoundingClientRect(),r=i.x+(o.left+o.width/2-n.left)/n.width*i.w,a=i.y+(o.top+o.height/2-n.top)/n.height*i.h;for(const e of t.querySelectorAll(`line.tip[data-mid="${s}"]`))e.setAttribute("x2",r.toFixed(1)),e.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return G;const i=this._space+"|"+e.x+"|"+e.y+"|"+e.w+"|"+e.h,s=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const n=[],o=[];for(const i of t){if(i.hidden||!this._isVacDev(i))continue;if("static_icon"===ui(i.marker?.display))continue;const t=this._vacSource(i);if(!t)continue;const r=vl(this.hass?.states[t]?.attributes);if(!r)continue;const a=i.marker?.vacuum?.calibration?.[this._vacMapId(i,r)];if(!a||6!==a.length)continue;const l=this._vacRt.get(i.id),c=l?.moving??!1,h=Dl(i.marker?.vacuum),d="always"===h||"cleaning"===h&&c,p=this._vacSrvTrails[i.id],u=this._vacMapId(i,r),_=p?.current?.map_id===u&&Array.isArray(p.current.points)?p.current:null,m=p?.previous?.map_id===u&&Array.isArray(p.previous.points)?p.previous:null;if("always"===h&&m&&m.points.length>1){const t=m.points.map(([t,e])=>{const[i,s]=cl(a,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");o.push(W``)}if(d){const t=gl(r,_,l?.trail||[]),e=!c||"integration"!==t.source&&"server"!==t.source?t.path:fl(t.path);if(e.length){const t=e.map(t=>t.map(([t,e],i)=>{const[s,n]=cl(a,t,e);return`${i?"L":"M"} ${s.toFixed(1)} ${n.toFixed(1)}`}).join(" ")).join(" ");o.push(W``);const s=e[e.length-1];if(c&&s?.length>=2){const t=s[s.length-1],[e,n]=cl(a,t[0],t[1]),r=e.toFixed(1),l=n.toFixed(1);o.push(W``)}}}if(!c||!r.pos)continue;const[g,f]=cl(a,r.pos.x,r.pos.y),v=(g-e.x)/e.w*100,y=(f-e.y)/e.h*100,b=l&&l.lastTs>0&&Date.now()-l.lastTs>6e4,w=i.marker?.icon||i.icon||"mdi:robot-vacuum";n.push(j`
this._vacFitPointer(e,t)}>${o}${a}${l}`}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const t=this.renderRoot,e=this._stageEl,i=this._vacLastView,s=t?.querySelectorAll?.(".vacpuck")||[];if(!e||!i||!s.length)return void(this._vacRaf=0);const o=e.getBoundingClientRect();for(const e of s){const s=e.getAttribute("data-mid"),n=e.getBoundingClientRect(),r=i.x+(n.left+n.width/2-o.left)/o.width*i.w,a=i.y+(n.top+n.height/2-o.top)/o.height*i.h;for(const e of t.querySelectorAll(`line.tip[data-mid="${s}"]`))e.setAttribute("x2",r.toFixed(1)),e.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return W;const i=this._space+"|"+e.x+"|"+e.y+"|"+e.w+"|"+e.h,s=this._vacJumpOnce||i!==this._vacViewKey;this._vacViewKey=i,this._vacJumpOnce=!1;const o=[],n=[];for(const i of t){if(i.hidden||!this._isVacDev(i))continue;if("static_icon"===ui(i.marker?.display))continue;const t=this._vacSource(i);if(!t)continue;const r=Cl(this.hass?.states[t]?.attributes);if(!r)continue;const a=i.marker?.vacuum?.calibration?.[this._vacMapId(i,r)];if(!a||6!==a.length)continue;const l=this._vacRt.get(i.id),c=l?.moving??!1,h=Fl(i.marker?.vacuum),d="always"===h||"cleaning"===h&&c,p=this._vacSrvTrails[i.id],u=this._vacMapId(i,r),_=p?.current?.map_id===u&&Array.isArray(p.current.points)?p.current:null,m=p?.previous?.map_id===u&&Array.isArray(p.previous.points)?p.previous:null;if("always"===h&&m&&m.points.length>1){const t=m.points.map(([t,e])=>{const[i,s]=vl(a,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");n.push(B``)}if(d){const t=Sl(r,_,l?.trail||[]),e=!c||"integration"!==t.source&&"server"!==t.source?t.path:Ml(t.path);if(e.length){const t=e.map(t=>t.map(([t,e],i)=>{const[s,o]=vl(a,t,e);return`${i?"L":"M"} ${s.toFixed(1)} ${o.toFixed(1)}`}).join(" ")).join(" ");n.push(B``);const s=e[e.length-1];if(c&&s?.length>=2){const t=s[s.length-1],[e,o]=vl(a,t[0],t[1]),r=e.toFixed(1),l=o.toFixed(1);n.push(B``)}}}if(!c||!r.pos)continue;const[g,f]=vl(a,r.pos.x,r.pos.y),v=(g-e.x)/e.w*100,y=(f-e.y)/e.h*100,b=l&&l.lastTs>0&&Date.now()-l.lastTs>6e4,w=i.marker?.icon||i.icon||"mdi:robot-vacuum";o.push(j`
{t.stopPropagation();const e=this._vacEntity(i);e&&this._openMoreInfo(e)}}> -
`)}return this._vacLastView=e,n.length&&!this._vacRaf&&this._vacRafLoop(),n.length||o.length?j` - ${o.length?W`${o}`:G} - ${n}`:G}_renderDevice(t,e,i=!0){const s=this._pos(t),n=(s.x-e.x)/e.w*100,o=(s.y-e.y)/e.h*100,r=this._devicePresentation(t,i),a=[`left:${n}%`,`top:${o}%`,...bt(r)],l=r.disabledReason,c=r.haDisabled?this._t(`marker.ha_disabled_${l}`):t.userHidden?this._t("marker.hidden_ghost"):t.name,h=[t.model,null!=r.tempText?r.tempText+"°":"",null!=r.humText?r.humText+"%":"",null!=r.lqiText?"LQI "+r.lqiText:""].filter(Boolean).join(" · ");return j`
`)}return this._vacLastView=e,o.length&&!this._vacRaf&&this._vacRafLoop(),o.length||n.length?j` + ${n.length?B`${n}`:W} + ${o}`:W}_renderDevice(t,e,i=!0){const s=this._pos(t),o=(s.x-e.x)/e.w*100,n=(s.y-e.y)/e.h*100,r=this._devicePresentation(t,i),a=[`left:${o}%`,`top:${n}%`,...bt(r)],l=r.disabledReason,c=r.haDisabled?this._t(`marker.ha_disabled_${l}`):t.userHidden?this._t("marker.hidden_ghost"):t.name,h=[t.model,null!=r.tempText?r.tempText+"°":"",null!=r.humText?r.humText+"%":"",null!=r.lqiText?"LQI "+r.lqiText:""].filter(Boolean).join(" · ");return j`
`}_roomArea(t){const e=Fe(t);if(!e)return null;const i=this._spaceWalls,s=i.length&&t.id&&Mr(this._spaceModel().rooms,t.id,i,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,Cc)||e,n=this._cleanFloor(t,s),o=this._cellCm/this._gridPitch;return go(n.area*o*o/1e4,"mi"===this.hass?.config?.unit_system?.length)}_roomTemp(t){const e=t.settings?.temp_source;return e?bn(this._planHass,e,"temp",this._markers):t.area?this._climate().get(t.area)?.temp??null:null}_roomHum(t){const e=t.settings?.hum_source;return e?bn(this._planHass,e,"hum",this._markers):t.area?this._climate().get(t.area)?.hum??null:null}_climate(){const t=this._serverCfg?.markers,e=this._planHass,i=this._climateCache;if(i&&i.h===e&&i.r===this._iconRules&&i.mk===t)return i.m;const s=function(t,e,i){const s=new Map;if(!t?.entities)return s;const n=gn(i),o=new Set;for(const t of i||[]){if(t?.removed||!0!==t?.use_climate_temp)continue;const e=(t.binding||"").indexOf(":");e>0&&o.add(t.binding.slice(e+1))}const r=new Map;for(const[e,i]of Object.entries(t.entities)){if(i.device_id&&n.devices.has(i.device_id)||!i.device_id&&n.entities.has(e))continue;const s=i.device_id?t.devices?.[i.device_id]:null,a=i.area_id||s?.area_id||null;if(!a)continue;if(i.entity_category)continue;if(!(o.size>0&&e.startsWith("climate.")&&(o.has(e)||i.device_id&&o.has(i.device_id)))){if(kt.has(i.platform))continue;if(wn.test(e))continue}let l=r.get(a);l||(l=new Map,r.set(a,l));const c=i.device_id||e;let h=l.get(c);if(!h){const n=t.states?.[e];h={name:(s?s.name_by_user||s.name:i.name||n?.attributes?.friendly_name||e)||e,model:s?.model,ents:[]},l.set(c,h)}h.ents.push(e)}for(const[i,n]of r){const r=[],a=[];for(const[i,s]of n){const n=mn(t,s.name,s.model,s.ents,e),l="mdi:thermometer"===n||"mdi:air-filter"===n;if(l){const e=hn(t,s.ents);null!=e&&r.push(e)}if(o.size>0&&(o.has(i)||s.ents.some(t=>o.has(t)))){const e=dn(t,s.ents);null!=e&&r.push(e)}if(l||"mdi:water-percent"===n){const e=un(t,s.ents);null!=e&&a.push(e)}}(r.length||a.length)&&s.set(i,{temp:r.length?Math.round(r.reduce((t,e)=>t+e,0)/r.length*10)/10:null,hum:a.length?Math.round(a.reduce((t,e)=>t+e,0)/a.length):null})}return s}(e,this._iconRules,t);return this._climateCache={h:e,r:this._iconRules,mk:t,m:s},s}_resetRoomDialogFields(){this._roomEditId=null,this._roomFill="",this._roomCustomFill=null,this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1}_openRoomEdit(t){if(!t.id)return;this._roomEditId=t.id,this._nameSel=t.name||"",this._areaSel=t.area||"",this._roomFill="glow"===t.settings?.fill_mode?"":t.settings?.fill_mode||"";const e=t.settings?.custom_fill;this._roomCustomFill=e&&"object"==typeof e?Ei(e,Fi(this._curSpaceCfg).customFill):null,this._roomTempSrc=t.settings?.temp_source||"",this._roomHumSrc=t.settings?.hum_source||"",this._roomNameScale=as(t.settings?.name_scale),this._roomLabelScale=as(t.settings?.label_scale),this._roomSrcOpen=null,this._roomSrcFilter="",this._roomDialog=!0}_roomSettingsFromDialog(){const t={};return this._roomFill&&(t.fill_mode=this._roomFill),this._roomCustomFill&&(t.custom_fill=this._roomCustomFill),this._roomTempSrc&&(t.temp_source=this._roomTempSrc),this._roomHumSrc&&(t.hum_source=this._roomHumSrc),1!==this._roomNameScale&&(t.name_scale=this._roomNameScale),1!==this._roomLabelScale&&(t.label_scale=this._roomLabelScale),Object.keys(t).length?t:null}_saveRoomEdit(){const t=this._curSpaceCfg,e=t?.rooms.find(t=>t.id===this._roomEditId);if(!e)return this._roomDialog=!1,void(this._roomEditId=null);e.name=this._nameSel.trim()||e.name,e.area=this._areaSel||null;const i=e.settings||{},s={...i};this._roomFill?s.fill_mode=this._roomFill:delete s.fill_mode,this._roomCustomFill?s.custom_fill=this._roomCustomFill:delete s.custom_fill,"glow"===i.fill_mode&&"boolean"!=typeof i.glow&&(s.glow=!0),this._roomTempSrc?s.temp_source=this._roomTempSrc:delete s.temp_source,this._roomHumSrc?s.hum_source=this._roomHumSrc:delete s.hum_source,1!==this._roomNameScale?s.name_scale=this._roomNameScale:delete s.name_scale,1!==this._roomLabelScale?s.label_scale=this._roomLabelScale:delete s.label_scale,Object.keys(s).length?e.settings=s:delete e.settings,this._saveConfig(),this._roomDialog=!1,this._roomEditId=null,this._nameSel="",this._areaSel="",this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate(),this._showToast(this._t("toast.room_updated"))}_roomSrcCandidates(){const t=this._planHass,e=gn(this._markers),i=this._roomSrcFilter.trim().toLowerCase(),s=[];for(const n of Object.values(t.devices)){if("service"===n.entry_type)continue;if(e.devices.has(n.id))continue;const t=(n.name_by_user||n.name||n.id).trim();i&&!t.toLowerCase().includes(i)||s.push({value:"device:"+n.id,label:t,sub:n.model||this._t("marker.sub_device")})}for(const[n,o]of Object.entries(t.entities)){if(!n.startsWith("sensor.")||o.hidden)continue;if(fn(t,n,e))continue;const r=o.name||t.states[n]?.attributes?.friendly_name||n;i&&!(r+" "+n).toLowerCase().includes(i)||s.push({value:"entity:"+n,label:r,sub:n})}return s.sort((t,e)=>t.label.localeCompare(e.label)),s.slice(0,200)}_roomSrcLabel(t){const e=t.indexOf(":"),i=t.slice(0,e),s=t.slice(e+1);return"device"===i?this._fullRegistryHass.devices[s]?.name_by_user||this._fullRegistryHass.devices[s]?.name||s:this._fullRegistryHass.entities[s]?.name||this.hass.states[s]?.attributes?.friendly_name||s}_labelPos(t,e){const i=this._layout["rl_"+(t.id||"")];if(i&&i.s===e)return{x:i.x*Cc,y:i.y*Cc};const s=this._snap(this._roomCenter(t));return{x:s[0],y:s[1]}}_labelDown(t,e,i){if("plan"!==this._mode)return;t.preventDefault(),t.stopPropagation();const s=this._labelPos(e,i);this._drag={id:"rl_"+(e.id||""),sx:t.clientX,sy:t.clientY,ox:s.x,oy:s.y,moved:!1},Ac(t),this._tip=null}_labelMove(t,e,i){const s="rl_"+(e.id||"");if(!this._drag||this._drag.id!==s)return;const n=this._stageEl;if(!n)return;const o=this._spaceModel(i).vb,r=n.getBoundingClientRect(),a=this._viewOr(o),l=(t.clientX-this._drag.sx)/r.width*a.w,c=(t.clientY-this._drag.sy)/r.height*a.h;Math.abs(t.clientX-this._drag.sx)+Math.abs(t.clientY-this._drag.sy)>3&&(this._drag.moved=!0);const h=ua(this._drag.ox+l),d=ua(this._drag.oy+c);this._savePos({id:s,space:i},h,d)}_labelUp(t){const e="rl_"+(t.id||"");if(!this._drag||this._drag.id!==e)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&window.setTimeout(()=>this._drag=null,0)}_labelScale(t){const e=this._layout["rl_"+(t.id||"")]?.k;return"number"==typeof e&&Number.isFinite(e)?Math.min(3,Math.max(.5,e)):1}_rlResizeDown(t,e,i){if("plan"!==this._mode)return;t.preventDefault(),t.stopPropagation();const s=t.target.closest(".roomlabel");if(!s)return;const n=s.getBoundingClientRect(),o=n.left+n.width/2,r=n.top+n.height/2,a=Math.max(8,Math.hypot(t.clientX-o,t.clientY-r));this._rlResize={id:"rl_"+(e.id||""),space:i,k0:this._labelScale(e),cx:o,cy:r,d0:a},Ac(t)}_rlResizeMove(t){const e=this._rlResize;if(!e)return;t.stopPropagation();const i=Math.max(8,Math.hypot(t.clientX-e.cx,t.clientY-e.cy)),s=Math.min(3,Math.max(.5,e.k0*(i/e.d0))),n=this._layout[e.id];if(n)this._layout={...this._layout,[e.id]:{...n,k:s}};else{const t=e.id.slice(3),i=this._spaceModel(e.space).rooms.find(e=>e.id===t);if(!i)return;const n=this._labelPos(i,e.space);this._layout={...this._layout,[e.id]:{s:e.space,x:n.x/Cc,y:n.y/Cc,k:s}}}this._dirtyPos.add(e.id)}_rlResizeUp(){this._rlResize&&(this._rlResize=null,this._persistLayout())}_renderRoomGear(t,e,i){if(!t.id)return G;let s=null;if(t.poly?(s=this._gearPtCache.get(t.poly)||null,s||(s=Ze(t.poly),this._gearPtCache.set(t.poly,s))):null!=t.x&&null!=t.y&&(s=[t.x+(t.w||0)/2,t.y+(t.h||0)/2]),!s)return G;const n=(s[0]-i.x)/i.w*100,o=(s[1]-i.y)/i.h*100;return j``}_renderRoomLabel(t,e,i,s){if(!t.name&&!this._markup)return G;const n=this._labelPos(t,e.id),o=(n.x-i.x)/i.w*100,r=(n.y-i.y)/i.h*100,a=Math.min(1,s.opacity+.25),l=this._labelScale(t),c=[];if(t.area||t.settings?.temp_source||t.settings?.hum_source||s.labelLight){if(s.labelTemp){const e=this._roomTemp(t);null!=e&&c.push(j`${e}°`)}if(s.labelHum){const e=this._roomHum(t);null!=e&&c.push(j`${e}%`)}if(s.labelLqi&&t.area){const e=this._roomLqi(t.area);null!=e&&c.push(j`${e}`)}if(s.labelLight){const e=(h=an(this._planHass,this._devices,t)).length?{on:h.filter(t=>t.on).length,total:h.length}:null;if(e){const t=0===e.on?this._t("roomcard.light_off"):e.on===e.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:e.on,total:e.total});c.push(j`${t}`)}}}var h;return j`
`}_renderRoomLabel(t,e,i,s){if(!t.name&&!this._markup)return W;const o=this._labelPos(t,e.id),n=(o.x-i.x)/i.w*100,r=(o.y-i.y)/i.h*100,a=Math.min(1,s.opacity+.25),l=this._labelScale(t),c=[];if(t.area||t.settings?.temp_source||t.settings?.hum_source||s.labelLight){if(s.labelTemp){const e=this._roomTemp(t);null!=e&&c.push(j`${e}°`)}if(s.labelHum){const e=this._roomHum(t);null!=e&&c.push(j`${e}%`)}if(s.labelLqi&&t.area){const e=this._roomLqi(t.area);null!=e&&c.push(j`${e}`)}if(s.labelLight){const e=(h=mo(this._planHass,this._devices,t)).length?{on:h.filter(t=>t.on).length,total:h.length}:null;if(e){const t=0===e.on?this._t("roomcard.light_off"):e.on===e.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:e.on,total:e.total});c.push(j`${t}`)}}}var h;return j`
this._labelDown(i,t,e.id)} @pointermove=${i=>this._labelMove(i,t,e.id)} @pointerup=${()=>this._labelUp(t)} @@ -4677,38 +4708,38 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f >${t.name||(this._markup?this._t("room.unnamed"):"")}${!this._markup&&t.area?j`{e.stopPropagation(),this._clickRoom(t)}} - @pointerdown=${t=>t.stopPropagation()}>`:G} - ${c.length?j`${c}`:G} + @pointerdown=${t=>t.stopPropagation()}>`:W} + ${c.length?j`${c}`:W} ${"plan"===this._mode?["tl","tr","bl","br"].map(i=>j`this._rlResizeDown(i,t,e.id)} @pointermove=${t=>this._rlResizeMove(t)} @pointerup=${()=>this._rlResizeUp()} - @pointercancel=${()=>this._rlResizeUp()}>`):G} -
`}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"!==this._tool&&"partition"!==this._tool||!this._path.length||this._contourClosed?"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:this._path[this._path.length-1]:null}_renderMeasureLabel(t){const e=this._measureAnchor,i=this._cursorPt,s=(i[0]-t.x)/t.w*100,n=(i[1]-t.y)/t.h*100,o=ls(e,i),r=Math.round(10*o)/10,a=cs(o);return j`
- ${this._fmtLen(e,i)} · ${r}°
`}get _decorMeasure(){const t=this._decorDraft;if(!t||"decor"!==this._mode)return null;const[e,i]=t.a,[s,n]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-n)<1e-6)return null;const o=(e+s)/2,r=(i+n)/2;if("line"===t.kind){const e=ls(t.a,t.b);return{x:o,y:r,on45:cs(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}const a=this._fmtLen([e,i],[s,i]),l=this._fmtLen([s,i],[s,n]);if("ellipse"===t.kind){const t=this._fmtLen([0,0],[Math.abs(s-e)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(n-i)/2]);return{x:o,y:r,on45:!1,text:Math.abs(Math.abs(s-e)-Math.abs(n-i))<1e-6?`R ${t}`:`Rx ${t} × Ry ${a}`}}return{x:o,y:r,on45:!1,text:`${a} × ${l} · ${go(No(Math.abs(s-e),this._cellCm,this._gridPitch)*No(Math.abs(n-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const t=this._drag.id.slice(3),e=this._spaceModel().rooms.find(e=>e.id===t);return e?(()=>{const t=this._labelPos(e,this._space);return[t.x,t.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const t=this._devices.find(t=>t.id===this._drag.id);return t?(()=>{const e=this._pos(t);return[e.x,e.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const t=this._decorList.find(t=>t.id===this._decorMove.id);if(!t)return null;const e=Cc,i=this._decorH;return"line"===t.kind?[t.x1*e,t.y1*i]:[t.x*e,t.y*i]}return null}return null}_alignCandidates(){const t=[],e=this._spaceModel();if(this._markup){if(this._drag?.id.startsWith("rl_")){const i=this._drag.id.slice(3);for(const s of e.rooms){if(!s.name||s.id===i)continue;const e=this._labelPos(s,this._space);t.push([e.x,e.y])}return t}for(const i of e.rooms){const e=Fe(i);if(e)for(const i of e)t.push(i)}if("draw"===this._tool)for(const e of this._path)t.push(e);if("split"===this._tool&&this._splitSel?.pts)for(const e of this._splitSel.pts)t.push(e);return t}if("devices"===this._mode){for(const e of this._devices){if(e.space!==this._space||e.id===this._drag?.id||"ha_disabled"===e.bindingStatus?.kind)continue;const i=this._pos(e);t.push([i.x,i.y])}return t}if("decor"===this._mode){const e=this._decorMove?.id;return t.push(...this._decorSnapGeometry(e).points),this._decorDraft&&t.push(this._decorDraft.a),t}return t}_renderAlignGuides(){const t=this._alignPoint;if(!t)return W``;const e=this._drag?.id.startsWith("rl_")?.5*this._gridPitch:.05*this._gridPitch,i=function(t,e,i){let s=null,n=null;for(const o of e)if(!(Math.abs(o[0]-t[0])<1e-6&&Math.abs(o[1]-t[1])<1e-6)){if(Math.abs(o[0]-t[0])<=i){const e=Math.abs(o[1]-t[1]);e>1e-6&&(!s||e1e-6&&(!n||e - ${i.map(e=>{const[i,o,r,a]="x"===e.axis?[e.at,e.from[1],e.at,t[1]+Math.sign(t[1]-e.from[1])*n]:[e.from[0],e.at,t[0]+Math.sign(t[0]-e.from[0])*n,e.at];return W` + @pointercancel=${()=>this._rlResizeUp()}>`):W} +
`}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"!==this._tool&&"partition"!==this._tool||!this._path.length||this._contourClosed?"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:this._path[this._path.length-1]:null}_renderMeasureLabel(t){const e=this._measureAnchor,i=this._cursorPt,s=(i[0]-t.x)/t.w*100,o=(i[1]-t.y)/t.h*100,n=_s(e,i),r=Math.round(10*n)/10,a=ms(n);return j`
+ ${this._fmtLen(e,i)} · ${r}°
`}get _decorMeasure(){const t=this._decorDraft;if(!t||"decor"!==this._mode)return null;const[e,i]=t.a,[s,o]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-o)<1e-6)return null;const n=(e+s)/2,r=(i+o)/2;if("line"===t.kind){const e=_s(t.a,t.b);return{x:n,y:r,on45:ms(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}const a=this._fmtLen([e,i],[s,i]),l=this._fmtLen([s,i],[s,o]);if("ellipse"===t.kind){const t=this._fmtLen([0,0],[Math.abs(s-e)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(o-i)/2]);return{x:n,y:r,on45:!1,text:Math.abs(Math.abs(s-e)-Math.abs(o-i))<1e-6?`R ${t}`:`Rx ${t} × Ry ${a}`}}return{x:n,y:r,on45:!1,text:`${a} × ${l} · ${Sn(Gn(Math.abs(s-e),this._cellCm,this._gridPitch)*Gn(Math.abs(o-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const t=this._drag.id.slice(3),e=this._spaceModel().rooms.find(e=>e.id===t);return e?(()=>{const t=this._labelPos(e,this._space);return[t.x,t.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const t=this._devices.find(t=>t.id===this._drag.id);return t?(()=>{const e=this._pos(t);return[e.x,e.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const t=this._decorList.find(t=>t.id===this._decorMove.id);if(!t)return null;const e=Nc,i=this._decorH;return"line"===t.kind?[t.x1*e,t.y1*i]:[t.x*e,t.y*i]}return null}return null}_alignCandidates(){const t=[],e=this._spaceModel();if(this._markup){if(this._drag?.id.startsWith("rl_")){const i=this._drag.id.slice(3);for(const s of e.rooms){if(!s.name||s.id===i)continue;const e=this._labelPos(s,this._space);t.push([e.x,e.y])}return t}for(const i of e.rooms){const e=Fe(i);if(e)for(const i of e)t.push(i)}if("draw"===this._tool)for(const e of this._path)t.push(e);if("split"===this._tool&&this._splitSel?.pts)for(const e of this._splitSel.pts)t.push(e);return t}if("devices"===this._mode){for(const e of this._devices){if(e.space!==this._space||e.id===this._drag?.id||"ha_disabled"===e.bindingStatus?.kind)continue;const i=this._pos(e);t.push([i.x,i.y])}return t}if("decor"===this._mode){const e=this._decorMove?.id;return t.push(...this._decorSnapGeometry(e).points),this._decorDraft&&t.push(this._decorDraft.a),t}return t}_renderAlignGuides(){const t=this._alignPoint;if(!t)return B``;const e=this._drag?.id.startsWith("rl_")?.5*this._gridPitch:.05*this._gridPitch,i=function(t,e,i){let s=null,o=null;for(const n of e)if(!(Math.abs(n[0]-t[0])<1e-6&&Math.abs(n[1]-t[1])<1e-6)){if(Math.abs(n[0]-t[0])<=i){const e=Math.abs(n[1]-t[1]);e>1e-6&&(!s||e1e-6&&(!o||e + ${i.map(e=>{const[i,n,r,a]="x"===e.axis?[e.at,e.from[1],e.at,t[1]+Math.sign(t[1]-e.from[1])*o]:[e.from[0],e.at,t[0]+Math.sign(t[0]-e.from[0])*o,e.at];return B` `})} - `}_renderOpeningCenterTick(t){const e=(t.angle+90)*Math.PI/180;return W``}_renderOpeningCenterTick(t){const e=(t.angle+90)*Math.PI/180;return B``}_roomCenter(t){if(t.poly){const e=t.poly.length;return[t.poly.reduce((t,e)=>t+e[0],0)/e,t.poly.reduce((t,e)=>t+e[1],0)/e]}return[t.x+t.w/2,t.y+.1*Math.min(t.w,t.h)]}_openingAmt(t){const e=t.contact&&this._planEntityAvailable(t.contact)?this.hass.states[t.contact]?.state:null;return Ue(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&(!fn(this._fullRegistryHass,t,gn(this._markers))&&"active"===this._bindingStatus("entity:"+t).kind)}_renderOpenings(t){const e=this._openingsR;if(!e.length)return W``;const i=t.color,s=this._spaceWalls,n=this._openPairs().flatMap(t=>t.segs),o=this._openingWallIndexFor(this._spaceModel(),n).value;return W`${e.map(t=>{const e=t.rlen/2,n=this._openingAmt(t),r=n>0&&!!t.contact&&this._planEntityAvailable(t.contact)?"var(--hp-open)":i,a="gate"===t.type?!t.flip_v:t.flip_v,l=s.length||"gate"===t.type?Er(o,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:a}):{ox:0,oy:0,cm:0,side:-1},c=l.cm>0?l.cm/this._cellCm*this._gridPitch/2:4,h=t.flip_h?-1:1,d=t.flip_v?-1:1;let p,u=0,_=0;if(l.cm>0&&(l.ox||l.oy)){const e=-t.angle*Math.PI/180,i=Math.cos(e),s=Math.sin(e);u=l.ox*i-l.oy*s,_=l.ox*s+l.oy*i,_*=d,u*=h}if("window"===t.type){const t=Math.PI/2*e,i=l.cm>0?W``:G;p=W` + x2="${t.x+15*Math.cos(e)}" y2="${t.y+15*Math.sin(e)}">`}_roomCenter(t){if(t.poly){const e=t.poly.length;return[t.poly.reduce((t,e)=>t+e[0],0)/e,t.poly.reduce((t,e)=>t+e[1],0)/e]}return[t.x+t.w/2,t.y+.1*Math.min(t.w,t.h)]}_openingAmt(t){const e=t.contact&&this._planEntityAvailable(t.contact)?this.hass.states[t.contact]?.state:null;return Ue(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&(!Mo(this._fullRegistryHass,t,So(this._markers))&&"active"===this._bindingStatus("entity:"+t).kind)}_renderOpenings(t){const e=this._openingsR;if(!e.length)return B``;const i=t.color,s=this._spaceWalls,o=this._openPairs().flatMap(t=>t.segs),n=this._openingWallIndexFor(this._spaceModel(),o).value;return B`${e.map(t=>{const e=t.rlen/2,o=this._openingAmt(t),r=o>0&&!!t.contact&&this._planEntityAvailable(t.contact)?"var(--hp-open)":i,a="gate"===t.type?!t.flip_v:t.flip_v,l=s.length||"gate"===t.type?Br(n,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:a}):{ox:0,oy:0,cm:0,side:-1},c=l.cm>0?l.cm/this._cellCm*this._gridPitch/2:4,h=t.flip_h?-1:1,d=t.flip_v?-1:1;let p,u=0,_=0;if(l.cm>0&&(l.ox||l.oy)){const e=-t.angle*Math.PI/180,i=Math.cos(e),s=Math.sin(e);u=l.ox*i-l.oy*s,_=l.ox*s+l.oy*i,_*=d,u*=h}if("window"===t.type){const t=Math.PI/2*e,i=l.cm>0?B``:W;p=B` + stroke="${r}" stroke-dasharray="${t}" stroke-dashoffset="${t*(1-o)}"> + stroke="${r}" stroke-dasharray="${t}" stroke-dashoffset="${t*(1-o)}"> - + - + ${i} - `}else if("gate"===t.type){const t=l.side*d*10*n;p=W` + `}else if("gate"===t.type){const t=l.side*d*10*o;p=B` @@ -4720,16 +4751,16 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f - `}else{const i=t.rlen,s=Math.PI/2*i;p=W` + `}else{const i=t.rlen,s=Math.PI/2*i;p=B` + stroke="${r}" stroke-dasharray="${s}" stroke-dashoffset="${s*(1-o)}"> - + - `}const m="gate"===t.type?Math.sin(10*Math.PI/180)*e:0,g=Math.max(16,c+8,m+8),f=Math.max(20,c+10,m+12);return W``}const m="gate"===t.type?Math.sin(10*Math.PI/180)*e:0,g=Math.max(16,c+8,m+8),f=Math.max(20,c+10,m+12);return B` @@ -4743,25 +4774,25 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f @pointermove=${e=>this._opPointerMove(e,t)} @pointerup=${e=>this._opPointerUp(e,t)} @pointercancel=${e=>this._opPointerUp(e,t)}> - `})}`}_renderOpeningLocks(t){const e=this._openingsR.filter(t=>"window"!==t.type&&t.lock&&this._planEntityAvailable(t.lock));if(!e.length)return j``;const i=this._openPairs().flatMap(t=>t.segs),s=this._openingWallIndexFor(this._spaceModel(),i).value;return j`${e.map(e=>{const i=this.hass.states[e.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),r=(e.angle+90)*Math.PI/180,a="gate"===e.type?Er(s,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:!e.flip_v}):null,l=a?-16*a.side:16*(e.flip_v?-1:1),c=e.rx+Math.cos(r)*l,h=e.ry+Math.sin(r)*l,d=(c-t.x)/t.w*100,p=(h-t.y)/t.h*100;return j`
`})}`}_renderOpeningLocks(t){const e=this._openingsR.filter(t=>"window"!==t.type&&t.lock&&this._planEntityAvailable(t.lock));if(!e.length)return j``;const i=this._openPairs().flatMap(t=>t.segs),s=this._openingWallIndexFor(this._spaceModel(),i).value;return j`${e.map(e=>{const i=this.hass.states[e.lock]?.state,o="locked"===i,n=o||["unlocked","open","opening","unlocking","locking"].includes(String(i)),r=(e.angle+90)*Math.PI/180,a="gate"===e.type?Br(s,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:!e.flip_v}):null,l=a?-16*a.side:16*(e.flip_v?-1:1),c=e.rx+Math.cos(r)*l,h=e.ry+Math.sin(r)*l,d=(c-t.x)/t.w*100,p=(h-t.y)/t.h*100;return j`
{t.stopPropagation(),"view"===this._mode&&(this._openingInfo=e)}}> - -
`})}`}_lockAction(t,e){if(this._planEntityAvailable(t)){if("unlock"===e){const e=this.hass?.states?.[t]?.attributes?.friendly_name||t;if(!confirm(this._t("confirm.unlock",{name:e})))return}this.hass?.callService?.("lock",e,{entity_id:t})}}_renderOpeningInfoCard(){const t=this._openingInfo,e=t.contact&&this._planEntityAvailable(t.contact)?t.contact:null,i=t.lock&&this._planEntityAvailable(t.lock)?t.lock:null,s=e?this.hass.states[e]?.state:null,n=this._openingAmt(t),o=i?this.hass.states[i]?.state:null,r="door"===t.type?"opening.door":"gate"===t.type?"opening.gate":"opening.window",a="door"===t.type?"mdi:door":"gate"===t.type?"mdi:gate":"mdi:window-closed-variant",l="gate"===t.type?n>0?"mdi:gate-open":"mdi:gate":n>0?"mdi:door-open":"mdi:door-closed",c=(t,e,i,s="")=>j`
${e}${i}
`;return j` +
`})}`}_lockAction(t,e){if(this._planEntityAvailable(t)){if("unlock"===e){const e=this.hass?.states?.[t]?.attributes?.friendly_name||t;if(!confirm(this._t("confirm.unlock",{name:e})))return}this.hass?.callService?.("lock",e,{entity_id:t})}}_renderOpeningInfoCard(){const t=this._openingInfo,e=t.contact&&this._planEntityAvailable(t.contact)?t.contact:null,i=t.lock&&this._planEntityAvailable(t.lock)?t.lock:null,s=e?this.hass.states[e]?.state:null,o=this._openingAmt(t),n=i?this.hass.states[i]?.state:null,r="door"===t.type?"opening.door":"gate"===t.type?"opening.gate":"opening.window",a="door"===t.type?"mdi:door":"gate"===t.type?"mdi:gate":"mdi:window-closed-variant",l="gate"===t.type?o>0?"mdi:gate-open":"mdi:gate":o>0?"mdi:door-open":"mdi:door-closed",c=(t,e,i,s="")=>j`
${e}${i}
`;return j`this._openingInfo=null}>
- ${e?c(l,this._t("opening.contact_label"),"unavailable"===s||null==s?this._t("opening.state_unknown"):this._t(n>0?"opening.open":"opening.closed"),n>0?"warn":"ok"):G} - ${i?c("locked"===o?"mdi:lock":"mdi:lock-open-variant",this._t("opening.lock_label"),"locked"===o?this._t("opening.locked"):["unlocked","open"].includes(String(o))?this._t("opening.unlocked"):this._t("opening.state_unknown"),"locked"===o?"ok":"warn"):G} - ${i&&("locked"===o||["unlocked","open"].includes(String(o)))?j``:i&&["locking","unlocking"].includes(String(o))?j``:i&&["locking","unlocking"].includes(String(n))?j``:G} - ${e||i?G:j`

${this._t("opening.no_entities")}

`} + `:W} + ${e||i?W:j`

${this._t("opening.no_entities")}

`}
@@ -4787,98 +4818,98 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f {const i=pc(e.target.value);null!=i&&(this._openingDialog={...t,lengthCm:i})}} /> + @input=${e=>{const i=wc(e.target.value);null!=i&&(this._openingDialog={...t,lengthCm:i})}} /> ${i(this._contactCandidates(),t.contact,e=>this._openingDialog={...t,contact:e})} ${t.contact?j``:G} + ${this._t("opening.invert")}`:W} ${"window"!==t.type?j` - ${i(this._lockCandidates(),t.lock,e=>this._openingDialog={...t,lock:e})}`:G} + ${i(this._lockCandidates(),t.lock,e=>this._openingDialog={...t,lock:e})}`:W} ${"gate"!==t.type?j``:G} + ${this._t("opening.flip_h")}`:W}
${t.id?j``:G} + `:W}
-
`}_gridLevels(){const t=this._stageEl,e=this._viewOr(this._baseVb()),i=t&&t.clientWidth&&e.w?t.clientWidth/e.w:1;return function(t,e,i=7){if(!(t>0&&e>0&&Number.isFinite(e)))return null;const s=Da.find(s=>t*s*e>=i);if(void 0===s)return null;const n=Da.find(t=>t>=5*s)??5*s;return{fine:s,coarse:n}}(this._gridPitch,i)}_renderMarkupDefs(t){const e=this._gridLevels();if(!e)return W``;const i=this._gridPitch*e.fine,s=this._gridPitch*e.coarse,n=this._gridPitch*e.fine*.14;return W` + `}_gridLevels(){const t=this._stageEl,e=this._viewOr(this._baseVb()),i=t&&t.clientWidth&&e.w?t.clientWidth/e.w:1;return function(t,e,i=7){if(!(t>0&&e>0&&Number.isFinite(e)))return null;const s=Fa.find(s=>t*s*e>=i);if(void 0===s)return null;const o=Fa.find(t=>t>=5*s)??5*s;return{fine:s,coarse:o}}(this._gridPitch,i)}_renderMarkupDefs(t){const e=this._gridLevels();if(!e)return B``;const i=this._gridPitch*e.fine,s=this._gridPitch*e.coarse,o=this._gridPitch*e.fine*.14;return B` - - - - + + + + - - - - + + + + - `}_renderPhysicalEditorLayer(){const t=this._spaceModel(),e=this._gridPitch,i=this._viewOr(this._baseVb()),s=this._stageEl,n=s?.clientWidth?i.w/s.clientWidth:e/8,o=Math.max(.22*e,8*n),r=Math.max(o,12*n),a=t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`,l=(t,e)=>this._physicalSel?.kind===t&&this._physicalSel.id===e||"column"===t&&this._duplicateColumnId===e,c=(t.room_drafts||[]).flatMap(t=>t.points.slice(0,-1).map((e,i)=>{const s=t.points[i+1],o=Vo(t.segments[i]?.cm||15,this._cellCm,this._gridPitch);return W``}_renderPhysicalEditorLayer(){const t=this._spaceModel(),e=this._gridPitch,i=this._viewOr(this._baseVb()),s=this._stageEl,o=s?.clientWidth?i.w/s.clientWidth:e/8,n=Math.max(.22*e,8*o),r=Math.max(n,12*o),a=t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`,l=(t,e)=>this._physicalSel?.kind===t&&this._physicalSel.id===e||"column"===t&&this._duplicateColumnId===e,c=(t.room_drafts||[]).flatMap(t=>t.points.slice(0,-1).map((e,i)=>{const s=t.points[i+1],n=ir(t.segments[i]?.cm||15,this._cellCm,this._gridPitch);return B`{this._physicalSel={kind:"draft",id:t.id,segment:i},this._registerPhysicalTap("draft",t.id,i)}}>`})),h=(t.partitions||[]).map(t=>{const e=Wr(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?W`{this._physicalSel={kind:"draft",id:t.id,segment:i},this._registerPhysicalTap("draft",t.id,i)}}>`})),h=(t.partitions||[]).map(t=>{const e=Qr(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?B`this._physicalDown(e,"partition",t.id)} @pointermove=${t=>this._physicalMove(t)} - @pointerup=${t=>this._physicalUp(t)}>`:G}),d=(t.wall_columns||[]).map(t=>{const e=Br(this._physicalRotate?.id===t.id?{...t,angle:this._physicalRotate.angle}:t,this._cellCm,this._gridPitch);return W`this._physicalUp(t)}>`:W}),d=(t.wall_columns||[]).map(t=>{const e=ta(this._physicalRotate?.id===t.id?{...t,angle:this._physicalRotate.angle}:t,this._cellCm,this._gridPitch);return B`this._physicalDown(e,"column",t.id)} @pointermove=${t=>this._physicalMove(t)} - @pointerup=${t=>this._physicalUp(t)}>`}),p=this._physicalDrag,u=(()=>{if(!p?.moved)return G;const t="partition"===p.kind?Wr(p.base.a,p.base.b,p.base.cm,this._cellCm,this._gridPitch):Br(p.base,this._cellCm,this._gridPitch);return t?W``:G})(),_=(()=>{const i=this._physicalSel;if(!i)return G;if("draft"===i.kind){const e=t.room_drafts.find(t=>t.id===i.id);return e?W` + @pointerup=${t=>this._physicalUp(t)}>`}),p=this._physicalDrag,u=(()=>{if(!p?.moved)return W;const t="partition"===p.kind?Qr(p.base.a,p.base.b,p.base.cm,this._cellCm,this._gridPitch):ta(p.base,this._cellCm,this._gridPitch);return t?B``:W})(),_=(()=>{const i=this._physicalSel;if(!i)return W;if("draft"===i.kind){const e=t.room_drafts.find(t=>t.id===i.id);return e?B` t.join(",")).join(" ")}> - ${e.points.map(t=>W``)} - `:G}if("partition"===i.kind){const e=t.partitions.find(t=>t.id===i.id);if(!e)return G;const s=Wr(e.a,e.b,e.cm,this._cellCm,this._gridPitch);if(!s)return G;const n=(e.a[0]+e.b[0])/2,r=(e.a[1]+e.b[1])/2;return W` + ${e.points.map(t=>B``)} + `:W}if("partition"===i.kind){const e=t.partitions.find(t=>t.id===i.id);if(!e)return W;const s=Qr(e.a,e.b,e.cm,this._cellCm,this._gridPitch);if(!s)return W;const o=(e.a[0]+e.b[0])/2,r=(e.a[1]+e.b[1])/2;return B` - - - - `}const s=t.wall_columns.find(t=>t.id===i.id);if(!s)return G;const l=this._physicalRotate?.id===s.id?{...s,angle:this._physicalRotate.angle}:s,c=Br(l,this._cellCm,this._gridPitch);if("square"!==l.shape)return W` + + + + `}const s=t.wall_columns.find(t=>t.id===i.id);if(!s)return W;const l=this._physicalRotate?.id===s.id?{...s,angle:this._physicalRotate.angle}:s,c=ta(l,this._cellCm,this._gridPitch);if("square"!==l.shape)return B` - - `;const h=Vo(l.cm,this._cellCm,this._gridPitch),d=qr(l.angle)*Math.PI/180,p=Math.sin(d),u=-Math.cos(d),_=[l.center[0]+p*h/2,l.center[1]+u*h/2],m=[_[0]+p*Math.max(e,24*n),_[1]+u*Math.max(e,24*n)];return W` + + `;const h=ir(l.cm,this._cellCm,this._gridPitch),d=Jr(l.angle)*Math.PI/180,p=Math.sin(d),u=-Math.cos(d),_=[l.center[0]+p*h/2,l.center[1]+u*h/2],m=[_[0]+p*Math.max(e,24*o),_[1]+u*Math.max(e,24*o)];return B` - + this._physicalRotateDown(t,s)} @pointermove=${t=>this._physicalRotateMove(t)} @pointerup=${t=>this._physicalRotateUp(t)}> - `})();return W`${c}${h}${d}${u}${_}`}_renderMarkupLayer(t){const e=this._openPairs().flatMap(t=>t.segs),i=this._thickWallCuts(),s=e.concat(i),n=s.length?Qi(this._segments,s,.02*this._gridPitch):this._segments,o=this._path,r=this._gridPitch,a=this._viewOr(this._baseVb()),l="draw"===this._tool||"partition"===this._tool?this._drawWallCm:null,c=(()=>"draw"!==this._tool&&"partition"!==this._tool||!o.length||!(null!=l&&l>0)?null:this._contourClosed?o:this._cursorPt?[...o,this._cursorPt]:o.length>=2?o:null)(),h=c?hr(c,Vo(l,this._cellCm,this._gridPitch)/2,this._contourClosed):"";return W` - ${this._gridLevels()?W``:G} - ${n.map(t=>W``)} + `})();return B`${c}${h}${d}${u}${_}`}_renderMarkupLayer(t){const e=this._openPairs().flatMap(t=>t.segs),i=this._thickWallCuts(),s=e.concat(i),o=s.length?ns(this._segments,s,.02*this._gridPitch):this._segments,n=this._path,r=this._gridPitch,a=this._viewOr(this._baseVb()),l="draw"===this._tool||"partition"===this._tool?this._drawWallCm:null,c=(()=>"draw"!==this._tool&&"partition"!==this._tool||!n.length||!(null!=l&&l>0)?null:this._contourClosed?n:this._cursorPt?[...n,this._cursorPt]:n.length>=2?n:null)(),h=c?yr(c,ir(l,this._cellCm,this._gridPitch)/2,this._contourClosed):"";return B` + ${this._gridLevels()?B``:W} + ${o.map(t=>B``)} ${this._renderPhysicalEditorLayer()} - ${"column"===this._tool&&this._cursorPt&&this._drawWallCm?W``M ${Br({shape:"square",center:this._cursorPt,cm:this._drawWallCm},this._cellCm,this._gridPitch).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`)()}>`:G} - ${h?W` - `:G} - ${o.length>1?W``:G} - ${!o.length||!this._cursorPt||"draw"!==this._tool&&"partition"!==this._tool||this._contourClosed?G:W``M ${ta({shape:"square",center:this._cursorPt,cm:this._drawWallCm},this._cellCm,this._gridPitch).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`)()}>`:W} + ${h?B` + `:W} + ${n.length>1?B``:W} + ${!n.length||!this._cursorPt||"draw"!==this._tool&&"partition"!==this._tool||this._contourClosed?W:B``} - ${o.map((t,e)=>W``)} - ${(()=>{const t=this._openingPreview;if(!t)return G;const e=t.angle*Math.PI/180,i=Math.cos(e)*t.rlen/2,s=Math.sin(e)*t.rlen/2;return W`B``)} + ${(()=>{const t=this._openingPreview;if(!t)return W;const e=t.angle*Math.PI/180,i=Math.cos(e)*t.rlen/2,s=Math.sin(e)*t.rlen/2;return B` `})()} - ${"split"===this._tool&&this._splitSel?.pts?.length?W`${this._splitSel.pts.length>1?W``:G} - ${this._splitSel.pts.map((t,e)=>W``)} - ${this._cursorPt?W``:G}`:G} + ${"split"===this._tool&&this._splitSel?.pts?.length?B`${this._splitSel.pts.length>1?B``:W} + ${this._splitSel.pts.map((t,e)=>B``)} + ${this._cursorPt?B``:W}`:W} `}_renderPhysicalDialog(){const t=this._physicalDialog,e="column"===t.kind;return j`{const i=e.target.value;this._physicalDialog={...t,shape:i}}}> - `:G} + `:W}
this._physicalDialog={...t,cm:e.target.value}} /> ${this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
${e&&"square"===t.shape?j` this._physicalDialog={...t,angle:e.target.value}} />`:G} - ${t.length?j`
${this._t("physical.length")}: ${t.length}
`:G} + @input=${e=>this._physicalDialog={...t,angle:e.target.value}} />`:W} + ${t.length?j`
${this._t("physical.length")}: ${t.length}
`:W}
${"draft"===t.kind?j` @@ -4919,7 +4950,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f ${this._t("btn.save")}
- `}_renderMarkupBar(){const t=this._geometryHistory.undoName,e=this._geometryHistory.redoName,i="boundary"===this._tool&&!!this._openWallAnchor,s=i?this._t("markup.boundary_cancel"):t?this._t("history.undo_named",{name:t}):this._t("history.undo_empty"),n=i?this._t("markup.boundary_cancel"):e?this._t("history.redo_named",{name:e}):this._t("history.redo_empty");return j`
+ `}_renderMarkupBar(){const t=this._geometryHistory.undoName,e=this._geometryHistory.redoName,i="boundary"===this._tool&&!!this._openWallAnchor,s=i?this._t("markup.boundary_cancel"):t?this._t("history.undo_named",{name:t}):this._t("history.undo_empty"),o=i?this._t("markup.boundary_cancel"):e?this._t("history.redo_named",{name:e}):this._t("history.redo_empty");return j`
@@ -4988,7 +5019,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f
@@ -5019,33 +5050,33 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f
-
`}_cardEntities(t){const e=this._planHass,i=[],s=new Set,n=t=>{if(!t||s.has(t)||!e.states[t])return;const n=e.entities[t];if("config"===n?.entity_category||"diagnostic"===n?.entity_category)return;s.add(t);const o=t.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(o)?i.push({eid:t,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(o)?i.push({eid:t,kind:"open"}):["sensor","binary_sensor","number","select"].includes(o)&&i.push({eid:t,kind:"value"})};for(const i of an(e,[t]))n(i.eid);t.primary&&n(t.primary);for(const e of t.entities)n(e);return i.slice(0,12)}_cardToggle(t){const e=t.split(".")[0];"lock"!==e&&"alarm_control_panel"!==e&&this._planEntityAvailable(t)&&this.hass.callService("homeassistant","toggle",{entity_id:t}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}_renderInfoCard(){const t=this._infoCard,e=t.primary?this.hass.states[t.primary]:void 0,i=e?Mi(this.hass,t.primary)?.text??e.state:null,s=(t.controls??t.marker?.controls??[]).filter(Ji).filter(t=>this._planEntityAvailable(t));return j``}_cardEntities(t){const e=this._planHass,i=[],s=new Set,o=t=>{if(!t||s.has(t)||!e.states[t])return;const o=e.entities[t];if("config"===o?.entity_category||"diagnostic"===o?.entity_category)return;s.add(t);const n=t.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(n)?i.push({eid:t,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(n)?i.push({eid:t,kind:"open"}):["sensor","binary_sensor","number","select"].includes(n)&&i.push({eid:t,kind:"value"})};for(const i of mo(e,[t]))o(i.eid);t.primary&&o(t.primary);for(const e of t.entities)o(e);return i.slice(0,12)}_cardToggle(t){const e=t.split(".")[0];"lock"!==e&&"alarm_control_panel"!==e&&this._planEntityAvailable(t)&&this.hass.callService("homeassistant","toggle",{entity_id:t}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}_renderInfoCard(){const t=this._infoCard,e=t.primary?this.hass.states[t.primary]:void 0,i=e?Mi(this.hass,t.primary)?.text??e.state:null,s=(t.controls??t.marker?.controls??[]).filter(is).filter(t=>this._planEntityAvailable(t));return j`
${(()=>{const e=this._cardEntities(t);return e.length?j`
- ${e.map(({eid:t,kind:e})=>{const i=this.hass.states[t],s=this.hass.entities[t]?.name||i?.attributes?.friendly_name||t,n=i?Mi(this.hass,t)?.text??i.state:"",o="on"===i?.state||["open","unlocked","playing","cleaning"].includes(i?.state);return j`
- + ${e.map(({eid:t,kind:e})=>{const i=this.hass.states[t],s=this.hass.entities[t]?.name||i?.attributes?.friendly_name||t,o=i?Mi(this.hass,t)?.text??i.state:"",n="on"===i?.state||["open","unlocked","playing","cleaning"].includes(i?.state);return j`
+ ${s} - ${"toggle"===e?j``:"open"===e?j``:j`${n}`} + ${"toggle"===e?j``:"open"===e?j``:j`${o}`}
`})} -
`:G})()} - ${t.model?j`
${this._t("info.model")}${t.model}
`:G} - ${i&&!this._cardEntities(t).length?j`
${this._t("info.state")}${i}
`:G} +
`:W})()} + ${t.model?j`
${this._t("info.model")}${t.model}
`:W} + ${i&&!this._cardEntities(t).length?j`
${this._t("info.state")}${i}
`:W} ${di(t.link)?j`
${this._t("info.link")} - ${t.link}
`:G} - ${t.description?j`
${t.description}
`:G} + ${t.link}
`:W} + ${t.description?j`
${t.description}
`:W} ${t.pdfs&&t.pdfs.length?j`
${this._t("info.manuals")} ${t.pdfs.map(t=>j` - ${t.name}`)}
`:G} + ${t.name}`)}
`:W} ${s.length?j`
${this._t("info.controls")} ${s.map(t=>{const e=this.hass.states[t],i="on"===e?.state;return j` ${e?.attributes?.friendly_name||t}`})} -
`:G} - ${t.model||i||t.link||t.description||t.pdfs&&t.pdfs.length||s.length?G:j`
${this._t("info.none")}
`} +
`:W} + ${t.model||i||t.link||t.description||t.pdfs&&t.pdfs.length||s.length?W:j`
${this._t("info.none")}
`}
${t.primary?j``:G} + `:W}
- `}_markerDraft(t){if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return null;const e=us(t.room),i=ai(t.binding,t.devId,()=>"__hp_device_preview__"),s=this._markers.find(e=>e.id===i||e.id===t.devId),n=nn(t.binding,t.controls,this._bindingEntities(t.binding)),o={id:i,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&3!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle||null,tap_action:t.tapAction||null,tap_target:"run"===t.tapAction&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:n.length?n:null,is_light:!!t.isLight||null,use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=pc(t.glowRadius);return null==e||e<=0?null:Math.round(this._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:t.hideFromPlan,vacuum:s?.vacuum||null};return("virtual"===t.binding||t.room)&&(o.space=e?.space||("virtual"===t.binding?this._space:null),o.area=e?.area||null,o.room_id=e?.roomId||null),o}_markerPreviewDevice(t){const e=this._markerDraft(t);if(!e)return null;const i=`${this._haRegistry.revision}\n${JSON.stringify(e)}`;if(this._markerPreviewMemo?.key===i)return this._markerPreviewMemo.device;const s=function(t){const{marker:e,...i}=t;return yn({...i,markers:[e]}).find(t=>t.id===e.id)||null}({hass:this.hass,registry:this._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),marker:e,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||this._space,loc:t=>this._t(t),iconRules:this._iconRules});return this._markerPreviewMemo={key:i,device:s},s}_renderMarkerDialog(){const t=this._markerDialog,e="virtual"===t.bindingMode,i=this._bindingCandidates(),s=this._bindingEntities(t.binding),n=e?null:this._bindingStatus(t.binding),o=!e&&this._bindingHasHaPage(t.binding),r=this._markerPreviewDevice(t),a=r?Fi(this._serverCfg?.spaces.find(t=>t.id===r.space)):null,l=r?An(this._planHass,r,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:a?.showLqi??!1!==this._config?.show_signal,designPreview:!0,activityRuntime:this._activityRt.get(r.id)}):null,c=(()=>{if(e)return null;const s=i.find(e=>e.value===t.binding);if(s)return s.label;const[n,o]=t.binding.split(":");return"device"===n?this._fullRegistryHass.devices[o]?.name_by_user||this._fullRegistryHass.devices[o]?.name||o:this._fullRegistryHass.entities[o]?.name||this.hass.states[o]?.attributes?.friendly_name||o})();return j``}_markerDraft(t){if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return null;const e=ys(t.room),i=ai(t.binding,t.devId,()=>"__hp_device_preview__"),s=this._markers.find(e=>e.id===i||e.id===t.devId),o=co(t.binding,t.controls,this._bindingEntities(t.binding)),n={id:i,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&3!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle||null,tap_action:t.tapAction||null,tap_target:"run"===t.tapAction&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:o.length?o:null,...this._markerLightFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=wc(t.glowRadius);return null==e||e<=0?null:Math.round(this._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:t.hideFromPlan,vacuum:s?.vacuum||null};return("virtual"===t.binding||t.room)&&(n.space=e?.space||("virtual"===t.binding?this._space:null),n.area=e?.area||null,n.room_id=e?.roomId||null),n}_markerPreviewDevice(t){const e=this._markerDraft(t);if(!e)return null;const i=`${this._haRegistry.revision}\n${JSON.stringify(e)}`;if(this._markerPreviewMemo?.key===i)return this._markerPreviewMemo.device;const s=function(t){const{marker:e,...i}=t;return Do({...i,markers:[e]}).find(t=>t.id===e.id)||null}({hass:this.hass,registry:this._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),marker:e,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||this._space,loc:t=>this._t(t),iconRules:this._iconRules});return this._markerPreviewMemo={key:i,device:s},s}_markerSpatialSource(t){const e=this._markerPreviewDevice(t);return e?go(mo(this._planHass,[{...e,hidden:!1}])):null}_markerAutoHasSpatialSource(t){const e={...t,lightRole:"auto",lightRoleTouched:!0},i=this._markerPreviewDevice(e);return!!i&&uo(this._planHass,{...i})}_setMarkerLightRole(t){const e=this._markerDialog;e&&(this._markerDialog={...e,lightRole:t,lightRoleTouched:!0})}_setMarkerGlowMode(t){const e=this._markerDialog;if(!e)return;if("auto"===t)return void(this._markerDialog={...e,glowMode:t,glowTouched:!0});const i=this._markerSpatialSource(e),s=Zi(i?this._planHass.states[i.eid]:void 0,null,this._fillColors.glow_light.c),o=!e.glowColorDrafted,n="fixed"===t&&!e.glowBrightnessDrafted;this._markerDialog={...e,glowMode:t,glowColor:o?s.c:e.glowColor,glowBrightness:n?Math.max(1,Math.round(100*s.bri)):e.glowBrightness,glowColorDrafted:!0,glowBrightnessDrafted:e.glowBrightnessDrafted||"fixed"===t,glowTouched:!0}}_renderMarkerDialog(){const t=this._markerDialog,e="virtual"===t.bindingMode,i=this._bindingCandidates(),s=this._bindingEntities(t.binding),o=e?null:this._bindingStatus(t.binding),n=!e&&this._bindingHasHaPage(t.binding),r=this._markerPreviewDevice(t),a=r?Fi(this._serverCfg?.spaces.find(t=>t.id===r.space)):null,l=r?Uo(this._planHass,r,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:a?.showLqi??!1!==this._config?.show_signal,designPreview:!0,activityRuntime:this._activityRt.get(r.id)}):null,c=this._markerAutoHasSpatialSource(t),h=this._markerSpatialSource(t),d="never"===t.lightRole||!h,p="never"===t.lightRole?this._t("marker.glow_disabled_never"):"auto"!==t.lightRole||c?this._t("marker.glow_disabled_no_entity"):this._t("marker.glow_disabled_auto"),u=(()=>{if(e)return null;const s=i.find(e=>e.value===t.binding);if(s)return s.label;const[o,n]=t.binding.split(":");return"device"===o?this._fullRegistryHass.devices[n]?.name_by_user||this._fullRegistryHass.devices[n]?.name||n:this._fullRegistryHass.entities[n]?.name||this.hass.states[n]?.attributes?.friendly_name||n})();return j`this._markerDialog=null}>
- ${"ha_disabled"===n?.kind?j`
+ ${"ha_disabled"===o?.kind?j`
- ${this._t(`marker.ha_disabled_${n.reason}`)} - ${o?j``:G} -
`:"unverified"===n?.kind&&t.binding?j`
+ `:W} +
`:"unverified"===o?.kind&&t.binding?j`
${this._t("marker.ha_registry_limited")} -
`:G} +
`:W}
@@ -5095,7 +5126,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f
${"ha"===t.bindingMode?j` ${t.bindingOpen?j`
@@ -5104,12 +5135,12 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f @input=${e=>this._markerDialog={...t,bindingFilter:e.target.value}} />
${i.map(e=>j`
this._markerDialog={...t,binding:e.value,bindingOpen:!1,controls:sn(e.value,t.controls,this._bindingEntities(e.value)),autoIcon:this._autoIconForBinding(e.value)}}> + @click=${()=>this._markerDialog={...t,binding:e.value,bindingOpen:!1,controls:lo(e.value,t.controls,this._bindingEntities(e.value)),autoIcon:this._autoIconForBinding(e.value)}}> ${e.label}${e.sub}
`)} - ${i.length?G:j`
${this._t("marker.nothing_found")}
`} + ${i.length?W:j`
${this._t("marker.nothing_found")}
`}
-
`:G}`:G} +
`:W}`:W} @@ -5128,22 +5159,22 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f ${"run"===t.tapAction?(()=>{const e=t.runFilter.trim().toLowerCase(),i=this._runCandidates().filter(t=>!e||t.label.toLowerCase().includes(e)||t.value.includes(e)),s=t.tapTarget?this._runCandidates().find(e=>e.value===t.tapTarget):null;return j` - ${t.tapTarget&&!s?j`
${this._t("marker.run_target_gone",{id:t.tapTarget})}
`:G} + ${t.tapTarget&&!s?j`
${this._t("marker.run_target_gone",{id:t.tapTarget})}
`:W} {t.target.select()}} @input=${e=>this._markerDialog={...t,runFilter:e.target.value,tapTarget:""}} /> - ${s?G:j`
+ ${s?W:j`
${i.slice(0,40).map(e=>j`
this._markerDialog={...t,tapTarget:e.value,runFilter:""}}> ${e.label}${e.sub}
`)} - ${i.length?G:j`
${this._t("marker.nothing_found")}
`} -
`}`})():G} + ${i.length?W:j`
${this._t("marker.nothing_found")}
`} +
`}`})():W} ${"run"===t.tapAction||"toggle"===t.tapAction||"cover"===t.tapAction||!t.tapAction&&"toggle"===t.defaultTap?j``:G} + `:W}
${this._t("marker.controls_hint")}
@@ -5152,36 +5183,81 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f ${this.hass.states[e]?.attributes?.friendly_name||e} this._markerDialog={...t,controls:t.controls.filter(t=>t!==e)}}> `)} - `:G} + `:W} this._markerDialog={...t,controlsFilter:e.target.value}} /> ${t.controlsFilter.trim()?j`
- ${Object.keys(this.hass.states).filter(e=>sn(t.binding,[e],s).length>0&&!t.controls.includes(e)&&this._planEntityAvailable(e)).filter(e=>{const i=t.controlsFilter.trim().toLowerCase(),s=String(this.hass.states[e]?.attributes?.friendly_name||"");return e.toLowerCase().includes(i)||s.toLowerCase().includes(i)}).slice(0,8).map(e=>j``)} -
`:G} + `:W} ${this._bindingHasClimate(t.binding)?j``:G} - - -
- this._markerDialog={...t,glowRadius:e.target.value}} /> - ${this._imperial?this._t("gs.unit_ft"):this._t("gs.unit_m")} - ${this._t("marker.glow_radius_hint")} -
+ `:W} +
+ ${this._t("marker.light_role_label")} +

${this._t("marker.light_role_tip")}

+
+ + + +
+
+ +
+ ${this._t("marker.glow_color_label")} +

${this._t("marker.glow_color_tip")}

+
+ + + +
+ ${"auto"!==t.glowMode?j`
+ {this._markerDialog={...t,glowColor:e.detail.color,glowColorDrafted:!0,glowTouched:!0}}}> + ${"fixed"===t.glowMode?j` + ${this._t("marker.glow_brightness")} + ${this._rangeInput(1,100,1,t.glowBrightness,e=>{this._markerDialog={...t,glowBrightness:e,glowBrightnessDrafted:!0,glowTouched:!0}},d,this._t("marker.glow_brightness"))} + ${Math.round(t.glowBrightness)}%`:W} +
`:W} + ${"fixed"===t.glowMode?j`

${this._t("marker.glow_brightness_hint")}

`:W} + +
+ this._markerDialog={...t,glowRadius:e.target.value}} /> + ${this._imperial?this._t("gs.unit_ft"):this._t("gs.unit_m")} + ${this._t("marker.glow_radius_hint")} +
+
+ ${d?j`

+ ${p} +

`:W} ${customElements.get("ha-icon-picker")?j`this._markerDialog={...t,icon:t.autoIcon}}> ${this._t("marker.icon_pin_auto")} -

`:G} +

`:W} this._spaceDialog={...t,source:"draw"}} /> @@ -5323,9 +5399,9 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f
- {const i=pc(e.target.value);this._spaceDialog={...t,cellCm:null!=i&&i>0?Math.max(.1,Math.min(cc,i)):t.cellCm}}} /> + @input=${e=>{const i=wc(e.target.value);this._spaceDialog={...t,cellCm:null!=i&&i>0?Math.max(.1,Math.min(vc,i)):t.cellCm}}} /> ${this._t("space.scale_unit")}
@@ -5379,20 +5455,20 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f - ${"static"===(t.bgMode??Do(this._settings,{}))?j` + ${"static"===(t.bgMode??Fn(this._settings,{}))?j`
this._spaceDialog={...t,bgColor:e.target.value}} /> ${t.bgColor?j``:j`${this._t("space.bg_inherited")}`} -
`:G} + `:W}
{const i=e.target.value.trim(),s=""===i?null:Math.round(Number(i));this._spaceDialog={...t,northDeg:null!==s&&Number.isFinite(s)?Math.min(359,Math.max(0,s)):null}}} /> - ${null===t.northDeg?this._t("space.north_inherited",{v:null===Co(this._settings,{})?"—":String(Co(this._settings,{}))+"°"}):"°"} + ${null===t.northDeg?this._t("space.north_inherited",{v:null===Nn(this._settings,{})?"—":String(Nn(this._settings,{}))+"°"}):"°"}
{const i=pc(e.target.value);null!=i&&(this._spaceDialog={...t,tempMin:i})}} /> + @input=${e=>{const i=wc(e.target.value);null!=i&&(this._spaceDialog={...t,tempMin:i})}} /> – {const i=pc(e.target.value);null!=i&&(this._spaceDialog={...t,tempMax:i})}} /> + @input=${e=>{const i=wc(e.target.value);null!=i&&(this._spaceDialog={...t,tempMax:i})}} /> °C - `:G} + `:W} ${"custom"===e&&"custom"===t.fillMode?j`
${this._t("space.custom_fill")} @@ -5425,8 +5501,8 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f @hp-color-opacity-change=${e=>{this._spaceDialog={...t,customFill:{c:e.detail.color,a:e.detail.opacity}}}}> ${t.customFill?j``:G} -
`:G}`)} + ${this._t("btn.reset")}`:W} + `:W}`)}
`}_renderMergeDialog(){const t=this._mergeDialog,e=this._spaceModel().rooms,i=(i,s)=>{const n=e.find(t=>t.id===i),o=n?.area?this.hass.areas[n.area]?.name:null;return j`
`}_renderMergeDialog(){const t=this._mergeDialog,e=this._spaceModel().rooms,i=(i,s)=>{const o=e.find(t=>t.id===i),n=o?.area?this.hass.areas[o.area]?.name:null;return j``};return j`this._mergeDialog=null}>
@@ -5499,7 +5575,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f ${t.label}${t.sub}
`)} - `:G}`:G}`}_renderRoomDialog(){const t=!!this._roomEditId,e=!!this._areaSel||!!this._nameSel.trim(),i=Fi(this._curSpaceCfg),s=this._roomFill||i.fill,n=this._roomCustomFill||i.customFill,o=[...this._freeAreas];if(t&&this._areaSel&&!o.some(t=>t.area_id===this._areaSel)){const t=this.hass.areas[this._areaSel];t&&o.unshift(t)}return j``:W}`:W}`}_renderRoomDialog(){const t=!!this._roomEditId,e=!!this._areaSel||!!this._nameSel.trim(),i=Fi(this._curSpaceCfg),s=this._roomFill||i.fill,o=this._roomCustomFill||i.customFill,n=[...this._freeAreas];if(t&&this._areaSel&&!n.some(t=>t.area_id===this._areaSel)){const t=this.hass.areas[this._areaSel];t&&n.unshift(t)}return j`
@@ -5511,7 +5587,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f @@ -5526,11 +5602,11 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f {this._roomCustomFill={c:t.detail.color,a:t.detail.opacity}}}> - ${this._roomCustomFill?j``:G} -
`:G} + ${this._roomCustomFill?j``:W} + `:W} ${this._renderRoomSource("temp")} ${this._renderRoomSource("hum")} @@ -5552,11 +5628,11 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f ${t?j``:j`${this._pendingSplit?G:j``:j`${this._pendingSplit?W:j``} `} -
`}}Ic.properties={_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_rszSel:{state:!0},_rszLive:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_openWallAnchor:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_vacAllCamerasFor:{state:!0},_vacCalConfirm:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomCustomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},Ic.ZOOM_MAX=8,Ic.ZOOM_MIN=1/3,Ic._touchSeen=!1,Ic._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,Ic.styles=[yt,ql],customElements.get("houseplan-card")||customElements.define("houseplan-card",Ic),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${ac} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); +
`}}jc.properties={_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_rszSel:{state:!0},_rszLive:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_openWallAnchor:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_vacAllCamerasFor:{state:!0},_vacCalConfirm:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomCustomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},jc.ZOOM_MAX=8,jc.ZOOM_MIN=1/3,jc._touchSeen=!1,jc._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,jc.styles=[yt,Jl],customElements.get("houseplan-card")||customElements.define("houseplan-card",jc),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${gc} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); diff --git a/custom_components/houseplan/manifest.json b/custom_components/houseplan/manifest.json index c23851b5..02385fcd 100755 --- a/custom_components/houseplan/manifest.json +++ b/custom_components/houseplan/manifest.json @@ -16,5 +16,5 @@ "issue_tracker": "https://github.com/Matysh/houseplan-card/issues", "requirements": [], "single_config_entry": true, - "version": "1.61.0-beta.4" + "version": "1.61.0-beta.5" } diff --git a/custom_components/houseplan/validation.py b/custom_components/houseplan/validation.py index e72fb632..f018d9fd 100644 --- a/custom_components/houseplan/validation.py +++ b/custom_components/houseplan/validation.py @@ -643,6 +643,18 @@ MARKER_SCHEMA = vol.Schema( ), vol.Optional("controls"): vol.Any(None, vol.All([_TEXT], vol.Length(max=MAX_CONTROLS))), vol.Optional("glow_radius_cm"): vol.Any(vol.All(vol.Coerce(float), vol.Range(min=10, max=10000)), None), + vol.Optional("glow_color"): vol.Any( + None, + vol.Schema( + { + vol.Required("c"): _COLOR, + vol.Optional("bri"): vol.Any( + None, + vol.All(_finite, vol.Range(min=0.01, max=1.0)), + ), + } + ), + ), vol.Optional("is_light"): vol.Any(bool, None), # climate current_temperature: badge + room-average vote (off unless True) vol.Optional("use_climate_temp"): vol.Any(bool, None), diff --git a/demo/benchmark_glow.mjs b/demo/benchmark_glow.mjs index ebb22b40..3c08a929 100644 --- a/demo/benchmark_glow.mjs +++ b/demo/benchmark_glow.mjs @@ -17,6 +17,9 @@ const parsedSamples = Number(valueArg('samples')); const parsedWarmups = Number(valueArg('warmups')); const samples = Math.max(1, Math.min(20, Number.isFinite(parsedSamples) && parsedSamples > 0 ? parsedSamples : 7)); const warmups = Math.max(0, Math.min(5, Number.isFinite(parsedWarmups) && parsedWarmups >= 0 ? parsedWarmups : 1)); +const requestedVariants = valueArg('variants')?.split(',').map(Number); +if (requestedVariants?.some((count) => ![1, 10, 30, 60].includes(count))) + throw new Error(`invalid Glow variants: ${valueArg('variants')}`); const output = valueArg('output') ? resolve(valueArg('output')) : null; const targetRoot = resolve(valueArg('target-root') ?? '.'); const additiveFixture = JSON.parse(readFileSync( @@ -78,6 +81,7 @@ const makeOverlayFixture = () => { }; }; const fixture = profile === 'large-light-blend-v1' ? additiveFixture : makeOverlayFixture(); +if (requestedVariants?.length) fixture.variants = [...new Set(requestedVariants)]; const viewport = { width: 1280, height: 900 }; const { page, browser } = await launch( @@ -267,7 +271,7 @@ try { } const metricNames = [ - 'stateUpdate1Ms', 'stateUpdate10Ms', 'stateUpdate30Ms', 'stateUpdate60Ms', 'screenshotCaptureMs', + ...fixture.variants.map((count) => `stateUpdate${count}Ms`), 'screenshotCaptureMs', ]; const report = { schema: 2, diff --git a/demo/golden/harness.mjs b/demo/golden/harness.mjs index 1ef327a6..deab76e6 100644 --- a/demo/golden/harness.mjs +++ b/demo/golden/harness.mjs @@ -86,6 +86,16 @@ export async function prepareGoldenScenario(page, scenario) { fixture.states[entityId] = { ...state, state: 'off' }; } } + if (scenario.markerOverrides) { + const ids = new Set(scenario.markerOverrides.map((marker) => marker.id)); + fixture.config.markers = [ + ...(fixture.config.markers || []).filter((marker) => !ids.has(marker.id)), + ...structuredClone(scenario.markerOverrides), + ]; + } + if (scenario.layoutOverrides) { + fixture.layout = { ...(fixture.layout || {}), ...structuredClone(scenario.layoutOverrides) }; + } return page.evaluate(async ({ fixture, scenario }) => { const wait = (ms) => new Promise((done) => setTimeout(done, ms)); diff --git a/demo/golden/matrix.mjs b/demo/golden/matrix.mjs index 82dbcb33..ac22b454 100644 --- a/demo/golden/matrix.mjs +++ b/demo/golden/matrix.mjs @@ -1,5 +1,5 @@ /** Data-only HP-QA-01 capture matrix. Bump when framing or scenarios change. */ -export const GOLDEN_MATRIX_VERSION = 5; +export const GOLDEN_MATRIX_VERSION = 6; const stage = { capture: 'stage', threshold: { maxChannelDelta: 10, maxDiffRatio: 0.0005 } }; const page = { capture: 'page', threshold: { maxChannelDelta: 10, maxDiffRatio: 0.0008 } }; @@ -67,6 +67,14 @@ export const GOLDEN_SCENARIOS = Object.freeze([ { id: 'lighting-temp-glow-room-override-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view', fillMode: 'temp', glowEnabled: true, roomGlow: { 'light-left': true, 'light-right': false }, theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage }, + { id: 'lighting-manual-auto-spill-overlap-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view', + fillMode: 'temp', glowEnabled: true, + markerOverrides: [{ + id: 'golden-light-two', binding: 'device:golden-light-two', + glow_color: { c: '#3a8fff', bri: 0.35 }, glow_radius_cm: 420, + }], + layoutOverrides: { 'golden-light-two': { s: 'golden-lighting', x: 0.39, y: 0.57 } }, + theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage }, { id: 'hover-over-glow-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view', hoverRoom: 'light-right', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage }, { id: 'hover-nested-room-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view', diff --git a/demo/performance/README.md b/demo/performance/README.md index e1a25793..6438b3ff 100644 --- a/demo/performance/README.md +++ b/demo/performance/README.md @@ -20,11 +20,22 @@ high-tail guard rather than a population estimate: Every report is tied to the source fingerprint embedded by Rollup. A stale bundle is a hard failure. -## CI contract +## CI contracts -The `performance` job checks out the candidate and its base SHA, builds both, -and runs them sequentially with the same Node.js 22 process family, pinned -Playwright Chromium and hosted runner. `compare.mjs` then applies two limits: +Ordinary pushes, pull requests and prereleases use the blocking +`performance_smoke` job in `validate.yml`. It builds only the candidate and +measures the heaviest 60-source `large-house-glow-overlay-v1` state after one +warm-up, with three recorded samples. `compare.mjs --absolute-only` enforces the +reviewed hard timing, Long Task, heap, cache and rendered-device ceilings from +`budgets-glow-smoke.json`; it deliberately makes no noisy base-relative claim. +This is a catastrophic-regression guard, not a performance trend detector. + +The dedicated `performance.yml` workflow is the full comparison. It runs on +every `main` promotion, weekly and on manual dispatch for an important beta or +performance-sensitive change. It checks out the candidate and its base SHA, +builds both, and runs them sequentially with the same Node.js 22 process +family, pinned Playwright Chromium and hosted runner. `compare.mjs` then +applies two limits: 1. a relative regression allowance against the base-SHA report; 2. an absolute safety ceiling from `budgets.json`. @@ -35,23 +46,24 @@ regressions. Small fast operations receive an absolute noise allowance so normal scheduler jitter does not become a false regression. Heap, Long Tasks, warmed-cache growth and the expected rendered-device count are gated separately. Long-Task maximum/count/total checks use the same -relative-plus-absolute policy as timings. Both raw reports and the comparison are always uploaded as -the `large-house-performance` artifact, and the table is written to the GitHub -job summary. +relative-plus-absolute policy as timings. Both raw reports and the comparison +are always uploaded as the `full-performance` artifact, and the table is +written to the GitHub job summary. Stable release assets require both exact-SHA +`Validate` and exact-SHA `Full Performance`; prereleases require only +`Validate`. This base-vs-candidate design intentionally does not compare timings captured on different machines or different Chromium builds. A runtime/profile mismatch fails closed. Before the base checkout, CI fetches the complete commit graph and verifies the -requested comparison revision. A push `github.event.before` must both exist and -remain an ancestor of the candidate; this catches the unreachable SHA left by a -force-push. For that rewritten-push case, CI warns and prefers the candidate's -direct parent as the closest honest comparison. An unusable PR base or another -case without a safe parent falls back, with a warning, to the newest semver -release tag reachable from the candidate and excludes a tag on the candidate -itself. If no safe comparison exists, the job fails closed instead of comparing -against an arbitrary commit. +requested comparison revision. A `main` push uses `github.event.before`, which +must both exist and remain an ancestor of the candidate; this catches the +unreachable SHA left by a force-push. A manual run may name an explicit tag, +branch or SHA, while an empty manual input and the weekly run use the candidate +parent. An unusable requested revision falls back with a warning to the direct +parent, then to the newest reachable semver release. If no safe comparison +exists, the job fails closed instead of comparing against an arbitrary commit. ## Private card contract @@ -129,6 +141,8 @@ Chromium CPU throttling x4, but deliberately exercise different fixtures: ```bash npm run benchmark:glow -- --profile=large-light-blend-v1 --output=artifacts/performance/glow.json npm run benchmark:glow -- --profile=large-house-glow-overlay-v1 --output=artifacts/performance/overlay.json +npm run benchmark:glow -- --profile=large-house-glow-overlay-v1 --variants=60 --samples=3 --warmups=1 --output=artifacts/performance-smoke/candidate.json +npm run benchmark:compare -- --absolute-only --budgets=demo/performance/budgets-glow-smoke.json --candidate=artifacts/performance-smoke/candidate.json --output=artifacts/performance-smoke/comparison.json ``` Reports include per-variant state-update timings, render/pool counts, Long @@ -140,5 +154,6 @@ base SHA. The initial absolute ceilings are intentionally conservative bootstrap limits; they must be reviewed against the first paired Ubuntu artifacts before the -feature is promoted from beta. Same-runner relative checks remain the primary -candidate-only regression signal. +feature is promoted from beta. Same-runner relative checks in the full workflow +remain the primary regression signal; the candidate-only smoke only guards +against catastrophic failures. diff --git a/demo/performance/budgets-glow-smoke.json b/demo/performance/budgets-glow-smoke.json new file mode 100644 index 00000000..49b1d72d --- /dev/null +++ b/demo/performance/budgets-glow-smoke.json @@ -0,0 +1,39 @@ +{ + "schema": 1, + "profile": "large-house-glow-overlay-v1", + "minimumSamples": 3, + "timings": { + "stateUpdate60Ms": { + "stat": "median", + "hardMaxMs": 2200 + }, + "screenshotCaptureMs": { + "stat": "median", + "hardMaxMs": 3200 + } + }, + "longTasks": { + "maxSingleMs": 2700, + "maxCountP95": 12, + "maxTotalP95Ms": 5500 + }, + "heap": { + "required": true, + "hardMaxGrowthBytes": 67108864 + }, + "cacheEntries": { + "cleanFloor": 80, + "glowClip": 128, + "wallUnion": 1, + "openingTunnel": 1, + "openingWallIndex": 1 + }, + "cacheGrowth": { + "cleanFloor": 0, + "glowClip": 0, + "wallUnion": 0, + "openingTunnel": 0, + "openingWallIndex": 0 + }, + "renderedDevices": 200 +} diff --git a/demo/performance/compare.mjs b/demo/performance/compare.mjs index 9550b6fe..1aa57e49 100644 --- a/demo/performance/compare.mjs +++ b/demo/performance/compare.mjs @@ -4,6 +4,7 @@ import { dirname, resolve } from 'node:path'; import { evaluatePerformanceBudget, performanceSummaryMarkdown } from './evaluate.mjs'; const valueArg = (name) => process.argv.find((arg) => arg.startsWith(`--${name}=`))?.slice(name.length + 3); +const absoluteOnly = process.argv.includes('--absolute-only'); const candidatePath = resolve(valueArg('candidate') ?? 'artifacts/performance/candidate.json'); const baselinePath = resolve(valueArg('baseline') ?? 'artifacts/performance/baseline.json'); const budgetsPath = resolve(valueArg('budgets') ?? 'demo/performance/budgets.json'); @@ -12,8 +13,9 @@ const outputPath = resolve(valueArg('output') ?? 'artifacts/performance/comparis const readJson = (path) => JSON.parse(readFileSync(path, 'utf8')); const evaluation = evaluatePerformanceBudget({ candidate: readJson(candidatePath), - baseline: readJson(baselinePath), + baseline: absoluteOnly ? null : readJson(baselinePath), budgets: readJson(budgetsPath), + absoluteOnly, }); mkdirSync(dirname(outputPath), { recursive: true }); diff --git a/demo/performance/evaluate.mjs b/demo/performance/evaluate.mjs index cd12ae8a..22f485a1 100644 --- a/demo/performance/evaluate.mjs +++ b/demo/performance/evaluate.mjs @@ -60,16 +60,19 @@ const makeCheck = (id, actual, limit, details = {}) => ({ }); /** - * Evaluate a candidate report against both stable absolute ceilings and a - * report captured from the base SHA on the same runner. + * Evaluate a candidate report against stable absolute ceilings. Full captures + * additionally compare a base-SHA report from the same runner; fast smoke + * captures deliberately enforce only the hard candidate limits. */ -export const evaluatePerformanceBudget = ({ candidate, baseline, budgets }) => { +export const evaluatePerformanceBudget = ({ candidate, baseline, budgets, absoluteOnly = false }) => { requireReport(candidate, budgets, 'candidate'); - requireReport(baseline, budgets, 'baseline'); - if (!sameJson(candidate.fixture, baseline.fixture)) throw new Error('fixture mismatch'); - for (const key of ['node', 'chromium', 'platform', 'arch']) { - if (candidate.runtime?.[key] !== baseline.runtime?.[key]) { - throw new Error(`runtime mismatch for ${key}`); + if (!absoluteOnly) { + requireReport(baseline, budgets, 'baseline'); + if (!sameJson(candidate.fixture, baseline.fixture)) throw new Error('fixture mismatch'); + for (const key of ['node', 'chromium', 'platform', 'arch']) { + if (candidate.runtime?.[key] !== baseline.runtime?.[key]) { + throw new Error(`runtime mismatch for ${key}`); + } } } @@ -77,63 +80,74 @@ export const evaluatePerformanceBudget = ({ candidate, baseline, budgets }) => { for (const [metric, budget] of Object.entries(budgets.timings)) { const stat = budget.stat ?? 'median'; const actual = candidate.summary?.[metric]?.[stat]; - const base = baseline.summary?.[metric]?.[stat]; - if (!finite(actual) || !finite(base)) throw new Error(`missing ${stat} for ${metric}`); - const regressionLimit = relativeLimit(base, budget.maxRegressionRatio, budget.noiseAllowanceMs); + const base = absoluteOnly ? null : baseline.summary?.[metric]?.[stat]; + if (!finite(actual) || (!absoluteOnly && !finite(base))) throw new Error(`missing ${stat} for ${metric}`); + const regressionLimit = absoluteOnly + ? Number.POSITIVE_INFINITY + : relativeLimit(base, budget.maxRegressionRatio, budget.noiseAllowanceMs); checks.push(makeCheck( `timing.${metric}.${stat}`, actual, Math.min(budget.hardMaxMs, regressionLimit), - { baseline: round(base), hardLimit: budget.hardMaxMs, regressionLimit: round(regressionLimit) }, + { + ...(absoluteOnly ? {} : { baseline: round(base), regressionLimit: round(regressionLimit) }), + hardLimit: budget.hardMaxMs, + }, )); } const candidateLong = candidate.longTasks ?? summarizeLongTasks(candidate.rows); - const baselineLong = baseline.longTasks ?? summarizeLongTasks(baseline.rows); + const baselineLong = absoluteOnly ? null : (baseline.longTasks ?? summarizeLongTasks(baseline.rows)); const longTasksAvailable = candidate.rows.every((row) => { const windows = Object.values(row.longTasks ?? {}); return windows.length > 0 && windows.every((item) => item?.supported === true); }); checks.push({ id: 'longTask.available', actual: longTasksAvailable ? 1 : 0, limit: 1, pass: longTasksAvailable }); - const singleRegressionLimit = relativeLimit( - baselineLong.maxSingleMs, - budgets.longTasks.maxSingleRegressionRatio, + const singleRegressionLimit = absoluteOnly ? Number.POSITIVE_INFINITY : relativeLimit( + baselineLong.maxSingleMs, budgets.longTasks.maxSingleRegressionRatio, budgets.longTasks.maxSingleNoiseAllowanceMs, ); checks.push(makeCheck( 'longTask.maxSingleMs', candidateLong.maxSingleMs, Math.min(budgets.longTasks.maxSingleMs, singleRegressionLimit), - { baseline: baselineLong.maxSingleMs, hardLimit: budgets.longTasks.maxSingleMs }, + { + ...(absoluteOnly ? {} : { baseline: baselineLong.maxSingleMs }), + hardLimit: budgets.longTasks.maxSingleMs, + }, )); - const countRegressionLimit = relativeLimit( - baselineLong.countP95, - budgets.longTasks.maxCountRegressionRatio, + const countRegressionLimit = absoluteOnly ? Number.POSITIVE_INFINITY : relativeLimit( + baselineLong.countP95, budgets.longTasks.maxCountRegressionRatio, budgets.longTasks.countNoiseAllowance, ); checks.push(makeCheck( 'longTask.countP95', candidateLong.countP95, Math.min(budgets.longTasks.maxCountP95, countRegressionLimit), - { baseline: baselineLong.countP95, hardLimit: budgets.longTasks.maxCountP95 }, + { + ...(absoluteOnly ? {} : { baseline: baselineLong.countP95 }), + hardLimit: budgets.longTasks.maxCountP95, + }, )); - const longRegressionLimit = relativeLimit( - baselineLong.totalP95Ms, - budgets.longTasks.maxTotalRegressionRatio, + const longRegressionLimit = absoluteOnly ? Number.POSITIVE_INFINITY : relativeLimit( + baselineLong.totalP95Ms, budgets.longTasks.maxTotalRegressionRatio, budgets.longTasks.noiseAllowanceMs, ); checks.push(makeCheck( 'longTask.totalP95Ms', candidateLong.totalP95Ms, Math.min(budgets.longTasks.maxTotalP95Ms, longRegressionLimit), - { baseline: baselineLong.totalP95Ms, hardLimit: budgets.longTasks.maxTotalP95Ms }, + { + ...(absoluteOnly ? {} : { baseline: baselineLong.totalP95Ms }), + hardLimit: budgets.longTasks.maxTotalP95Ms, + }, )); const candidateHeap = candidate.rows .map((row) => row.heapGrowthBytes) .filter(finite) .map((value) => Math.max(0, value)); - const baselineHeap = baseline.rows + const baselineHeap = absoluteOnly ? [] : baseline.rows .map((row) => row.heapGrowthBytes) .filter(finite) .map((value) => Math.max(0, value)); @@ -142,17 +156,22 @@ export const evaluatePerformanceBudget = ({ candidate, baseline, budgets }) => { id: 'heap.preciseGc', actual: preciseGc ? 1 : 0, limit: budgets.heap.required ? 1 : 0, pass: !budgets.heap.required || preciseGc, }); - if (budgets.heap.required && (!candidateHeap.length || !baselineHeap.length)) { + if (budgets.heap.required && (!candidateHeap.length || (!absoluteOnly && !baselineHeap.length))) { checks.push({ id: 'heap.available', actual: candidateHeap.length, limit: 1, pass: false }); - } else if (candidateHeap.length && baselineHeap.length) { + } else if (candidateHeap.length && (absoluteOnly || baselineHeap.length)) { const actual = percentile(candidateHeap, 0.95); - const base = percentile(baselineHeap, 0.95); - const regressionLimit = relativeLimit(base, budgets.heap.maxRegressionRatio, budgets.heap.noiseAllowanceBytes); + const base = absoluteOnly ? null : percentile(baselineHeap, 0.95); + const regressionLimit = absoluteOnly ? Number.POSITIVE_INFINITY : relativeLimit( + base, budgets.heap.maxRegressionRatio, budgets.heap.noiseAllowanceBytes, + ); checks.push(makeCheck( 'heap.growthP95Bytes', actual, Math.min(budgets.heap.hardMaxGrowthBytes, regressionLimit), - { baseline: base, hardLimit: budgets.heap.hardMaxGrowthBytes }, + { + ...(absoluteOnly ? {} : { baseline: base }), + hardLimit: budgets.heap.hardMaxGrowthBytes, + }, )); } @@ -178,8 +197,9 @@ export const evaluatePerformanceBudget = ({ candidate, baseline, budgets }) => { schema: 1, profile: budgets.profile, pass: failures.length === 0, + mode: absoluteOnly ? 'absolute' : 'relative', candidateFingerprint: candidate.buildFingerprint, - baselineFingerprint: baseline.buildFingerprint, + baselineFingerprint: baseline?.buildFingerprint ?? null, checks, failures, }; diff --git a/demo/smoke_glow.mjs b/demo/smoke_glow.mjs index 5b2c290b..3ae0cf35 100644 --- a/demo/smoke_glow.mjs +++ b/demo/smoke_glow.mjs @@ -29,7 +29,7 @@ const res = await page.evaluate(async () => { // 4) rgb-лампа красит градиент своим цветом (форсируем rgb у лампы) c.hass = { ...c.hass, states: { ...c.hass.states, [eid]: { ...st0, state: 'on', attributes: { ...st0.attributes, rgb_color: [255, 0, 0] } } } }; await c.updateComplete; - out.gradientColored = [...sr().querySelectorAll('defs radialGradient stop')].some((s2) => s2.getAttribute('stop-color') === 'rgb(255, 0, 0)'); + out.gradientColored = [...sr().querySelectorAll('defs radialGradient stop')].some((s2) => s2.getAttribute('stop-color') === '#ff0000'); c.hass = { ...c.hass, states: { ...c.hass.states, [eid]: st0 } }; await c.updateComplete; // 5) пятно обрезано комнатой (есть clipPath) out.clipped = sr().querySelectorAll('defs clipPath[id^="hp-glowclip"]').length > 0; @@ -79,15 +79,39 @@ const res = await page.evaluate(async () => { const offs = stops ? [...stops.querySelectorAll('stop')].map((st2) => [st2.getAttribute('offset'), st2.getAttribute('stop-opacity')]) : []; out.plateau80 = offs.length === 3 && offs[0][0] === '0%' && offs[1][0] === '70%' && offs[0][1] === offs[1][1] && offs[2][1] === '0'; - // 7а) персональный радиус источника перекрывает глобальный + // Tri-state role is live: Auto -> Never -> Auto removes and restores only + // this marker's own spatial pool. const litMarkerId = litLight.id; + const litBinding = litLight.bindingKind === 'virtual' + ? 'virtual' + : litLight.bindingKind + ':' + litLight.bindingRef; + const autoSpotCount = sr().querySelectorAll('.glowlayer circle').length; c._serverCfg = { ...c._serverCfg, markers: [ ...(c._serverCfg.markers || []).filter((m) => m.id !== litMarkerId), - { id: litMarkerId, binding: litLight.bindingKind === 'virtual' ? 'virtual' : litLight.bindingKind + ':' + litLight.bindingRef, glow_radius_cm: 150 }, + { id: litMarkerId, binding: litBinding, is_light: false }, + ] }; + c._regSignature = ''; c._maybeRebuildDevices(); c.requestUpdate(); await c.updateComplete; + out.roleNeverHidesOwnPool = sr().querySelectorAll('.glowlayer circle').length === autoSpotCount - 1; + c._serverCfg = { ...c._serverCfg, markers: (c._serverCfg.markers || []).filter((m) => m.id !== litMarkerId) }; + c._regSignature = ''; c._maybeRebuildDevices(); c.requestUpdate(); await c.updateComplete; + out.roleAutoRestoresOwnPool = sr().querySelectorAll('.glowlayer circle').length === autoSpotCount; + // 7а) персональный радиус источника перекрывает глобальный + c._serverCfg = { ...c._serverCfg, markers: [ + ...(c._serverCfg.markers || []).filter((m) => m.id !== litMarkerId), + { + id: litMarkerId, + binding: litBinding, + glow_radius_cm: 150, + glow_color: { c: '#123456', bri: 0.25 }, + }, ] }; c._regSignature = ''; c._maybeRebuildDevices(); c.requestUpdate(); await c.updateComplete; const rOwn = Number(sr().querySelector('.glowlayer circle')?.getAttribute('r')); out.perSourceRadius = Math.abs(rOwn - c._cmToUnits(150)) < 0.5; + const ownStop = sr().querySelector('defs radialGradient stop'); + out.perSourceAppearance = ownStop?.getAttribute('stop-color') === '#123456' + && Math.abs(Number(ownStop.getAttribute('stop-opacity')) - 0.428) < 0.002 + && !sr().querySelector('.glow-pools-frame')?.hasAttribute('opacity'); c._serverCfg = { ...c._serverCfg, markers: (c._serverCfg.markers || []).filter((m) => m.id !== litMarkerId) }; c._regSignature = ''; c._maybeRebuildDevices(); c.requestUpdate(); await c.updateComplete; // 6в) флаг «источник света»: умный выключатель с обычными светильниками diff --git a/demo/smoke_glow_blending.mjs b/demo/smoke_glow_blending.mjs index d9be40f2..e29f8fee 100644 --- a/demo/smoke_glow_blending.mjs +++ b/demo/smoke_glow_blending.mjs @@ -56,9 +56,9 @@ try { a = [224, 80, 32], b = [32, 112, 224], bg = [20, 30, 40], ) => { const shapes = reverse - ? `` - : ``; - const source = `${shapes}`; + ? `` + : ``; + const source = `${shapes}`; const url = URL.createObjectURL(new Blob([source], { type: 'image/svg+xml' })); try { const image = await new Promise((resolve, reject) => { @@ -120,6 +120,7 @@ try { const customBaseTunnel = card.renderRoot.querySelector('.opening-tunnels[data-layer="glow-base"]'); return { blend: poolGroup?.getAttribute('data-blend'), + poolFrameHasOpacity: poolFrame?.hasAttribute('opacity'), pools: card.renderRoot.querySelectorAll('.glow-pool').length, clippedPools, baseLayerOrder: follows(dataTunnel, fullBase) @@ -135,13 +136,26 @@ try { }; }, fixture); const pixel = (data, index) => data.slice(index * 4, index * 4 + 4); - const screen = (a, b) => a.map((value, index) => Math.round(255 - ((255 - value) * (255 - b[index])) / 255)); const over = (fg, bg, alpha) => fg.map((value, index) => Math.round(value * alpha + bg[index] * (1 - alpha))); + // Pools blend against the transparent backdrop of their isolated group, + // then that group is composited over the room. Account for the backdrop + // alpha here instead of treating the room itself as the blend backdrop. + const isolatedScreenOver = (first, second, background, alpha) => { + const outputAlpha = alpha + alpha * (1 - alpha); + return first.map((backdrop, index) => { + const source = second[index]; + const blended = 255 - ((255 - backdrop) * (255 - source)) / 255; + const sourceColor = (1 - alpha) * source + alpha * blended; + const groupPremultiplied = alpha * sourceColor + alpha * (1 - alpha) * backdrop; + return Math.round(groupPremultiplied + background[index] * (1 - outputAlpha)); + }); + }; const bg = [20, 30, 40]; - const expected = [...over(screen([224, 80, 32], [32, 112, 224]), bg, 0.7), 255]; - const expectedFallback = [...over([32, 112, 224], bg, 0.7), 255]; + const expected = [...isolatedScreenOver([224, 80, 32], [32, 112, 224], bg, 0.7), 255]; + const expectedFallback = [...over([32, 112, 224], over([224, 80, 32], bg, 0.7), 0.7), 255]; const close = (actual, wanted) => actual.every((value, index) => Math.abs(value - wanted[index]) <= 2); if (out.blend !== 'screen') throw new Error(`runtime probe did not enable screen: ${out.blend}`); + if (out.poolFrameHasOpacity) throw new Error('Glow pool frame still applies a second opacity'); if (out.pools !== 60) throw new Error(`expected 60 pools, got ${out.pools}`); if (!close(pixel(out.forward, 1), expected)) throw new Error(`screen pixel mismatch: ${pixel(out.forward, 1)} vs ${expected}`); if (!close(pixel(out.reverse, 1), expected)) throw new Error('screen result depends on marker order'); diff --git a/demo/smoke_ha_controls.mjs b/demo/smoke_ha_controls.mjs index 2cb3af02..60b0cde3 100644 --- a/demo/smoke_ha_controls.mjs +++ b/demo/smoke_ha_controls.mjs @@ -36,13 +36,15 @@ const res = await page.evaluate(async () => { c._openMarkerDialog(c._devices[0]); await c.updateComplete; const switches = [...sr().querySelectorAll('hp-dialog ha-switch')]; - out.haSwitchRendered = switches.length >= 2; // is_light + hide-from-plan at minimum + out.haSwitchRendered = switches.length >= 2; // confirmation + hide-from-plan at minimum out.haNoPlainCheckbox = !sr().querySelector('hp-dialog .srcrow input[type=checkbox]'); + out.lightRoleRadios = sr().querySelectorAll('hp-dialog input[name="marker-light-role"]').length === 3; + out.glowModeRadios = sr().querySelectorAll('hp-dialog input[name="marker-glow-mode"]').length === 3; const sliders = [...sr().querySelectorAll('hp-dialog ha-slider')]; out.haSliderRendered = sliders.length >= 2; // size + angle // --- 3) card -> control: state is pushed into the element -------------- - c._markerDialog = { ...c._markerDialog, isLight: true, hideFromPlan: false }; + c._markerDialog = { ...c._markerDialog, tapConfirm: true, hideFromPlan: false }; await c.updateComplete; out.downstreamChecked = [...sr().querySelectorAll('hp-dialog ha-switch')].some((el) => el.checked === true); @@ -52,7 +54,7 @@ const res = await page.evaluate(async () => { sw.checked = !before; sw.dispatchEvent(new Event('change', { bubbles: true })); await c.updateComplete; - out.upstreamChecked = c._markerDialog.hideFromPlan === !before || c._markerDialog.isLight === !before + out.upstreamChecked = c._markerDialog.hideFromPlan === !before || c._markerDialog.useClimateTemp === !before || c._markerDialog.tapConfirm === !before; // --- 5) slider both ways ---------------------------------------------- diff --git a/demo/srv/assets/houseplan-card.js b/demo/srv/assets/houseplan-card.js index 64c8704c..eb1264c2 100755 --- a/demo/srv/assets/houseplan-card.js +++ b/demo/srv/assets/houseplan-card.js @@ -1,5 +1,5 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f5f9700be5383ff9d6201dba36f46";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!a(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),u.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set: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=p(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??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}),(u.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,$=t=>t,x=k.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,T=`<${D}>`,P=document,R=()=>P.createComment(""),z=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,I="[ \t\n\f\r]",E=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,N=/-->/g,F=/>/g,O=RegExp(`>|${I}(?:([^\\s"'>=/]+)(${I}*=${I}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,H=/"/g,q=/^(?:script|style|textarea|title)$/i,U=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),j=U(1),W=U(2),B=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=P.createTreeWalker(P,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",r=E;for(let e=0;e"===l[0]?(r=n??E,c=-1):void 0===l[1]?c=-2:(c=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?O:'"'===l[3]?H:L):r===H||r===L?r=O:r===N||r===F?r=E:(r=O,n=void 0);const d=r===O&&t[e+1].startsWith("/>")?" ":"";o+=r===E?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,o+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const r=t.length-1,a=this.parts,[l,c]=J(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iA(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==G&&z(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){A(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=!z(t)||t!==this._$AH&&t!==B,o&&(this._$AH=t);else{const s=t;let r,a;for(t=n[0],r=0;r{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n})(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return B}}lt._$litElement$=!0,lt.finalized=!0,at.litElementHydrateSupport?.({LitElement:lt});const ct=at.litElementPolyfillSupport;ct?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ht=new WeakMap;let dt=0;class pt extends lt{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._titleId="hp-dialog-title-"+ ++dt,this._focusInitial=()=>{const t=this.querySelector("[autofocus]")||this._focusableElements()[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key)return t.preventDefault(),t.stopImmediatePropagation(),void this._requestClose();if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),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=ht.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),ht.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=this._focusRoot,e=this._opener;this._opener=null,this._focusRoot=null;const i=t?ht.get(t):void 0;i?.dialogs.delete(this),super.disconnectedCallback(),t&&i&&requestAnimationFrame(()=>{const i=ht.get(t);if(!i)return;if(!i.dialogs.size)return i.opener?.isConnected&&i.opener.focus({preventScroll:!0}),void ht.delete(t);const s=e?.closest("hp-dialog");e?.isConnected&&s&&i.dialogs.has(s)&&e.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(",");return Array.from(this.querySelectorAll(t)).filter(t=>{const e=getComputedStyle(t);return!t.hidden&&"none"!==e.display&&"hidden"!==e.visibility})}render(){const t=j` - ${this.icon?j``:G} +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="fc04a319d946937a0d7fb1f2df149fb0fe5164d616bf18f84cac66f0f69e6890";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let o=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const n=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new o(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new o("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!a(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),u.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:o}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const n=s?.call(this);o?.call(this,e),this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=p(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),o=t.litNonce;void 0!==o&&s.setAttribute("nonce",o),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const o=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==o?this.removeAttribute(s):this.setAttribute(s,o),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),o="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const n=o.fromAttribute(e,t.type);this[s]=n??this._$Ej?.get(s)??n,this._$Em=null}}requestUpdate(t,e,i,s=!1,o){if(void 0!==t){const n=this.constructor;if(!1===s&&(o=this[t]),i??=n.getPropertyOptions(t),!((i.hasChanged??y)(o,e)||i.useDefault&&i.reflect&&o===this._$Ej?.get(t)&&!this.hasAttribute(n._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:o},n){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,n??e??this[t]),!0!==o||void 0!==n)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(u.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,$=t=>t,x=k.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,T=`<${D}>`,P=document,R=()=>P.createComment(""),z=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,I="[ \t\n\f\r]",E=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,N=/-->/g,F=/>/g,O=RegExp(`>|${I}(?:([^\\s"'>=/]+)(${I}*=${I}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,H=/"/g,q=/^(?:script|style|textarea|title)$/i,U=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),j=U(1),B=U(2),G=Symbol.for("lit-noChange"),W=Symbol.for("lit-nothing"),V=new WeakMap,K=P.createTreeWalker(P,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let o,n=2===e?"":3===e?"":"",r=E;for(let e=0;e"===l[0]?(r=o??E,c=-1):void 0===l[1]?c=-2:(c=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?O:'"'===l[3]?H:L):r===H||r===L?r=O:r===N||r===F?r=E:(r=O,o=void 0);const d=r===O&&t[e+1].startsWith("/>")?" ":"";n+=r===E?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,n+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let o=0,n=0;const r=t.length-1,a=this.parts,[l,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())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iA(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==W&&z(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){A(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const o of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(o),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=W}_$AI(t,e=this,i,s){const o=this.strings;let n=!1;if(void 0===o)t=X(this,t,e,0),n=!z(t)||t!==this._$AH&&t!==G,n&&(this._$AH=t);else{const s=t;let r,a;for(t=o[0],r=0;r{const s=i?.renderBefore??e;let o=s._$litPart$;if(void 0===o){const t=i?.renderBefore??null;s._$litPart$=o=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return G}}lt._$litElement$=!0,lt.finalized=!0,at.litElementHydrateSupport?.({LitElement:lt});const ct=at.litElementPolyfillSupport;ct?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ht=new WeakMap;let dt=0;class pt extends lt{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._titleId="hp-dialog-title-"+ ++dt,this._focusInitial=()=>{const t=this.querySelector("[autofocus]")||this._focusableElements()[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key)return t.preventDefault(),t.stopImmediatePropagation(),void this._requestClose();if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),o=i[0],n=i[i.length-1];!t.shiftKey||s!==o&&i.includes(s)?t.shiftKey||s!==n||(t.preventDefault(),o.focus()):(t.preventDefault(),n.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=ht.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),ht.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=this._focusRoot,e=this._opener;this._opener=null,this._focusRoot=null;const i=t?ht.get(t):void 0;i?.dialogs.delete(this),super.disconnectedCallback(),t&&i&&requestAnimationFrame(()=>{const i=ht.get(t);if(!i)return;if(!i.dialogs.size)return i.opener?.isConnected&&i.opener.focus({preventScroll:!0}),void ht.delete(t);const s=e?.closest("hp-dialog");e?.isConnected&&s&&i.dialogs.has(s)&&e.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(",");return Array.from(this.querySelectorAll(t)).filter(t=>{const e=getComputedStyle(t);return!t.hidden&&"none"!==e.display&&"hidden"!==e.visibility})}render(){const t=j` + ${this.icon?j``:W} ${this.title} `;return this._useHaDialog?j` - `}}pt.properties={title:{type:String},icon:{type:String},wide:{type:Boolean,reflect:!0},dismissOnScrim:{type:Boolean,attribute:"dismiss-on-scrim"},hass:{attribute:!1}},pt.styles=o` + `}}pt.properties={title:{type:String},icon:{type:String},wide:{type:Boolean,reflect:!0},dismissOnScrim:{type:Boolean,attribute:"dismiss-on-scrim"},hass:{attribute:!1}},pt.styles=n` :host { display: contents; color: var(--primary-text-color, #e6e7eb); @@ -184,12 +184,12 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f display: flex; flex-direction: column; } - `,customElements.get("hp-dialog")||customElements.define("hp-dialog",pt);const ut=/^#[0-9a-fA-F]{6}$/;function _t(t,e){return"string"==typeof t&&ut.test(t)?t:e}const mt="(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])",gt=new RegExp(`^rgb\\(${mt}, ${mt}, ${mt}\\)$`);function ft(t){if(!Array.isArray(t)||t.length<3)return null;const e=t.slice(0,3);if(!e.every(t=>"number"==typeof t&&Number.isFinite(t)))return null;const[i,s,n]=e.map(t=>Math.round(Math.min(255,Math.max(0,t))));return`rgb(${i}, ${s}, ${n})`}class vt extends lt{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this._open=!1,this._pickerRaf=0,this._outsidePointerDown=t=>{this._open&&!t.composedPath().includes(this)&&this._closePicker()},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closePicker(!0))},this._queuePickerPosition=()=>{this._open&&(this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._pickerRaf=requestAnimationFrame(()=>{this._pickerRaf=0,this._positionPicker()}))}}connectedCallback(){super.connectedCallback(),document.addEventListener("pointerdown",this._outsidePointerDown,!0),document.addEventListener("scroll",this._queuePickerPosition,!0),window.addEventListener("resize",this._queuePickerPosition),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){document.removeEventListener("pointerdown",this._outsidePointerDown,!0),document.removeEventListener("scroll",this._queuePickerPosition,!0),window.removeEventListener("resize",this._queuePickerPosition),this.removeEventListener("keydown",this._keyDown,!0),this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._pickerRaf=0,super.disconnectedCallback()}_toggle(){this.disabled||(this._open?this._closePicker():(this._open=!0,this.updateComplete.then(()=>this._positionPicker())))}_closePicker(t=!1){const e=this.renderRoot.querySelector(".picker");if(e?.hidePopover)try{e.matches(":popover-open")&&e.hidePopover()}catch{}this._open=!1,t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_positionPicker(){if(!this._open)return;const t=this.renderRoot.querySelector(".trigger"),e=this.renderRoot.querySelector(".picker");if(!t||!e)return;if(e.style.visibility="hidden",e.showPopover)try{e.matches(":popover-open")||e.showPopover()}catch{}const i=t.getBoundingClientRect(),s=e.getBoundingClientRect(),n=Math.max(8,window.innerWidth-s.width-8);let o=i.left;o+s.width>window.innerWidth-8&&(o=i.right-s.width),o=Math.min(n,Math.max(8,o));const r=i.bottom+7,a=i.top-7-s.height;let l=r;l=r+s.height>window.innerHeight-8&&a>=8?a:Math.min(Math.max(8,l),Math.max(8,window.innerHeight-s.height-8)),e.style.left=`${Math.round(o)}px`,e.style.top=`${Math.round(l)}px`,e.style.visibility=""}_emit(t,e){const i=Number.isFinite(e)?e:Number(this.opacity)||0,s=Math.min(1,Math.max(0,i));this.color=t,this.opacity=s,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:t,opacity:s},bubbles:!0,composed:!0}))}render(){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),e=_t(this.color,"#607d8b"),i=`${this.label||"Color"}: ${e}, ${t}%`;return j` - ${this.label?j`${this.label}`:G} + `,customElements.get("hp-dialog")||customElements.define("hp-dialog",pt);const ut=/^#[0-9a-fA-F]{6}$/;function _t(t,e){return"string"==typeof t&&ut.test(t)?t:e}const mt="(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])",gt=new RegExp(`^rgb\\(${mt}, ${mt}, ${mt}\\)$`);function ft(t){if(!Array.isArray(t)||t.length<3)return null;const e=t.slice(0,3);if(!e.every(t=>"number"==typeof t&&Number.isFinite(t)))return null;const[i,s,o]=e.map(t=>Math.round(Math.min(255,Math.max(0,t))));return`rgb(${i}, ${s}, ${o})`}class vt extends lt{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this._open=!1,this._pickerRaf=0,this._outsidePointerDown=t=>{this._open&&!t.composedPath().includes(this)&&this._closePicker()},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closePicker(!0))},this._queuePickerPosition=()=>{this._open&&(this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._pickerRaf=requestAnimationFrame(()=>{this._pickerRaf=0,this._positionPicker()}))}}connectedCallback(){super.connectedCallback(),document.addEventListener("pointerdown",this._outsidePointerDown,!0),document.addEventListener("scroll",this._queuePickerPosition,!0),window.addEventListener("resize",this._queuePickerPosition),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){document.removeEventListener("pointerdown",this._outsidePointerDown,!0),document.removeEventListener("scroll",this._queuePickerPosition,!0),window.removeEventListener("resize",this._queuePickerPosition),this.removeEventListener("keydown",this._keyDown,!0),this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._pickerRaf=0,super.disconnectedCallback()}_toggle(){this.disabled||(this._open?this._closePicker():(this._open=!0,this.updateComplete.then(()=>this._positionPicker())))}_closePicker(t=!1){const e=this.renderRoot.querySelector(".picker");if(e?.hidePopover)try{e.matches(":popover-open")&&e.hidePopover()}catch{}this._open=!1,t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_positionPicker(){if(!this._open)return;const t=this.renderRoot.querySelector(".trigger"),e=this.renderRoot.querySelector(".picker");if(!t||!e)return;if(e.style.visibility="hidden",e.showPopover)try{e.matches(":popover-open")||e.showPopover()}catch{}const i=t.getBoundingClientRect(),s=e.getBoundingClientRect(),o=Math.max(8,window.innerWidth-s.width-8);let n=i.left;n+s.width>window.innerWidth-8&&(n=i.right-s.width),n=Math.min(o,Math.max(8,n));const r=i.bottom+7,a=i.top-7-s.height;let l=r;l=r+s.height>window.innerHeight-8&&a>=8?a:Math.min(Math.max(8,l),Math.max(8,window.innerHeight-s.height-8)),e.style.left=`${Math.round(n)}px`,e.style.top=`${Math.round(l)}px`,e.style.visibility=""}_emit(t,e){const i=Number.isFinite(e)?e:Number(this.opacity)||0,s=Math.min(1,Math.max(0,i));this.color=t,this.opacity=s,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:t,opacity:s},bubbles:!0,composed:!0}))}render(){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),e=_t(this.color,"#607d8b"),i=this.showOpacity?`${this.label||"Color"}: ${e}, ${t}%`:`${this.label||"Color"}: ${e}`;return j` + ${this.label?j`${this.label}`:W} ${this._open&&!this.disabled?j` -
- ${this.opacityLabel} - this._emit(e,Number(t.target.value)/100)} /> - this._emit(e,Number(t.target.value)/100)} /> - % -
- `:G} - `}}vt.properties={label:{type:String},opacityLabel:{type:String,attribute:"opacity-label"},color:{type:String},opacity:{type:Number},disabled:{type:Boolean,reflect:!0},_open:{state:!0}},vt.styles=o` + ${this.showOpacity?j`
+ ${this.opacityLabel} + this._emit(e,Number(t.target.value)/100)} /> + this._emit(e,Number(t.target.value)/100)} /> + % +
`:W} + `:W} + `}}vt.properties={label:{type:String},opacityLabel:{type:String,attribute:"opacity-label"},color:{type:String},opacity:{type:Number},disabled:{type:Boolean,reflect:!0},showOpacity:{type:Boolean,attribute:"show-opacity"},_open:{state:!0}},vt.styles=n` :host { position: relative; display: inline-flex; @@ -329,7 +329,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f opacity: .5; pointer-events: none; } - `,customElements.get("hp-color-opacity")||customElements.define("hp-color-opacity",vt);const yt=o` + `,customElements.get("hp-color-opacity")||customElements.define("hp-color-opacity",vt);const yt=n` :host { --hp-bg: var(--card-background-color, #16212e); --hp-line: var(--divider-color, #2b3d4f); @@ -831,8 +831,8 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f pointer-events: none; } /* The isolated group makes source pools blend with one another, never with - the room data fill, Glow base, paper or backdrop. Outer opacity is the - existing 0.7 and is applied once after this flat group is composited. */ + the room data fill, Glow base, paper or backdrop. Per-stop alpha already + contains the shared 0.7 ceiling; an outer opacity would apply it twice. */ .glow-pools-frame, .glow-pools { isolation: isolate; @@ -2256,6 +2256,35 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f .srcrow > span:first-of-type { white-space: nowrap; } .colorrow .opl { color: var(--hp-muted); font-size: var(--fs-s); } .colorrow .opv { font-size: var(--fs-s); min-width: 34px; text-align: right; } + .markerlightgroup { + min-width: 0; + margin: var(--sp-5) 0 0; + padding: var(--sp-4); + border: 1px solid var(--hp-line); + border-radius: var(--rad-m); + } + .markerlightgroup legend { + padding: 0 var(--sp-2); + color: var(--hp-txt); + font-weight: 600; + } + .markerlightgroup[disabled] { opacity: .62; } + .markerlighttip { margin: 0 0 var(--sp-3) !important; } + .markerradios { display: grid; gap: var(--sp-1); min-width: 0; } + .markerlightgroup .srcrow > span:first-of-type { + min-width: 0; + white-space: normal; + overflow-wrap: anywhere; + } + .markerglowvalue { margin: var(--sp-3) 0; flex-wrap: wrap; } + .markerglowvalue hp-color-opacity { flex: none; } + .markerlightdisabled { + display: flex; + align-items: center; + gap: var(--sp-2); + margin-top: var(--sp-2) !important; + } + .markerlightdisabled ha-icon { --mdc-icon-size: 18px; flex: none; } .planrow { display: flex; align-items: center; @@ -3172,17 +3201,17 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f z-index: 120; max-width: 90vw; } - `;function bt(t){const e=[];if(1!==t.scale&&e.push(`--dev-scale:${t.scale}`),"icon_ripple"===t.display){e.push(`--ripple-scale:${t.rippleScale}`);const n=(i=t.rippleColor,s=null,"string"==typeof i&&(ut.test(i)||gt.test(i))?i:s);n&&e.push(`--ripple-color:${n}`)}var i,s;return e}function wt(t,e){const i=t.activity,s=t.classes.includes("activity-gen2");return j` - ${"none"!==i?j``:G} - ${e.newDevice?j``:G} - ${t.haDisabled?j``:G} + `;function bt(t){const e=[];if(1!==t.scale&&e.push(`--dev-scale:${t.scale}`),"icon_ripple"===t.display){e.push(`--ripple-scale:${t.rippleScale}`);const o=(i=t.rippleColor,s=null,"string"==typeof i&&(ut.test(i)||gt.test(i))?i:s);o&&e.push(`--ripple-color:${o}`)}var i,s;return e}function wt(t,e){const i=t.activity,s=t.classes.includes("activity-gen2");return j` + ${"none"!==i?j``:W} + ${e.newDevice?j``:W} + ${t.haDisabled?j``:W} ${null!=t.valueText?j`${t.valueText}`:j``} - ${null!=t.tempText&&null==t.valueText?j`${t.tempText}°`:G} - ${null!=t.humText&&null==t.valueText?j`${t.humText}%`:G} - ${null!=t.lqiText?j`${t.lqiText}`:G} - `}const kt=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),$t=[{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 xt(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 St=xt($t),Mt={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"},Ct="mdi:chip";function Dt(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??St)if(t.test(s))return e;return Ct}const Tt=["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 Pt=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,Rt=Math.ceil,zt=Math.floor,At="[BigNumber Error] ",It=At+"Number primitive has more than 15 significant digits: ",Et=1e14,Nt=14,Ft=9007199254740991,Ot=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],Lt=1e7,Ht=1e9;function qt(t){var e=0|t;return t>0||t===e?e:e-1}function Ut(t){for(var e,i,s=1,n=t.length,o=t[0]+"";sc^i?1:-1;for(a=(l=n.length)<(c=o.length)?l:c,r=0;ro[r]^i?1:-1;return l==c?0:l>c^i?1:-1}function Wt(t,e,i,s){if(ti||t!==zt(t))throw Error(At+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Bt(t){var e=t.c.length-1;return qt(t.e/Nt)==e&&t.c[e]%2!=0}function Gt(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Vt(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?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>y?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!Pt.test(d=String(t)))return n(p,d,c);p.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(a=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(a<0&&(a=l),a+=+d.slice(l+1),d=d.substring(0,l)):a<0&&(a=d.length)}else{if(Wt(e,2,x.length,"Base"),10==e&&S)return P(p=new M(t),_+p.e+1,m);if(d=String(t),c="number"==typeof t){if(0*t!=0)return n(p,d,c,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(It+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,h=d.length;la){a=h;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return n(p,String(t),c,e)}c=!1,(a=(d=s(d,e,10,p.s)).indexOf("."))>-1?d=d.replace(".",""):a=d.length}for(l=0;48===d.charCodeAt(l);l++);for(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(t>Ft||t!==zt(t)))throw Error(It+p.s*t);if((a=a-l-1)>y)p.c=p.e=null;else if(a=f)?Gt(l,r):Vt(l,r,"0");else if(o=(t=P(new M(t),e,i)).e,a=(l=Ut(t.c)).length,1==s||2==s&&(e<=o||o<=g)){for(;ar),l=Vt(l,o,"0"),o+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=o-a)>0)for(o+1==a&&(l+=".");e--;l+="0");return t.s<0&&n?"-"+l:l}function D(t,e){for(var i,s,n=1,o=new M(t[0]);n=10;n/=10,s++);return(i=s+i*Nt-1)>y?t.c=t.e=null:i=10;a/=10,n++);if((o=e-n)<0)o+=Nt,r=e,l=d[c=0],h=zt(l/p[n-r-1]%10);else if((c=Rt((o+1)/Nt))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,n=1,r=(o%=Nt)-Nt+1}else{for(l=a=d[c],n=1;a>=10;a/=10,n++);h=(r=(o%=Nt)-Nt+n)<0?0:zt(l/p[n-r-1]%10)}if(s=s||e<0||null!=d[c+1]||(r<0?l:l%p[n-r-1]),s=i<4?(h||s)&&(0==i||i==(t.s<0?3:2)):h>5||5==h&&(4==i||s||6==i&&(o>0?r>0?l/p[n-r]:0:d[c-1])%10&1||i==(t.s<0?8:7)),e<1||!d[0])return d.length=0,s?(e-=t.e+1,d[0]=p[(Nt-e%Nt)%Nt],t.e=-e||0):d[0]=t.e=0,t;if(0==o?(d.length=c,a=1,c--):(d.length=c+1,a=p[Nt-o],d[c]=r>0?zt(l/p[n-r]%p[r])*a:0),s)for(;;){if(0==c){for(o=1,r=d[0];r>=10;r/=10,o++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);o!=a&&(t.e++,d[0]==Et&&(d[0]=1));break}if(d[c]+=a,d[c]!=Et)break;d[c--]=0,a=1}for(o=d.length;0===d[--o];d.pop());}t.e>y?t.c=t.e=null:t.e=f?Gt(e,i):Vt(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(At+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(Wt(i=t[e],0,Ht,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(Wt(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(Wt(i[0],-Ht,0,e),Wt(i[1],0,Ht,e),g=i[0],f=i[1]):(Wt(i,-Ht,Ht,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)Wt(i[0],-Ht,-1,e),Wt(i[1],1,Ht,e),v=i[0],y=i[1];else{if(Wt(i,-Ht,Ht,e),!i)throw Error(At+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(At+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(At+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(Wt(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(Wt(i=t[e],0,Ht,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(At+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(At+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>=-Ht&&n<=Ht&&n===zt(n)){if(0===s[0]){if(0===n&&1===s.length)return!0;break t}if((e=(n+1)%Nt)<1&&(e+=Nt),String(s[0]).length==e){for(e=0;e=Et||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(At+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(o=9007199254740992,r=Math.random()*o&2097151?function(){return zt(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,n,o,a=0,l=[],c=new M(u);if(null==t?t=_:Wt(t,0,Ht),n=Rt(t/Nt),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(n*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(o%1e14),a+=2);a=n/2}else{if(!crypto.randomBytes)throw b=!1,Error(At+"crypto unavailable");for(e=crypto.randomBytes(n*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(o%1e14),a+=7);a=n/7}if(!b)for(;a=10;o/=10,a++);ai-1&&(null==r[n+1]&&(r[n+1]=0),r[n+1]+=r[n]/i|0,r[n]%=i)}return r.reverse()}return function(s,n,o,r,a){var l,c,h,d,p,u,g,f,v=s.indexOf("."),y=_,b=m;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(n)).pow(s.length-v),k=d,f.c=e(Vt(Ut(u.c),u.e,"0"),10,o,t),f.e=f.c.length),h=d=(g=e(s,n,o,a?(l=x,t):(l=t,x))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--h:(u.c=g,u.e=h,u.s=r,g=(u=i(u,f,y,b,o)).c,p=u.r,h=u.e),v=g[c=h+y+1],d=o/2,p=p||c<0||null!=g[c+1],p=b<4?(null!=v||p)&&(0==b||b==(u.s<0?3:2)):v>d||v==d&&(4==b||p||6==b&&1&g[c-1]||b==(u.s<0?8:7)),c<1||!g[0])s=p?Vt(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=c,p)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=Vt(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,n,o,r,a=0,l=t.length,c=e%Lt,h=e/Lt|0;for(t=t.slice();l--;)a=((n=c*(o=t[l]%Lt)+(s=h*o+(r=t[l]/Lt|0)*c)%Lt*Lt+a)/i|0)+(s/Lt|0)+h*r,t[l]=n%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var n,o;if(i!=s)o=i>s?1:-1;else for(n=o=0;ne[n]?1:-1;break}return o}function i(t,e,i,s){for(var n=0;i--;)t[i]-=n,n=t[i]1;t.splice(0,1));}return function(s,n,o,r,a){var l,c,h,d,p,u,_,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,a||(a=Et,c=qt(s.e/Nt)-qt(n.e/Nt),S=S/Nt|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,(p=zt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),$=D.length,w=C.length),b=$,v=(f=C.slice(0,$)).length;v<$;f[v++]=0);x=D.slice(),x=[0].concat(x),k=D[0],D[1]>=a/2&&k++;do{if(p=0,(l=e(D,f,$,v))<0){if(y=f[0],$!=v&&(y=y*a+(f[1]||0)),(p=zt(y/k))>1)for(p>=a&&(p=a-1),_=(u=t(D,p,a)).length,v=f.length;1==e(u,f,_,v);)p--,i(u,$<_?x:D,_,a),_=u.length,l=1;else 0==p&&(l=p=1),_=(u=D.slice()).length;if(_=10;S/=10,h++);P(m,o+(m.e=h+c*Nt-1)+1,r,d)}else m.e=c,m.r=+d;return m}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,n=function(t,e,i,s){var n,o=i?e:e.replace(d,"");if(h.test(o))t.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(a,function(t,e,i){return n="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=n?t:e}),s&&(n=s,o=o.replace(l,"$1").replace(c,"0.$1")),e!=o))return new M(o,n);if(M.DEBUG)throw Error(At+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},p.absoluteValue=p.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},p.comparedTo=function(t,e){return jt(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,n,o=this;if(null!=t)return Wt(t,0,Ht),null==e?e=m:Wt(e,0,8),P(new M(o),t+o.e+1,e);if(!(i=o.c))return null;if(s=((n=i.length-1)-qt(this.e/Nt))*Nt,n=i[n])for(;n%10==0;n/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,m)},p.dividedToIntegerBy=p.idiv=function(t,e){return i(this,new M(t,e),0,1)},p.exponentiatedBy=p.pow=function(t,e){var i,s,n,o,r,a,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(At+"Exponent not an integer: "+R(t));if(null!=e&&(e=new M(e)),r=t.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!t.c||!t.c[0])return c=new M(Math.pow(+R(h),r?t.s*(2-Bt(t)):+R(t))),e?c.mod(e):c;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&h.isInteger()&&e.isInteger())&&(h=h.mod(e))}else{if(t.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||r&&h.c[1]>=24e7:h.c[0]<8e13||r&&h.c[0]<=9999975e7)))return o=h.s<0&&Bt(t)?-0:0,h.e>-1&&(o=1/o),new M(a?1/o:o);k&&(o=Rt(k/Nt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Bt(t)):l=(n=Math.abs(+R(t)))%2,c=new M(u);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):s&&(c=c.mod(e))}if(n){if(0===(n=zt(n/2)))break;l=n%2}else if(P(t=t.times(i),t.e+1,1),t.e>14)l=Bt(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:(a&&(c=u.div(c)),e?c.mod(e):o?P(c,k,m,void 0):c)},p.integerValue=function(t){var e=new M(this);return null==t?t=m:Wt(t,0,8),P(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===jt(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return jt(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=jt(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&qt(this.e/Nt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return jt(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=jt(this,new M(t,e)))||0===e},p.isNaN=function(){return!this.s},p.isNegative=function(){return this.s<0},p.isPositive=function(){return this.s>0},p.isZero=function(){return!!this.c&&0==this.c[0]},p.minus=function(t,e){var i,s,n,o,r=this,a=r.s;if(e=(t=new M(t,e)).s,!a||!e)return new M(NaN);if(a!=e)return t.s=-e,r.plus(t);var l=r.e/Nt,c=t.e/Nt,h=r.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?r:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?r:3==m?-0:0)}if(l=qt(l),c=qt(c),h=h.slice(),a=l-c){for((o=a<0)?(a=-a,n=h):(c=l,n=d),n.reverse(),e=a;e--;n.push(0));n.reverse()}else for(s=(o=(a=h.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;h[i++]=0);for(e=Et-1;s>a;){if(h[--s]=0;){for(i=0,p=y[n]%g,u=y[n]/g|0,o=n+(r=l);o>n;)i=((c=p*(c=v[--r]%g)+(a=u*c+(h=v[r]/g|0)*p)%g*g+_[o]+i)/m|0)+(a/g|0)+u*h,_[o--]=c%m;_[o]=i}return i?++s:_.splice(0,1),T(t,_,s)},p.negated=function(){var t=new M(this);return t.s=-t.s||null,t},p.plus=function(t,e){var i,s=this,n=s.s;if(e=(t=new M(t,e)).s,!n||!e)return new M(NaN);if(n!=e)return t.s=-e,s.minus(t);var o=s.e/Nt,r=t.e/Nt,a=s.c,l=t.c;if(!o||!r){if(!a||!l)return new M(n/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*n)}if(o=qt(o),r=qt(r),a=a.slice(),n=o-r){for(n>0?(r=o,i=l):(n=-n,i=a),i.reverse();n--;i.push(0));i.reverse()}for((n=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=n),n=0;e;)n=(a[--e]=a[e]+l[e]+n)/Et|0,a[e]=Et===a[e]?0:a[e]%Et;return n&&(a=[n].concat(a),++r),T(t,a,r)},p.precision=p.sd=function(t,e){var i,s,n,o=this;if(null!=t&&t!==!!t)return Wt(t,1,Ht),null==e?e=m:Wt(e,0,8),P(new M(o),t,e);if(!(i=o.c))return null;if(s=(n=i.length-1)*Nt+1,n=i[n]){for(;n%10==0;n/=10,s--);for(n=i[0];n>=10;n/=10,s++);}return t&&o.e+1>s&&(s=o.e+1),s},p.shiftedBy=function(t){return Wt(t,-9007199254740991,Ft),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,n,o,r=this,a=r.c,l=r.s,c=r.e,h=_+4,d=new M("0.5");if(1!==l||!a||!a[0])return new M(!l||l<0&&(!a||a[0])?NaN:a?r:1/0);if(0==(l=Math.sqrt(+R(r)))||l==1/0?(((e=Ut(a)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=qt((c+1)/2)-(c<0||c%2),s=new M(e=l==1/0?"5e"+c:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+c)):s=new M(l+""),s.c[0])for((l=(c=s.e)+h)<3&&(l=0);;)if(o=s,s=d.times(o.plus(i(r,o,h,1))),Ut(o.c).slice(0,l)===(e=Ut(s.c)).slice(0,l)){if(s.e0&&_>0){for(o=_%a||a,h=u.substr(0,o);o<_;o+=a)h+=c+u.substr(o,a);l>0&&(h+=c+u.slice(o)),p&&(h="-"+h)}s=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,n,o,r,a,l,c,h,d,p,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(At+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+R(l));if(!f)return new M(g);for(e=new M(u),h=s=new M(u),n=c=new M(u),_=Ut(f),r=e.e=_.length-g.e-1,e.c[0]=Ot[(a=r%Nt)<0?Nt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:h:l,a=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,p=i(h,n,r*=2,m).minus(g).abs().comparedTo(i(c,s,r,m).minus(g).abs())<1?[h,n]:[c,s],y=a,p},p.toNumber=function(){return+R(this)},p.toPrecision=function(t,e){return null!=t&&Wt(t,1,Ht),C(this,t,e,2)},p.toString=function(t){var e,i=this,n=i.s,o=i.e;return null===o?n?(e="Infinity",n<0&&(e="-"+e)):e="NaN":(null==t?e=o<=g||o>=f?Gt(Ut(i.c),o):Vt(Ut(i.c),o,"0"):10===t&&S?e=Vt(Ut((i=P(new M(i),_+o+1,m)).c),i.e,"0"):(Wt(t,2,x.length,"Base"),e=s(Vt(Ut(i.c),o,"0"),10,t,n,!0)),n<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return R(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),Yt=class{key;left=null;right=null;constructor(t){this.key=t}},Jt=class extends Yt{constructor(t){super(t)}},Zt=class{size=0;modificationCount=0;splayCount=0;splay(t){const e=this.root;if(null==e)return this.compare(t,t),-1;let i=null,s=null,n=null,o=null,r=e;const a=this.compare;let l;for(;;)if(l=a(r.key,t),l>0){let e=r.left;if(null==e)break;if(l=a(e.key,t),l>0&&(r.left=e.right,e.right=r,r=e,e=r.left,null==e))break;null==i?s=r:i.left=r,i=r,r=e}else{if(!(l<0))break;{let e=r.right;if(null==e)break;if(l=a(e.key,t),l<0&&(r.right=e.left,e.left=r,r=e,e=r.right,null==e))break;null==n?o=r:n.right=r,n=r,r=e}}return null!=n&&(n.right=r.left,r.left=o),null!=i&&(i.left=r.right,r.right=s),this.root!==r&&(this.root=r,this.splayCount++),l}splayMin(t){let e=t,i=e.left;for(;null!=i;){const t=i;e.left=t.right,t.right=e,e=t,i=e.left}return e}splayMax(t){let e=t,i=e.right;for(;null!=i;){const t=i;e.right=t.left,t.left=e,e=t,i=e.right}return e}_delete(t){if(null==this.root)return null;if(0!=this.splay(t))return null;let e=this.root;const i=e,s=e.left;if(this.size--,null==s)this.root=e.right;else{const t=e.right;e=this.splayMax(s),e.right=t,this.root=e}return this.modificationCount++,i}addNewRoot(t,e){this.size++,this.modificationCount++;const i=this.root;null!=i?(e<0?(t.left=i,t.right=i.right,i.right=null):(t.right=i,t.left=i.left,i.left=null),this.root=t):this.root=t}_first(){const t=this.root;return null==t?null:(this.root=this.splayMin(t),this.root)}_last(){const t=this.root;return null==t?null:(this.root=this.splayMax(t),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(t){return this.validKey(t)&&0==this.splay(t)}defaultCompare(){return(t,e)=>te?1:0}wrap(){return{getRoot:()=>this.root,setRoot:t=>{this.root=t},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:t=>{this.splayCount=t},splay:t=>this.splay(t),has:t=>this.has(t)}}},Xt=class t extends Zt{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 Jt(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Jt(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 Jt(t.key);return function t(e,i){let s,n;do{if(s=e.left,n=e.right,null!=s){const e=new Jt(s.key);i.left=e,t(s,e)}if(null!=n){const t=new Jt(n.key);i.right=t,e=n,i=t}}while(null!=n)}(t,e),e}toSet(){return this.clone()}entries(){return new ee(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new te(this.wrap())}[Symbol.toStringTag]="[object Set]"},Qt=class{tree;path=new Array;modificationCount=null;splayCount;constructor(t){this.tree=t,this.splayCount=t.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const t=this.path[this.path.length-1];return this.getValue(t)}rebuildPath(t){this.path.splice(0,this.path.length),this.tree.splay(t),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(t){for(;null!=t;)this.path.push(t),t=t.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let t=this.tree.getRoot();for(;null!=t;)this.path.push(t),t=t.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let t=this.path[this.path.length-1],e=t.right;if(null!=e){for(;null!=e;)this.path.push(e),e=e.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===t;)t=this.path.pop();return this.path.length>0}},te=class extends Qt{getValue(t){return t.key}},ee=class extends Qt{getValue(t){return[t.key,t.key]}},ie=t=>()=>t,se=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):ie(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function ne(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)):ie(!1);return(t,i,s)=>{const n=t.x,o=t.y,r=s.x,a=s.y,l=o.minus(a).times(i.x.minus(r)).minus(n.minus(r).times(i.y.minus(a)));return e(l,n,o,r,a)?0:l.comparedTo(0)}}var oe=t=>t,re=t=>{if(t){const e=new Xt(se(t)),i=new Xt(se(t)),s=(t,e)=>e.addAndReturn(t),n=t=>({x:s(t.x,e),y:s(t.y,i)});return n({x:new Kt(0),y:new Kt(0)}),n}return oe},ae=t=>({set:t=>{le=ae(t)},reset:()=>ae(t),compare:se(t),snap:re(t),orient:ne(t)}),le=ae(),ce=(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),he=(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}}},de=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),pe=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ue=t=>pe(t,t).sqrt(),_e=(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 de(n,s).div(ue(n)).div(ue(s))},me=(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(ue(n)).div(ue(s))},ge=(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)))},ve=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:Me.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:_e(this.point,t.point,s.point),cosine:me(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:n,cosine:o}=e.get(t),{sine:r,cosine:a}=e.get(s);return n.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?o.isLessThan(a)?1:o.isGreaterThan(a)?-1:0:n.isLessThan(0)&&r.isLessThan(0)?o.isLessThan(a)?-1:o.isGreaterThan(a)?1:0:r.isLessThan(n)?-1:r.isGreaterThan(n)?1:0}}},ye=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}}},be=class{exteriorRing;interiorRings;constructor(t){this.exteriorRing=t,t.poly=this,this.interiorRings=[]}addInterior(t){this.interiorRings.push(t),t.poly=this}getGeom(){const t=this.exteriorRing.getGeom();if(null===t)return null;const e=[t];for(let t=0,i=this.interiorRings.length;t0?(this.tree.delete(e),i.push(t)):(this.segments.push(e),e.prev=s)}else{if(s&&n){const t=s.getIntersection(n);if(null!==t){if(!s.isAnEndpoint(t)){const e=this._splitSafely(s,t);for(let t=0,s=e.length;t0)return-1;const s=e.comparePoint(t.rightSE.point);return 0!==s?s:-1}if(i.isGreaterThan(s)){if(r.isLessThan(a)&&r.isLessThan(c))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(c))return 1;const i=e.comparePoint(t.leftSE.point);if(0!==i)return i;const s=t.comparePoint(e.rightSE.point);return s<0?1:s>0?-1:1}if(r.isLessThan(a))return-1;if(r.isGreaterThan(a))return 1;if(n.isLessThan(o)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(n.isGreaterThan(o)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!n.eq(o)){const t=l.minus(r),e=n.minus(i),h=c.minus(a),d=o.minus(s);if(t.isGreaterThan(e)&&h.isLessThan(d))return 1;if(t.isLessThan(e)&&h.isGreaterThan(d))return-1}return n.isGreaterThan(o)?1:n.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++Se,this.leftSE=t,t.segment=this,t.otherSE=e,this.rightSE=e,e.segment=this,e.otherSE=t,this.rings=i,this.windings=s}static fromRing(e,i,s){let n,o,r;const a=ve.comparePoints(e,i);if(a<0)n=e,o=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);n=i,o=e,r=-1}const l=new ve(n,!0),c=new ve(o,!1);return new t(l,c,[s],[r])}replaceRightSE(t){this.rightSE=t,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const t=this.leftSE.point.y,e=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:t.isLessThan(e)?t:e},ur:{x:this.rightSE.point.x,y:t.isGreaterThan(e)?t:e}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(t){return t.x.eq(this.leftSE.point.x)&&t.y.eq(this.leftSE.point.y)||t.x.eq(this.rightSE.point.x)&&t.y.eq(this.rightSE.point.y)}comparePoint(t){return le.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=he(e,i);if(null===s)return null;const n=this.leftSE.point,o=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=ce(e,r)&&0===this.comparePoint(r),c=ce(i,n)&&0===t.comparePoint(n),h=ce(e,a)&&0===this.comparePoint(a),d=ce(i,o)&&0===t.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?a:null;if(c)return h&&n.x.eq(a.x)&&n.y.eq(a.y)?null:n;if(l)return d&&o.x.eq(r.x)&&o.y.eq(r.y)?null:r;if(d&&h)return null;if(d)return o;if(h)return a;const p=((t,e,i,s)=>{if(e.x.isZero())return fe(i,s,t.x);if(s.x.isZero())return fe(t,e,i.x);if(e.y.isZero())return ge(i,s,t.y);if(s.y.isZero())return ge(t,e,i.y);const n=de(e,s);if(n.isZero())return null;const o={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=de(o,e).div(n),a=de(o,s).div(n),l=t.x.plus(a.times(e.x)),c=i.x.plus(r.times(s.x)),h=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(n,this.vector(),r,t.vector());return null===p?null:ce(s,p)?le.snap(p):null}split(e){const i=[],s=void 0!==e.events,n=new ve(e,!0),o=new ve(e,!1),r=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(n);const a=new t(n,r,this.rings.slice(),this.windings.slice());return ve.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),ve.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}},Ce=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=le.snap({x:new Kt(t[0][0]),y:new Kt(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;exe.run("union",t,e),Re=(t,...e)=>xe.run("intersection",t,e),ze=(t,...e)=>xe.run("difference",t,e);function Ae(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function Ie(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 Ee(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 Ne(t,e,i=1){const s=t[0].toFixed(i),n=t[1].toFixed(i),o=e[0].toFixed(i),r=e[1].toFixed(i),a=s=3?t.poly:t&&null!=t.x&&null!=t.y&&null!=t.w&&null!=t.h?[[t.x,t.y],[t.x+t.w,t.y],[t.x+t.w,t.y+t.h],[t.x,t.y+t.h]]:null}function Oe(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 Le(t){const e=[],i=new Set;for(const s of t||[]){const t=Fe(s);if(t)for(let s=0;s=90?t-=180:t<-90&&(t+=180),s.step&&s.step>0){const i=Math.sqrt(d),o=Math.min(Math.max(s.length||0,0)/2,i/2);let a=Math.round(p*i/s.step)*s.step;Math.abs(p*i-i/2)<=s.step/2&&(a=i/2),a=Math.max(o,Math.min(i-o,a));const l=a/i;n={x:e+l*c,y:r+l*h,angle:t}}else n={x:u[0],y:u[1],angle:t}}}return n}function qe(t,e,i,s,n,o=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,c=o;for(const e of Le(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(a[0]*(e[1]-t[1])-a[1]*(e[0]-t[0]));if(s(i[0])>o||s(i[1])>o)continue;const n=(i[0][0]-t[0])*a[0]+(i[0][1]-t[1])*a[1],r=(i[1][0]-t[0])*a[0]+(i[1][1]-t[1])*a[1],h=Math.min(n,r),d=Math.max(n,r),p=h>0?h:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],g=(h+d)/2;return{wallA:m(h),wallB:m(d),sideA:u,sideB:_,midA:m((h-p)/2),midB:m((p+d)/2),wallCenter:m(g),centered:Math.abs(g)<=n}}function Ue(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 je(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}function Be(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;let r=o?((t[0]-e[0])*s+(t[1]-e[1])*n)/o:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*n))}function Ge(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let n=0;nn&&r<-n||o<-n&&r>n)&&(a>n&&l<-n||a<-n&&l>n)}function Ze(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),n=Math.min(...i),o=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(o-n,a-r)||1;let c=0,h=0,d=0;for(let e=0;e1e-9?[h/(3*c),d/(3*c)]:[(n+o)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!We([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=(a-r)/e;for(let e=-4;e<=4;e++)for(let n=-4;n<=4;n++){const o=t+s*e/4,r=i+l*n/4,a=u(o,r);a>m&&(m=a,_=[o,r])}}return _||Xe(t)||t[0]}function Xe(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(Ke(s,t,e))return s;for(let s=0;sti(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&ti(e,t,i)))}function si(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 oi(t,e,i){for(let s=0;s1&&je(i[0],i[i.length-1],e)&&i.pop(),i}function ai(t,e,i){const[s,n]=t.split(":");return"device"===s?n:"entity"===s?"lg_"+n:e&&e.startsWith("v_")?e:i()}function li(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 hi(t,e,i,s){if(t.length<2)return;const n=e.x+s,o=e.x+e.w-s,r=e.y+s,a=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;st.startsWith("cover."))||null}const wi=["automation","script","scene"];const ki="—",$i="{}";function xi(t){const e=String(t??"").trim();if(!e)return null;let i=e,s="";const n=e.indexOf(":");if(n>=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 Si(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),n=xi(s?`${i}:${s}`:i);return n?`{${n.entity}${n.attr?`:${n.attr}`:""}}`:""}function Mi(t,e,i){const s=String(e??"").trim();if(!s)return null;const n=t?.states?.[s];if(!n)return null;const o=String(i??"").trim(),r=t=>t.slice(0,60);if(o){const e=function(t){if(null==t)return null;if(Array.isArray(t)){const e=t.map(t=>null==t?"":String(t)).join(", ");return e?e.slice(0,60):null}if("object"==typeof t)return null;const e=String(t);return""===e?null:e.slice(0,60)}(n.attributes?.[o]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,n,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=n.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function Ci(t,e,i){const s=String(e??"").trim(),n=String(i??"").trim()||s;if(!n)return t.text;const o=t.formatted&&s?function(t,e){if(!e)return t;const i=t.replace(/\s+$/,"");return i.endsWith(e)?i.slice(0,i.length-e.length).replace(/\s+$/,""):t}(t.text,s):t.text;return`${o} ${n}`}function Di(t,e){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 ki;const o=(e?.attr||"").trim(),r=Mi(t,i,o||null);if(null===r)return ki;return Ci(r,o?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}const Ti=20;function Pi(t){const e=Number(t?.scale);if(Number.isFinite(e)&&e>0)return Math.min(20,Math.max(.15,e));return{s:.7,m:1,l:1.5}[String(t?.size??"")]??1}function Ri(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 zi="#55606c",Ai=.55,Ii={c:"#607d8b",a:.18};function Ei(t,e=Ii){const i=t&&"object"==typeof t?t:null,s=i?.a;return{c:_t(i?.c,e.c),a:"number"==typeof s&&Number.isFinite(s)?Math.min(1,Math.max(0,s)):e.a}}function Ni(t,e){const i=Ei(t),s=e?.settings?.custom_fill;return s&&"object"==typeof s?Ei(s,i):i}function Fi(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:_t(e.room_color,zi),opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):Ai,fill:["lqi","light","temp","custom"].includes(e.fill_mode)?e.fill_mode:"none",customFill:Ei(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:_t(e.bg_color,null),hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function Oi(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return _t(i,"")}const Li={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 Hi(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(Li)){const s=Li[t],n=i[t];e[t]={c:_t(n?.c,s.c),a:n&&"number"==typeof n.a?Math.min(1,Math.max(0,n.a)):s.a}}return e}function qi(t,e,i){const s=Math.min(1,Math.max(0,i)),n=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),o=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=n.map((t,e)=>Math.round(t+(o[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function Ui(t,e,i,s,n,o,r,a=Ii){const l=function(t,e,i,s,n,o,r,a=Ii){if("custom"===t)return Ei(a);if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:qi(r.lqi_low.c,r.lqi_high.c,t),a:r.lqi_low.a+(r.lqi_high.a-r.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?r.light_none.a>0?r.light_none:null:"on"===i?r.light_on:r.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(n,o),e=Math.max(n,o);return se?r.temp_hot:r.temp_ok}return null}(t,e,i,s,n,o,r,a);return l?{color:l.c,opacity:l.a,mode:t}:null}const ji={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"]},Wi=[["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 Bi(t){for(const e of[...Object.values(ji),...Wi])if(t===e[0]||t===e[1])return e;return null}function Gi(t,e,i,s,n){if(!s||"unavailable"===s||"unknown"===s)return t;if(n){const i="cover"===e?Bi(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=ji[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const n=Bi(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 Vi(t,e){if(!t||"on"!==t.state)return null;const i=t.attributes||{},s=Number(i.brightness),n=Number.isFinite(s)&&s>0?Math.max(.15,Math.min(1,s/255)):1,o=ft(i.rgb_color);if(o)return{c:o,bri:n};const r=Number(i.color_temp_kelvin)||(Number(i.color_temp)>0?1e6/Number(i.color_temp):NaN);if(Number.isFinite(r)&&r>0){const[t,e,i]=function(t){const e=Math.min(4e4,Math.max(1e3,t))/100,i=e<=66?255:329.698727446*Math.pow(e-60,-.1332047592),s=e<=66?99.4708025861*Math.log(e)-161.1195681661:288.1221695283*Math.pow(e-60,-.0755148492),n=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,o=t=>Math.round(Math.min(255,Math.max(0,t)));return[o(i),o(s),o(n)]}(r);return{c:`rgb(${t}, ${e}, ${i})`,bri:n}}return{c:e,bri:n}}function Ki(t,e,i,s,n=170,o=0){const r=Math.hypot(e[0]-t[0],e[1]-t[1]),a=Math.hypot(i[0]-t[0],i[1]-t[1]);if(r<1e-6||a<1e-6||Math.min(r,a)>=s)return null;let l,c;if(Number.isFinite(o)&&o>1e-6){const s=i[0]-e[0],n=i[1]-e[1],r=Math.hypot(s,n);if(r<1e-6)return null;const a=-(n/r),h=s/r,d=(e[0]+i[0])/2,p=(e[1]+i[1])/2,u=(t[0]-d)*a+(t[1]-p)*h>=0?1:-1,_=o/2,m=[e[0]+a*_*u,e[1]+h*_*u],g=[i[0]+a*_*u,i[1]+h*_*u],f=[e[0]-a*_*u,e[1]-h*_*u],v=[i[0]-a*_*u,i[1]-h*_*u],y=Math.atan2(p-t[1],d-t[0]),b=e=>{let i=Math.atan2(e[1]-t[1],e[0]-t[0]);for(;i-y>Math.PI;)i-=2*Math.PI;for(;i-y<-Math.PI;)i+=2*Math.PI;return i},w=b(m),k=b(g),$=b(f),x=b(v),S=Math.max(Math.min(w,k),Math.min($,x)),M=Math.min(Math.max(w,k),Math.max($,x));if(!(M-S>1e-9))return null;l=S,c=M-S}else{for(l=Math.atan2(e[1]-t[1],e[0]-t[0]),c=Math.atan2(i[1]-t[1],i[0]-t[0])-l;c>Math.PI;)c-=2*Math.PI;for(;c<-Math.PI;)c+=2*Math.PI}const h=n*Math.PI/180;if(Math.abs(c)>h){const t=l+c/2;c=h*Math.sign(c),l=t-c/2}const d=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=l+c*e/8;d.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return d}function Yi(t,e,i,s,n){const o=e*Math.PI/180,r=[-Math.sin(o),Math.cos(o)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*n*a,t[1]+r[1]*n*a];return s.some(t=>Ke(l,t,1e-9))}function Ji(t){return t.startsWith("light.")||t.startsWith("switch.")}function Zi(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let n=0;np||l>p)continue;const u=(n[0]-o[0])*h+(n[1]-o[1])*d,_=(r[0]-o[0])*h+(r[1]-o[1])*d,m=Math.max(0,Math.min(u,_)),g=Math.min(c,Math.max(u,_));g-m>i&&s.push([o[0]+h*m,o[1]+d*m,o[0]+h*g,o[1]+d*g])}}return s}function Xi(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let n=!0;for(;n;){n=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const o of e)if(o.id&&i.has(o.id)&&s(t,o)){i.add(t.id),n=!0;break}}return i}function Qi(t,e,i=1e-6){const s=[];for(const n of t){const t=[n[0],n[1]],o=[n[2],n[3]],r=o[0]-t[0],a=o[1]-t[1],l=Math.hypot(r,a);if(lo||n>o)continue;const r=(s[0]-t[0])*c+(s[1]-t[1])*h,a=(s[2]-t[0])*c+(s[3]-t[1])*h,p=Math.max(0,Math.min(r,a)),u=Math.min(l,Math.max(r,a));u-p>i&&d.push([p,u])}if(!d.length){s.push([t[0],t[1],o[0],o[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,n]of d)e-p>i&&s.push([t[0]+c*p,t[1]+h*p,t[0]+c*e,t[1]+h*e]),p=Math.max(p,n);l-p>i&&s.push([t[0]+c*p,t[1]+h*p,o[0],o[1]])}return s}function ts(t,e,i=1e-6){const s=[];for(let e=0;e{if("string"!=typeof t||!t)return;const i=ns(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 ns(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 os(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i||"custom"===i?i:t}function rs(t,e){const i=e?.settings;return"boolean"==typeof i?.glow?i.glow:"glow"===i?.fill_mode||t}function as(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function ls(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function cs(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function hs(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 ds=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);function ps(t,e){return"alarm_control_panel"===t||"siren"===t||"binary_sensor"===t&&!!e&&ds.has(e)}function us(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 _s={availability:"available",status:"alarm",activity:"none"},ms=new Set(["motion","vibration","sound"]),gs=new Set(["occupancy","presence"]),fs=new Set(["door","window","garage_door","opening"]),vs=new Set(["running","power"]),ys=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const bs=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),ws=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping"]),ks=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),$s=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,xs=t=>String(t??"").trim().toLowerCase();function Ss(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(xs).some(t=>["power","main power","power switch","питание"].includes(t)))return!0;const o=xs(s?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(o)||/(?:^|[\s._-])питание$/.test(o)}function Ms(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=xs(t?.[e]);if(bs.has(i)||ws.has(i))return i}return""}function Cs(t,e){const i=t?.states?.[e],s=i?xs(i.state):"__missing__",n=String(e||"").split(".")[0],o=xs(i?.attributes?.device_class),r={eid:e,state:s,availability:$s(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===r.availability)return r;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&ps(t,e)}(n,o,s))return{...r,status:"alarm"};if("binary_sensor"===n)return ms.has(o)?{...r,edge:"rising"}:gs.has(o)?{...r,activity:"on"===s?"presence":"none"}:fs.has(o)?{...r,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===o?{...r,activity:"on"===s?"transition":"none"}:vs.has(o)&&"on"===s?{...r,status:"working",activity:"running"}:r;if("cover"===n)return{...r,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===n)return{...r,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===n)return{...r,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===n){const t=Ms(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(xs):[],n=!ws.has(s)&&(ks.has(s)||bs.has(s)||e.includes(s));return(t?bs.has(t):n)?{...r,status:"working",activity:"running"}:r}if(["light","switch","fan","humidifier"].includes(n))return"on"===s?{...r,status:"working",activity:"running"}:r;if("media_player"===n)return"off"===s?{...r,availability:"unavailable"}:r;if("vacuum"===n)return"cleaning"===s?{...r,status:"working",activity:"running"}:"returning"===s?{...r,status:"working",activity:"transition"}:r;if("script"===n)return"on"===s?{...r,status:"working",activity:"running"}:r;if("automation"===n)return r;if("button"===n||"event"===n)return{...r,edge:"change"};const a=Ms(i.attributes);return bs.has(a)||bs.has(s)&&!ws.has(s)?{...r,status:"working",activity:"running"}:r}function Ds(t,e){if(!(t=>!!t&&!$s(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 Ts=new WeakMap,Ps=new WeakMap;let Rs=1;const zs="houseplan.ha-binding-status.v1";let As=null;function Is(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=Ts.get(e);return i||(i={revision:0,authoritative:!1,access:"pending",devices:{},entities:{},lastSuccess:0,listeners:new Set,refs:0},Ts.set(e,i)),i}function Es(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 Ns(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=Es(i,"id"),o=Es(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 Fs(t,e){void 0===e.reloadTimer&&(e.reloadTimer=globalThis.setTimeout(()=>{e.reloadTimer=void 0,Ns(t,e)},80))}async function Os(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,()=>Fs(t,e),"device_registry_updated")),e.unsubEntity||(e.unsubEntity=await i.call(t.connection,()=>Fs(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 Ls(t,e){const i=Is(t);if(!i)return()=>{};const s=0===i.refs;i.refs++,i.listeners.add(e),("pending"===i.access||s)&&Ns(t,i),Os(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 Hs(t){const e=Is(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&&Fs(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 qs(t){if(!t||"object"!=typeof t&&"function"!=typeof t)return 0;let e=Ps.get(t);return e||(e=Rs++,Ps.set(t,e)),e}function Us(t,e=Hs(t)){return[e.revision,e.access,qs(e.devices),qs(e.entities)].join(":")}function js(t){return!!t&&null==t.disabled_by}function Ws(t,e=Hs(t)){const i={},s={},n={};for(const[t,s]of Object.entries(e.devices||{}))js(s)&&(i[t]=s);for(const[t,i]of Object.entries(e.entities||{})){if(!js(i))continue;const n=i.device_id?e.devices?.[i.device_id]:null;e.authoritative&&i.device_id&&!n||(n&&!js(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&&!js(t))continue;const o=t?.device_id?e.devices?.[t.device_id]:null;e.authoritative&&t?.device_id&&!o||(o&&!js(o)||(n[i]=s))}return{...t,devices:i,entities:s,states:n}}function Bs(t,e=Hs(t)){return{...t,devices:e.devices||{},entities:e.entities||{}}}function Gs(t,e){const i=[];for(const[s,n]of Object.entries(t||{}))n?.device_id===e&&i.push(s);return i}function Vs(){if(As)return As;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 As=i,As}catch{return{}}}function Ks(t,e,i=Hs(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 r=i.devices||{},a=i.entities||{};if(i.authoritative){if("device"===n){const t=r[o];if(!t)return{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[]};const e=Gs(a,o);if(!js(t))return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:e};const i=e.filter(t=>js(a[t]));return e.length&&!i.length?{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:e}:{kind:"active",enabledEntityIds:i,allEntityIds:e}}const e=a[o];return e&&!js(e)?{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]}:e?.device_id&&!r[e.device_id]?{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[o]}:e?.device_id&&!js(r[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 Vs()[t]||null}(e);if("device"===n){const e=r[o],i=Gs(a,o);if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:i};const s=i.filter(t=>{const e=a[t];return null==e?.disabled_by&&(!e.device_id||null==r[e.device_id]?.disabled_by)});if(i.length&&!s.length&&i.every(t=>null!=a[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=a[o];if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]};if(e?.device_id&&null!=r[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 Ys(t){const e={};for(const[i,s]of Object.entries(t.entities)){if(!s?.device_id||!js(s))continue;const n=t.devices?.[s.device_id];n&&!js(n)||(e[s.device_id]=e[s.device_id]||[]).push(i)}return e}function Js(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 Zs(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 Xs=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],Qs=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function tn(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 Xs){const e=n.filter(e=>e.eid.startsWith(t+"."));if(e.length)return Qs(e).map(t=>t.eid)}const o=n.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=xs(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return ms.has(i)||gs.has(i)||fs.has(i)||vs.has(i)||"moving"===i||ys.has(i)}(t,e.eid));if(o.length)return Qs(o).map(t=>t.eid);const r=n.filter(t=>t.eid.startsWith("switch."));if(r.length){const e=Qs(r);return[(e.find(e=>Ss(t,e.eid))||e[0]).eid]}const a=[];for(const t of Tt)a.push(...Qs(n.filter(e=>e.eid.startsWith(t+"."))));return a.push(...Qs(n.filter(t=>!Tt.includes(t.eid.split(".")[0])))),a.map(t=>t.eid)}function en(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=>Zs(t,e.eid));if(i)return i.eid}return tn(t,e)[0]}function sn(t,e,i=[]){return[...new Set(nn(t,e,i))].filter(t=>Ji(t))}function nn(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 on(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 rn(t,e){const i=sn(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"}));if(!0===e.marker?.is_light){const t=function(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(Ji),s=t.primary&&Ji(t.primary)?t.primary:null;return(e&&Ji(e)?e:null)||s||i[0]||null}(e);t&&!i.some(e=>e.eid===t)&&i.push({eid:t,via:"forced"})}if(i.length)return i;const s=e.primary||tn(t,e.entities)[0];return s&&!s.startsWith("light.")?[]:e.entities.filter(t=>t.startsWith("light.")).map(t=>({eid:t,via:"light"}))}function an(t,e,i){const s=[],n=new Map;for(const o of e){if(o.hidden||null!=i&&!on(o,i))continue;const e=rn(t,o);for(const{eid:i,via:r}of e){if(!i)continue;const e="controls"!==r,a={eid:i,device:o,via:r,castsGlow:e,on:"on"===t.states[i]?.state},l=n.get(i);null==l?(n.set(i,s.length),s.push(a)):e&&!s[l].castsGlow&&(s[l]=a)}}return s}function ln(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function cn(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 li(i)}function hn(t,e){for(const i of e){if(!Zs(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 dn(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 pn(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 un(t,e){for(const i of e){if(!pn(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 _n(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden||!js(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(!js(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 mn(t,e,i,s,n){const o=Dt(e,i,n);if(o!==Ct)return o;const r=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&r.push(i)}return function(t){for(const e of t){const t=Mt[e];if(t)return t}return null}(r)??Ct}function gn(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 fn(t,e,i){if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function vn(t,e,i,s,n){const o=sn(e.binding,e.controls,t.entities).filter(t=>!fn(i,t,s)).filter(t=>"active"===Ks(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 yn(t){const e=t.hass,i=t.registry||Hs(e),s=Ws(e,i),n=Bs(e,i),{areaToSpace:o,markers:r,settings:a,excluded:l,showAll:c,firstSpaceId:h,loc:d,iconRules:p}=t,u=!1!==a.group_lights,_=gn(r),m=_n(s,u).filter(t=>!fn(s,t.eid,_)),g=new Set(m.map(t=>t.area)),f=Ys(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 r){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||y.add(t.binding)}const b=(t,e)=>r.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 r=Ks(e,"device:"+t.id,i);if("active"!==r.kind)continue;const h=b("device",t.id);if(h&&h.hidden&&!a.filter_seeded)continue;const _=f[t.id]||[],m=Js(s,t,_),v=!a.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=mn(s,$,t.model,_,p);if(_.some(t=>t.startsWith("lock."))&&(S="mdi:lock"),v&&!c&&u&&"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:r.allEntityIds,bindingStatus:r,bindingKind:"device",bindingRef:t.id,pdfs:[]};C.primary=en(s,_,S),"mdi:thermometer"!==S&&"mdi:air-filter"!==S||(C.temp=hn(s,_)),C.primary&&pn(s,C.primary)&&(C.hum=un(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 r){if(t.removed)continue;const[r,l]=t.binding.split(":"),c="device"===r||"entity"===r?Ks(e,t.binding,i):null;if(!t.hidden||a.filter_seeded||"ha_disabled"===c?.kind)if("device"===r){const e=c;if("unverified"===e.kind)continue;const r=n.devices[l],a=t.area||r?.area_id||"",u=a&&o[a]||t.space||h,m="active"===e.kind?e.enabledEntityIds:[];let g=r?mn(s,r.name_by_user||r.name||"",r.model,m,p):"mdi:help-circle";m.some(t=>t.startsWith("lock."))&&(g="mdi:lock");const f={id:t.id,name:r?.name_by_user||r?.name||d("device.fallback"),model:r?.model||"",area:a,space:u,icon:g,entities:m,allEntities:e.allEntityIds.length?e.allEntityIds:r&&v[r.id]||[],bindingStatus:e,bindingKind:"device",bindingRef:l};f.primary=en(s,m,g),"mdi:thermometer"!==g&&"mdi:air-filter"!==g||(f.temp=hn(s,m)),f.primary&&pn(s,f.primary)&&(f.hum=un(s,m)),vn(f,t,n,_,i),k.push(f)}else if("entity"===r){if(fn(n,l,_))continue;const e=c;if("unverified"===e.kind)continue;const r=n.entities[l],a=t.area||r?.area_id||r?.device_id&&n.devices[r.device_id]?.area_id||"",d=a&&o[a]||t.space||h,u=s.states[l],m=r?.name||u?.attributes?.friendly_name||l;let g=mn(s,m,"",[l],p);l.startsWith("lock.")&&(g="mdi:lock");const f={id:t.id,name:m,model:"",area:a,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=hn(s,f.entities)),f.entities.length&&pn(s,l)&&(f.hum=un(s,f.entities)),vn(f,t,n,_,i),k.push(f)}else{const e=t.area||"",s=t.space||e&&o[e]||h,r={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};vn(r,t,n,_,i),k.push(r)}}return k}function bn(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),r=gn(i);return"device"===n?r.devices.has(o):"entity"===n&&fn(t,o,r)}(t,e,s))return null;const n=e.indexOf(":");if(n<0)return null;const o=e.slice(0,n),r=e.slice(n+1);if(!r)return null;if("entity"===o){const e=parseFloat(t.states[r]?.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===r).map(([t])=>t);return"temp"===i?hn(t,e):un(t,e)}return null}const wn=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 kn(t,e,i){const s=[];for(const n of e){if(n.area!==i||n.virtual)continue;const e=cn(t,n.entities);null!=e&&s.push(e)}return li(s)}function $n(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=hn(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 xn={sourceKind:"none",visualSources:[],criticalSources:[],samples:[]};function Sn(t,e){const i=t?.entities?.[e],s=t?.states?.[e];return String(i?.name||i?.original_name||s?.attributes?.friendly_name||e)}function Mn(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 Cn(t,e,i,s){return{eid:e,role:i,name:Sn(t,e),state:String(t?.states?.[e]?.state??""),stateText:Mn(t,e),integrationDomain:t?.entities?.[e]?.platform||null,sample:s||Cs(t,e)}}function Dn(t,e){const i=e.hidden&&e.userHidden?{...e,hidden:!1}:e;let s="none",n=[];const o="cover"===i.tapAction?bi(i.entities):null,r=an(t,[i]);if(o)s="cover",n=[Cn(t,o,"cover")];else if(r.length)s=r.some(t=>"controls"===t.via)?"controls":"light",n=r.map(e=>Cn(t,e.eid,"controls"===e.via?"control":"forced"===e.via?"forced_light":"light"));else{const e=tn(t,i.entities);if(e.length){s="device_role";const o=function(t,e,i){const s=e.map(e=>Cs(t,e)),n=i.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category);return 1===e.length&&n.length>1&&Ss(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,e,i.entities);n=e.map((e,i)=>Cn(t,e,"device_role",o[i]))}else i.primary&&(s="primary",n=[Cn(t,i.primary,"primary")])}const a=[];for(const e of i.entities||[]){const i=Cs(t,e);"alarm"!==i.status||n.some(t=>t.eid===e)||a.push(Cn(t,e,"critical",i))}return{sourceKind:s,visualSources:n,criticalSources:a,samples:[...n,...a].map(t=>t.sample)}}function Tn(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(!Zs(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||!pn(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=Mi(t,e);return o?{text:Ci(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 Pn(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 Rn(t,e,i=!0,s){const n=s||Dn(t,e);return Pn(e,n,Tn(t,e,n,i).source)}function zn(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 An(t,e,i){const s=ui(e.marker?.display),n="static_icon"===s,o=n&&!1===i.sourceDetails?xn:Dn(t,e),r=e.bindingStatus,a="ha_disabled"===r?.kind,l="orphaned"===r?.kind,c=!0===e.userHidden||!0===e.marker?.hidden,h=a||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 _s;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 p=d;h||n?p={availability:"available",status:"neutral",activity:"none"}:"alarm"===d.status||i.liveStates||(p={availability:"available",status:"neutral",activity:"none"});const u=n&&!1===i.sourceDetails?{source:null,text:null,fallback:null}:Tn(t,e,o,i.showTemperature),_=Pn(e,o,u.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&&(p={...p,activity:m.flashKind});const f="icon_ripple"===s&&!h&&i.liveStates&&"alarm"!==p.status?p.activity:"none",v=n||h||!i.showTemperature?null:!0===e.marker?.use_climate_temp?dn(t,e.entities):"mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon?hn(t,e.entities):null,y=!n&&!h&&i.showTemperature&&e.primary&&pn(t,e.primary)?un(t,e.entities):null,b=n||h||!i.showSignal||e.virtual?null:cn(t,e.entities),w="cover"===o.sourceKind?o.visualSources[0]?.eid:e.primary||o.visualSources[0]?.eid,k=w?t?.states?.[w]:null,$=!i.liveStates||n||h?e.icon:Gi(e.icon,w?.split(".")[0],k?.attributes?.device_class,k?.state,!!e.marker?.icon),x=!i.liveStates||n||h?null:an(t,[{...e,hidden:!1}]).map(e=>function(t){return t&&"on"===t.state?ft(t.attributes?.rgb_color):null}(t?.states?.[e.eid])).find(t=>!!t)||null,S=Number(e.marker?.size)>0?Number(e.marker.size):1,M=Number(e.marker?.angle)||0,C=Number(e.marker?.ripple_size)>0?Number(e.marker.ripple_size):3,D=_t(e.marker?.ripple_color,null),T=n?null:D||x||null,P="value"!==s||h?null:u.text,R="ha_disabled"===r?.kind?r.reason:null,z=function(t,e,i,s,n,o,r,a){return o?"ha_disabled":r?"orphaned":"static_icon"===a?"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"===a&&"none"!==i?"working_activity":"working":"open"===e.status?"open":(t.primary||"").startsWith("media_player.")?"media_neutral":"neutral":"live_states_disabled"}(e,p,f,o.sourceKind,i.liveStates,a,l,s),A=[];i.designPreview&&c&&A.push("hidden_design_preview"),n||!0!==e.marker?.vacuum?.live||A.push("vacuum_live_plan_only");const I=1===o.visualSources.length&&Ss(t,o.visualSources[0].eid),E=e.entities.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category).length;I&&E>1&&A.push("composite_power_source"),"icon_ripple"!==s&&"static_icon"!==s&&"alarm"!==d.status&&"none"!==d.activity&&A.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:u.source,sourceSignature:_,visual:p,display:s,icon:$,valueText:P,valueFullText:P,fallbackReason:"value"!==s||P?null:u.fallback,activity:f,activityGeneration:m?.gen||1,classes:[],tempText:null==v?null:String(v),humText:null==y?null:String(y),lqiText:null==b?null:String(b),lqiColor:null==b?null:Ae(b),lightColor:x,scale:S,angle:M,rippleScale:C,rippleColor:T,userHidden:c,effectiveHidden:h,haDisabled:a,disabledReason:R,orphaned:l,vacuumLive:!n&&!0===e.marker?.vacuum?.live,explanation:{reason:z,notices:A}};return{...N,classes:zn(N)}}const In=new WeakMap;function En(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=In.get(e);return i||(i={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},In.set(e,i)),i}function Nn(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function Fn(t,e){const i=En(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,Nn(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Nn(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,Nn(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 On(t){const e=En(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function Ln(t,e,i=On(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 Hn(t,e,i,s,n){const o=String(e||"").trim();if(!o)return null;const r=n?s.configEntries[n]:null;return{domain:o,label:Ln(t,o,s,r?.title),configEntryId:n,configEntryTitle:r?.title,confidence:i}}function qn(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 Un(t,e,i,s=On(t)){const n=i.entities?.[e]||t?.entities?.[e];if(!n)return null;const o=n.config_entry_id||void 0,r=o?s.configEntries[o]?.domain:"";return Hn(t,n.platform||r,"entity-platform",s,o)}var jn={"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 (lights/switches)","marker.tap_label":"Tap action for this device","tap.toggle_note":"Toggle never applies to locks and alarms; hold the icon to open the info card.","import.title":"Create spaces from HA floors","import.hint":"Your Home Assistant already knows these floors. Pick the ones to turn into plan spaces — you will upload a floor-plan image for each one next. Rooms are then outlined by hand on the plan.","import.start":"Create {n} space(s)","import.manual":"Start from scratch","import.progress":"Floor {i} of {n}","import.done":"Spaces created. Outline the rooms: click grid dots and close the contour.","btn.skip":"Skip","space.scale_label":"Scale (grid cell size)","space.scale_unit":"cm per cell","space.display_section":"Display","space.show_borders":"Always show room borders","space.show_names":"Show room names (drag to move)","space.room_color":"Border & name color","space.opacity":"Opacity","space.fill_label":"Room fill","fill.none":"None","fill.lqi":"Zigbee signal","fill.light":"Lights","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…","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_hint":"empty = default from general settings","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set how thick it is. Empty or zero removes the thickness.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new room-wall segment (1–100 cm). Shared walls keep the neighbour's value.","toast.wallthick_pick":"Click a wall (not an open boundary)","toast.wallthick_open":"Open boundaries have no thickness","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from 1 to {max} {unit}","toast.physical_angle":"Enter a rotation angle from 0° up to, but not including, 90°","toast.physical_limit":"The space has reached the limit for this type of geometry","markup.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":"Use the device's temperature sensor","marker.use_climate_temp_tip":"Air conditioners and thermostats know the room temperature (current_temperature). Shows it as a badge next to the icon and counts it in the room average — like a regular thermometer.","marker.is_light":"This device is a light source","marker.is_light_tip":"Creates light-source glow even without a light entity — for a smart switch driving ordinary fixtures. The glow follows the switch (or the lights bound above).","confirm.unlock":"Unlock “{name}”?","toast.files_migrate_failed":"Attachments could not be moved to the new binding, links keep pointing at the old files: {err}","space.pick_saved":"Already uploaded","space.pick_saved_hint":"Plans stored on the server, including ones you detached earlier","space.no_saved":"No plans stored on the server yet.","space.loading":"Loading…","space.used_by":"in use: {list}","space.in_use":"A space still uses this plan — detach it first","btn.use":"Use","confirm.delete_plan":'Delete the plan file "{name}" from the server? This cannot be undone.',"toast.plans_list_failed":"Could not list the stored plans: {err}","toast.plan_delete_failed":"Could not delete the plan: {err}","marker.hide":"Hide","marker.hide_tip":'The device will disappear from the plan after saving but will still count toward the room signal. Restore it through "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.","run.automation":"automation","run.script":"script","run.scene":"scene","confirm.tap_run":'Run "{name}"?',"confirm.tap_toggle":'Toggle "{name}"?',"confirm.tap_cover":'Open/close "{name}"?',"toast.run_started":"Started: {name}","toast.run_target_missing":"Run target not found — check the device settings","toast.run_target_required":"Pick an automation, script or scene","btn.run":"Run","vac.section":"Robot vacuum: live position","vac.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"};const Wn={en:jn,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":"Toggle никогда не применяется к замкам и сигнализациям; долгое нажатие всегда открывает инфо-карточку.","import.title":"Создать пространства из этажей HA","import.hint":"Home Assistant уже знает эти этажи. Отметьте, какие превратить в пространства плана — далее для каждого попросим картинку плана. Комнаты затем обводятся вручную по плану.","import.start":"Создать: {n}","import.manual":"Начать с нуля","import.progress":"Этаж {i} из {n}","import.done":"Пространства созданы. Обведите комнаты: кликайте по точкам сетки и замкните контур.","btn.skip":"Пропустить","space.scale_label":"Масштаб (размер клетки сетки)","space.scale_unit":"см на клетку","space.display_section":"Отображение","space.show_borders":"Всегда отображать границы комнат","space.show_names":"Отображать названия комнат (перетаскиваются)","space.room_color":"Цвет границ и названий","space.opacity":"Прозрачность","space.fill_label":"Заливка комнат","fill.none":"Нет","fill.lqi":"По силе зигби-сигнала","fill.light":"По освещению","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":"Загрузка…","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_hint":"пусто = по умолчанию из общих настроек","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены комнаты (1–100 см). Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от 1 до {max} {unit}","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","markup.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.is_light":"Это устройство — источник света","marker.is_light_tip":"Создаёт свечение источника света даже без light-сущности — для умного выключателя с обычными светильниками. Свечение следует за выключателем (или за привязанными выше лампами).","confirm.unlock":"Открыть замок «{name}»?","toast.files_migrate_failed":"Не удалось перенести вложения к новой привязке, ссылки остались на старые файлы: {err}","space.pick_saved":"Уже загруженные","space.pick_saved_hint":"Планы, сохранённые на сервере, включая отцеплённые ранее","space.no_saved":"На сервере пока нет сохранённых планов.","space.loading":"Загрузка…","space.used_by":"используется: {list}","space.in_use":"План используется пространством — сначала отцепите его","btn.use":"Выбрать","confirm.delete_plan":"Удалить файл плана «{name}» с сервера? Действие необратимо.","toast.plans_list_failed":"Не удалось получить список планов: {err}","toast.plan_delete_failed":"Не удалось удалить план: {err}","marker.hide":"Скрыть","marker.hide_tip":"Устройство исчезнет с плана после сохранения, но продолжит участвовать в расчёте сигнала комнаты. Вернуть его можно через кнопку «Скрытые и деактивированные» в редакторе устройств.","marker.show":"Показать","marker.show_tip":"Устройство снова появится на плане после сохранения.","marker.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":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","run.automation":"автоматизация","run.script":"скрипт","run.scene":"сцена","confirm.tap_run":"Запустить «{name}»?","confirm.tap_toggle":"Переключить «{name}»?","confirm.tap_cover":"Открыть/закрыть «{name}»?","toast.run_started":"Запущено: {name}","toast.run_target_missing":"Цель запуска не найдена — проверьте настройки устройства","toast.run_target_required":"Выберите автоматизацию, скрипт или сцену","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.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":"Применить"}};function Bn(t,e){if(e&&e in Wn)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function Gn(t,e,i){return Ri(Wn[t][e]??jn[e]??e,i)}class Vn extends lt{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=Fn(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0}get _lang(){return Bn(this.hass)}_t(t,e){return Gn(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=On(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),r=e.slice(n+1);if("entity"===o){const e=Un(t,r,i,s);return e?[e]:[]}if("device"!==o)return[];const a=i.devices?.[r]||t?.devices?.[r];if(!a)return[];const l=[a.config_entry_id,...Array.isArray(a.config_entries)?a.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const i=s.configEntries[e];return Hn(t,i?.domain,"registry-owner",s,e)});if(l.some(Boolean))return qn(l);const c=[];for(const[e,n]of Object.entries(i.entities||{}))n?.device_id===r&&null==n?.disabled_by&&c.push(Un(t,e,i,s));return c.some(Boolean)?qn(c):qn((Array.isArray(a.identifiers)?a.identifiers:[]).map(e=>{const i=Array.isArray(e)?e[0]:null;return Hn(t,i,"identifier-fallback",s)}))}(this.hass,this.presentation.binding,this.registry,On(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=Un(this.hass,t,this.registry,On(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:zn(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),r=Math.min(1,2.5/Math.max(1,o)),a=Math.round(100*r),l=["dev",...i.classes,"virtual"===i.binding?"virtual":"",i.haDisabled?"ghost ha-disabled":"",null!=i.valueText?"valonly":""].filter(Boolean).join(" "),c=bt(i).join(";"),h=e?this._t("marker.preview.demo_notice"):this._reason(t.explanation.reason),d=[this.deviceName,this._providerSummary(s),this._stateSummary(t),h,t.valueFullText||""].filter(Boolean).join(". ");return j`
+ style=${t.angle?`transform:rotate(${t.angle}deg)`:W}>`} + ${null!=t.tempText&&null==t.valueText?j`${t.tempText}°`:W} + ${null!=t.humText&&null==t.valueText?j`${t.humText}%`:W} + ${null!=t.lqiText?j`${t.lqiText}`:W} + `}const kt=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),$t=[{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 xt(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 St=xt($t),Mt={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"},Ct="mdi:chip";function Dt(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??St)if(t.test(s))return e;return Ct}const Tt=["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 Pt=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,Rt=Math.ceil,zt=Math.floor,At="[BigNumber Error] ",It=At+"Number primitive has more than 15 significant digits: ",Et=1e14,Nt=14,Ft=9007199254740991,Ot=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],Lt=1e7,Ht=1e9;function qt(t){var e=0|t;return t>0||t===e?e:e-1}function Ut(t){for(var e,i,s=1,o=t.length,n=t[0]+"";sc^i?1:-1;for(a=(l=o.length)<(c=n.length)?l:c,r=0;rn[r]^i?1:-1;return l==c?0:l>c^i?1:-1}function Bt(t,e,i,s){if(ti||t!==zt(t))throw Error(At+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Gt(t){var e=t.c.length-1;return qt(t.e/Nt)==e&&t.c[e]%2!=0}function Wt(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Vt(t,e,i){var s,o;if(e<0){for(o=i+".";++e;o+=i);t=o+t}else if(++e>(s=t.length)){for(o=i,e-=s;--e;o+=i);t+=o}else ey?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>y?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!Pt.test(d=String(t)))return o(p,d,c);p.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(a=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(a<0&&(a=l),a+=+d.slice(l+1),d=d.substring(0,l)):a<0&&(a=d.length)}else{if(Bt(e,2,x.length,"Base"),10==e&&S)return P(p=new M(t),_+p.e+1,m);if(d=String(t),c="number"==typeof t){if(0*t!=0)return o(p,d,c,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(It+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,h=d.length;la){a=h;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return o(p,String(t),c,e)}c=!1,(a=(d=s(d,e,10,p.s)).indexOf("."))>-1?d=d.replace(".",""):a=d.length}for(l=0;48===d.charCodeAt(l);l++);for(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(t>Ft||t!==zt(t)))throw Error(It+p.s*t);if((a=a-l-1)>y)p.c=p.e=null;else if(a=f)?Wt(l,r):Vt(l,r,"0");else if(n=(t=P(new M(t),e,i)).e,a=(l=Ut(t.c)).length,1==s||2==s&&(e<=n||n<=g)){for(;ar),l=Vt(l,n,"0"),n+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=n-a)>0)for(n+1==a&&(l+=".");e--;l+="0");return t.s<0&&o?"-"+l:l}function D(t,e){for(var i,s,o=1,n=new M(t[0]);o=10;o/=10,s++);return(i=s+i*Nt-1)>y?t.c=t.e=null:i=10;a/=10,o++);if((n=e-o)<0)n+=Nt,r=e,l=d[c=0],h=zt(l/p[o-r-1]%10);else if((c=Rt((n+1)/Nt))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,o=1,r=(n%=Nt)-Nt+1}else{for(l=a=d[c],o=1;a>=10;a/=10,o++);h=(r=(n%=Nt)-Nt+o)<0?0:zt(l/p[o-r-1]%10)}if(s=s||e<0||null!=d[c+1]||(r<0?l:l%p[o-r-1]),s=i<4?(h||s)&&(0==i||i==(t.s<0?3:2)):h>5||5==h&&(4==i||s||6==i&&(n>0?r>0?l/p[o-r]:0:d[c-1])%10&1||i==(t.s<0?8:7)),e<1||!d[0])return d.length=0,s?(e-=t.e+1,d[0]=p[(Nt-e%Nt)%Nt],t.e=-e||0):d[0]=t.e=0,t;if(0==n?(d.length=c,a=1,c--):(d.length=c+1,a=p[Nt-n],d[c]=r>0?zt(l/p[o-r]%p[r])*a:0),s)for(;;){if(0==c){for(n=1,r=d[0];r>=10;r/=10,n++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);n!=a&&(t.e++,d[0]==Et&&(d[0]=1));break}if(d[c]+=a,d[c]!=Et)break;d[c--]=0,a=1}for(n=d.length;0===d[--n];d.pop());}t.e>y?t.c=t.e=null:t.e=f?Wt(e,i):Vt(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(At+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(Bt(i=t[e],0,Ht,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(Bt(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(Bt(i[0],-Ht,0,e),Bt(i[1],0,Ht,e),g=i[0],f=i[1]):(Bt(i,-Ht,Ht,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)Bt(i[0],-Ht,-1,e),Bt(i[1],1,Ht,e),v=i[0],y=i[1];else{if(Bt(i,-Ht,Ht,e),!i)throw Error(At+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(At+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(At+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(Bt(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(Bt(i=t[e],0,Ht,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(At+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(At+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:k,FORMAT:$,ALPHABET:x}},M.isBigNumber=function(t){if(!t||!0!==t._isBigNumber)return!1;if(!M.DEBUG)return!0;var e,i,s=t.c,o=t.e,n=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===n||-1===n)&&o>=-Ht&&o<=Ht&&o===zt(o)){if(0===s[0]){if(0===o&&1===s.length)return!0;break t}if((e=(o+1)%Nt)<1&&(e+=Nt),String(s[0]).length==e){for(e=0;e=Et||i!==zt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===o&&(null===n||1===n||-1===n))return!0;throw Error(At+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(n=9007199254740992,r=Math.random()*n&2097151?function(){return zt(Math.random()*n)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,o,n,a=0,l=[],c=new M(u);if(null==t?t=_:Bt(t,0,Ht),o=Rt(t/Nt),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(o*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(n%1e14),a+=2);a=o/2}else{if(!crypto.randomBytes)throw b=!1,Error(At+"crypto unavailable");for(e=crypto.randomBytes(o*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(n%1e14),a+=7);a=o/7}if(!b)for(;a=10;n/=10,a++);ai-1&&(null==r[o+1]&&(r[o+1]=0),r[o+1]+=r[o]/i|0,r[o]%=i)}return r.reverse()}return function(s,o,n,r,a){var l,c,h,d,p,u,g,f,v=s.indexOf("."),y=_,b=m;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(o)).pow(s.length-v),k=d,f.c=e(Vt(Ut(u.c),u.e,"0"),10,n,t),f.e=f.c.length),h=d=(g=e(s,o,n,a?(l=x,t):(l=t,x))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--h:(u.c=g,u.e=h,u.s=r,g=(u=i(u,f,y,b,n)).c,p=u.r,h=u.e),v=g[c=h+y+1],d=n/2,p=p||c<0||null!=g[c+1],p=b<4?(null!=v||p)&&(0==b||b==(u.s<0?3:2)):v>d||v==d&&(4==b||p||6==b&&1&g[c-1]||b==(u.s<0?8:7)),c<1||!g[0])s=p?Vt(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=c,p)for(--n;++g[--c]>n;)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=Vt(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,o,n,r,a=0,l=t.length,c=e%Lt,h=e/Lt|0;for(t=t.slice();l--;)a=((o=c*(n=t[l]%Lt)+(s=h*n+(r=t[l]/Lt|0)*c)%Lt*Lt+a)/i|0)+(s/Lt|0)+h*r,t[l]=o%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var o,n;if(i!=s)n=i>s?1:-1;else for(o=n=0;oe[o]?1:-1;break}return n}function i(t,e,i,s){for(var o=0;i--;)t[i]-=o,o=t[i]1;t.splice(0,1));}return function(s,o,n,r,a){var l,c,h,d,p,u,_,m,g,f,v,y,b,w,k,$,x,S=s.s==o.s?1:-1,C=s.c,D=o.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&o.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(g=(m=new M(S)).c=[],S=n+(c=s.e-o.e)+1,a||(a=Et,c=qt(s.e/Nt)-qt(o.e/Nt),S=S/Nt|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,(p=zt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),$=D.length,w=C.length),b=$,v=(f=C.slice(0,$)).length;v<$;f[v++]=0);x=D.slice(),x=[0].concat(x),k=D[0],D[1]>=a/2&&k++;do{if(p=0,(l=e(D,f,$,v))<0){if(y=f[0],$!=v&&(y=y*a+(f[1]||0)),(p=zt(y/k))>1)for(p>=a&&(p=a-1),_=(u=t(D,p,a)).length,v=f.length;1==e(u,f,_,v);)p--,i(u,$<_?x:D,_,a),_=u.length,l=1;else 0==p&&(l=p=1),_=(u=D.slice()).length;if(_=10;S/=10,h++);P(m,n+(m.e=h+c*Nt-1)+1,r,d)}else m.e=c,m.r=+d;return m}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,o=function(t,e,i,s){var o,n=i?e:e.replace(d,"");if(h.test(n))t.s=isNaN(n)?null:n<0?-1:1;else{if(!i&&(n=n.replace(a,function(t,e,i){return o="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=o?t:e}),s&&(o=s,n=n.replace(l,"$1").replace(c,"0.$1")),e!=n))return new M(n,o);if(M.DEBUG)throw Error(At+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},p.absoluteValue=p.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},p.comparedTo=function(t,e){return jt(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,o,n=this;if(null!=t)return Bt(t,0,Ht),null==e?e=m:Bt(e,0,8),P(new M(n),t+n.e+1,e);if(!(i=n.c))return null;if(s=((o=i.length-1)-qt(this.e/Nt))*Nt,o=i[o])for(;o%10==0;o/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,m)},p.dividedToIntegerBy=p.idiv=function(t,e){return i(this,new M(t,e),0,1)},p.exponentiatedBy=p.pow=function(t,e){var i,s,o,n,r,a,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(At+"Exponent not an integer: "+R(t));if(null!=e&&(e=new M(e)),r=t.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!t.c||!t.c[0])return c=new M(Math.pow(+R(h),r?t.s*(2-Gt(t)):+R(t))),e?c.mod(e):c;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&h.isInteger()&&e.isInteger())&&(h=h.mod(e))}else{if(t.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||r&&h.c[1]>=24e7:h.c[0]<8e13||r&&h.c[0]<=9999975e7)))return n=h.s<0&&Gt(t)?-0:0,h.e>-1&&(n=1/n),new M(a?1/n:n);k&&(n=Rt(k/Nt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Gt(t)):l=(o=Math.abs(+R(t)))%2,c=new M(u);;){if(l){if(!(c=c.times(h)).c)break;n?c.c.length>n&&(c.c.length=n):s&&(c=c.mod(e))}if(o){if(0===(o=zt(o/2)))break;l=o%2}else if(P(t=t.times(i),t.e+1,1),t.e>14)l=Gt(t);else{if(0===(o=+R(t)))break;l=o%2}h=h.times(h),n?h.c&&h.c.length>n&&(h.c.length=n):s&&(h=h.mod(e))}return s?c:(a&&(c=u.div(c)),e?c.mod(e):n?P(c,k,m,void 0):c)},p.integerValue=function(t){var e=new M(this);return null==t?t=m:Bt(t,0,8),P(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===jt(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return jt(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=jt(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&qt(this.e/Nt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return jt(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=jt(this,new M(t,e)))||0===e},p.isNaN=function(){return!this.s},p.isNegative=function(){return this.s<0},p.isPositive=function(){return this.s>0},p.isZero=function(){return!!this.c&&0==this.c[0]},p.minus=function(t,e){var i,s,o,n,r=this,a=r.s;if(e=(t=new M(t,e)).s,!a||!e)return new M(NaN);if(a!=e)return t.s=-e,r.plus(t);var l=r.e/Nt,c=t.e/Nt,h=r.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?r:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?r:3==m?-0:0)}if(l=qt(l),c=qt(c),h=h.slice(),a=l-c){for((n=a<0)?(a=-a,o=h):(c=l,o=d),o.reverse(),e=a;e--;o.push(0));o.reverse()}else for(s=(n=(a=h.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;h[i++]=0);for(e=Et-1;s>a;){if(h[--s]=0;){for(i=0,p=y[o]%g,u=y[o]/g|0,n=o+(r=l);n>o;)i=((c=p*(c=v[--r]%g)+(a=u*c+(h=v[r]/g|0)*p)%g*g+_[n]+i)/m|0)+(a/g|0)+u*h,_[n--]=c%m;_[n]=i}return i?++s:_.splice(0,1),T(t,_,s)},p.negated=function(){var t=new M(this);return t.s=-t.s||null,t},p.plus=function(t,e){var i,s=this,o=s.s;if(e=(t=new M(t,e)).s,!o||!e)return new M(NaN);if(o!=e)return t.s=-e,s.minus(t);var n=s.e/Nt,r=t.e/Nt,a=s.c,l=t.c;if(!n||!r){if(!a||!l)return new M(o/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*o)}if(n=qt(n),r=qt(r),a=a.slice(),o=n-r){for(o>0?(r=n,i=l):(o=-o,i=a),i.reverse();o--;i.push(0));i.reverse()}for((o=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=o),o=0;e;)o=(a[--e]=a[e]+l[e]+o)/Et|0,a[e]=Et===a[e]?0:a[e]%Et;return o&&(a=[o].concat(a),++r),T(t,a,r)},p.precision=p.sd=function(t,e){var i,s,o,n=this;if(null!=t&&t!==!!t)return Bt(t,1,Ht),null==e?e=m:Bt(e,0,8),P(new M(n),t,e);if(!(i=n.c))return null;if(s=(o=i.length-1)*Nt+1,o=i[o]){for(;o%10==0;o/=10,s--);for(o=i[0];o>=10;o/=10,s++);}return t&&n.e+1>s&&(s=n.e+1),s},p.shiftedBy=function(t){return Bt(t,-9007199254740991,Ft),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,o,n,r=this,a=r.c,l=r.s,c=r.e,h=_+4,d=new M("0.5");if(1!==l||!a||!a[0])return new M(!l||l<0&&(!a||a[0])?NaN:a?r:1/0);if(0==(l=Math.sqrt(+R(r)))||l==1/0?(((e=Ut(a)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=qt((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(n=s,s=d.times(n.plus(i(r,n,h,1))),Ut(n.c).slice(0,l)===(e=Ut(s.c)).slice(0,l)){if(s.e0&&_>0){for(n=_%a||a,h=u.substr(0,n);n<_;n+=a)h+=c+u.substr(n,a);l>0&&(h+=c+u.slice(n)),p&&(h="-"+h)}s=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,o,n,r,a,l,c,h,d,p,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(At+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+R(l));if(!f)return new M(g);for(e=new M(u),h=s=new M(u),o=c=new M(u),_=Ut(f),r=e.e=_.length-g.e-1,e.c[0]=Ot[(a=r%Nt)<0?Nt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:h:l,a=y,y=1/0,l=new M(_),c.c[0]=0;d=i(l,e,0,1),1!=(n=s.plus(d.times(o))).comparedTo(t);)s=o,o=n,h=c.plus(d.times(n=h)),c=n,e=l.minus(d.times(n=e)),l=n;return n=i(t.minus(s),o,0,1),c=c.plus(n.times(h)),s=s.plus(n.times(o)),c.s=h.s=g.s,p=i(h,o,r*=2,m).minus(g).abs().comparedTo(i(c,s,r,m).minus(g).abs())<1?[h,o]:[c,s],y=a,p},p.toNumber=function(){return+R(this)},p.toPrecision=function(t,e){return null!=t&&Bt(t,1,Ht),C(this,t,e,2)},p.toString=function(t){var e,i=this,o=i.s,n=i.e;return null===n?o?(e="Infinity",o<0&&(e="-"+e)):e="NaN":(null==t?e=n<=g||n>=f?Wt(Ut(i.c),n):Vt(Ut(i.c),n,"0"):10===t&&S?e=Vt(Ut((i=P(new M(i),_+n+1,m)).c),i.e,"0"):(Bt(t,2,x.length,"Base"),e=s(Vt(Ut(i.c),n,"0"),10,t,o,!0)),o<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return R(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),Yt=class{key;left=null;right=null;constructor(t){this.key=t}},Jt=class extends Yt{constructor(t){super(t)}},Zt=class{size=0;modificationCount=0;splayCount=0;splay(t){const e=this.root;if(null==e)return this.compare(t,t),-1;let i=null,s=null,o=null,n=null,r=e;const a=this.compare;let l;for(;;)if(l=a(r.key,t),l>0){let e=r.left;if(null==e)break;if(l=a(e.key,t),l>0&&(r.left=e.right,e.right=r,r=e,e=r.left,null==e))break;null==i?s=r:i.left=r,i=r,r=e}else{if(!(l<0))break;{let e=r.right;if(null==e)break;if(l=a(e.key,t),l<0&&(r.right=e.left,e.left=r,r=e,e=r.right,null==e))break;null==o?n=r:o.right=r,o=r,r=e}}return null!=o&&(o.right=r.left,r.left=n),null!=i&&(i.left=r.right,r.right=s),this.root!==r&&(this.root=r,this.splayCount++),l}splayMin(t){let e=t,i=e.left;for(;null!=i;){const t=i;e.left=t.right,t.right=e,e=t,i=e.left}return e}splayMax(t){let e=t,i=e.right;for(;null!=i;){const t=i;e.right=t.left,t.left=e,e=t,i=e.right}return e}_delete(t){if(null==this.root)return null;if(0!=this.splay(t))return null;let e=this.root;const i=e,s=e.left;if(this.size--,null==s)this.root=e.right;else{const t=e.right;e=this.splayMax(s),e.right=t,this.root=e}return this.modificationCount++,i}addNewRoot(t,e){this.size++,this.modificationCount++;const i=this.root;null!=i?(e<0?(t.left=i,t.right=i.right,i.right=null):(t.right=i,t.left=i.left,i.left=null),this.root=t):this.root=t}_first(){const t=this.root;return null==t?null:(this.root=this.splayMin(t),this.root)}_last(){const t=this.root;return null==t?null:(this.root=this.splayMax(t),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(t){return this.validKey(t)&&0==this.splay(t)}defaultCompare(){return(t,e)=>te?1:0}wrap(){return{getRoot:()=>this.root,setRoot:t=>{this.root=t},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:t=>{this.splayCount=t},splay:t=>this.splay(t),has:t=>this.has(t)}}},Xt=class t extends Zt{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 Jt(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Jt(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 Jt(t.key);return function t(e,i){let s,o;do{if(s=e.left,o=e.right,null!=s){const e=new Jt(s.key);i.left=e,t(s,e)}if(null!=o){const t=new Jt(o.key);i.right=t,e=o,i=t}}while(null!=o)}(t,e),e}toSet(){return this.clone()}entries(){return new ee(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new te(this.wrap())}[Symbol.toStringTag]="[object Set]"},Qt=class{tree;path=new Array;modificationCount=null;splayCount;constructor(t){this.tree=t,this.splayCount=t.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const t=this.path[this.path.length-1];return this.getValue(t)}rebuildPath(t){this.path.splice(0,this.path.length),this.tree.splay(t),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(t){for(;null!=t;)this.path.push(t),t=t.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let t=this.tree.getRoot();for(;null!=t;)this.path.push(t),t=t.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let t=this.path[this.path.length-1],e=t.right;if(null!=e){for(;null!=e;)this.path.push(e),e=e.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===t;)t=this.path.pop();return this.path.length>0}},te=class extends Qt{getValue(t){return t.key}},ee=class extends Qt{getValue(t){return[t.key,t.key]}},ie=t=>()=>t,se=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):ie(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function oe(t){const e=t?(e,i,s,o,n)=>e.exponentiatedBy(2).isLessThanOrEqualTo(o.minus(i).exponentiatedBy(2).plus(n.minus(s).exponentiatedBy(2)).times(t)):ie(!1);return(t,i,s)=>{const o=t.x,n=t.y,r=s.x,a=s.y,l=n.minus(a).times(i.x.minus(r)).minus(o.minus(r).times(i.y.minus(a)));return e(l,o,n,r,a)?0:l.comparedTo(0)}}var ne=t=>t,re=t=>{if(t){const e=new Xt(se(t)),i=new Xt(se(t)),s=(t,e)=>e.addAndReturn(t),o=t=>({x:s(t.x,e),y:s(t.y,i)});return o({x:new Kt(0),y:new Kt(0)}),o}return ne},ae=t=>({set:t=>{le=ae(t)},reset:()=>ae(t),compare:se(t),snap:re(t),orient:oe(t)}),le=ae(),ce=(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),he=(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}}},de=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),pe=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ue=t=>pe(t,t).sqrt(),_e=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return de(o,s).div(ue(o)).div(ue(s))},me=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return pe(o,s).div(ue(o)).div(ue(s))},ge=(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)))},ve=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:Me.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:_e(this.point,t.point,s.point),cosine:me(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:o,cosine:n}=e.get(t),{sine:r,cosine:a}=e.get(s);return o.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?n.isLessThan(a)?1:n.isGreaterThan(a)?-1:0:o.isLessThan(0)&&r.isLessThan(0)?n.isLessThan(a)?-1:n.isGreaterThan(a)?1:0:r.isLessThan(o)?-1:r.isGreaterThan(o)?1:0}}},ye=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,o=e.length;s0&&(t=i)}let e=t.segment.prevInResult(),i=e?e.prevInResult():null;for(;;){if(!e)return null;if(!i)return e.ringOut;if(i.ringOut!==e.ringOut)return i.ringOut?.enclosingRing()!==e.ringOut?e.ringOut:e.ringOut?.enclosingRing();e=i.prevInResult(),i=e?e.prevInResult():null}}},be=class{exteriorRing;interiorRings;constructor(t){this.exteriorRing=t,t.poly=this,this.interiorRings=[]}addInterior(t){this.interiorRings.push(t),t.poly=this}getGeom(){const t=this.exteriorRing.getGeom();if(null===t)return null;const e=[t];for(let t=0,i=this.interiorRings.length;t0?(this.tree.delete(e),i.push(t)):(this.segments.push(e),e.prev=s)}else{if(s&&o){const t=s.getIntersection(o);if(null!==t){if(!s.isAnEndpoint(t)){const e=this._splitSafely(s,t);for(let t=0,s=e.length;t0)return-1;const s=e.comparePoint(t.rightSE.point);return 0!==s?s:-1}if(i.isGreaterThan(s)){if(r.isLessThan(a)&&r.isLessThan(c))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(c))return 1;const i=e.comparePoint(t.leftSE.point);if(0!==i)return i;const s=t.comparePoint(e.rightSE.point);return s<0?1:s>0?-1:1}if(r.isLessThan(a))return-1;if(r.isGreaterThan(a))return 1;if(o.isLessThan(n)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(o.isGreaterThan(n)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!o.eq(n)){const t=l.minus(r),e=o.minus(i),h=c.minus(a),d=n.minus(s);if(t.isGreaterThan(e)&&h.isLessThan(d))return 1;if(t.isLessThan(e)&&h.isGreaterThan(d))return-1}return o.isGreaterThan(n)?1:o.isLessThan(n)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++Se,this.leftSE=t,t.segment=this,t.otherSE=e,this.rightSE=e,e.segment=this,e.otherSE=t,this.rings=i,this.windings=s}static fromRing(e,i,s){let o,n,r;const a=ve.comparePoints(e,i);if(a<0)o=e,n=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);o=i,n=e,r=-1}const l=new ve(o,!0),c=new ve(n,!1);return new t(l,c,[s],[r])}replaceRightSE(t){this.rightSE=t,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const t=this.leftSE.point.y,e=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:t.isLessThan(e)?t:e},ur:{x:this.rightSE.point.x,y:t.isGreaterThan(e)?t:e}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(t){return t.x.eq(this.leftSE.point.x)&&t.y.eq(this.leftSE.point.y)||t.x.eq(this.rightSE.point.x)&&t.y.eq(this.rightSE.point.y)}comparePoint(t){return le.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=he(e,i);if(null===s)return null;const o=this.leftSE.point,n=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=ce(e,r)&&0===this.comparePoint(r),c=ce(i,o)&&0===t.comparePoint(o),h=ce(e,a)&&0===this.comparePoint(a),d=ce(i,n)&&0===t.comparePoint(n);if(c&&l)return d&&!h?n:!d&&h?a:null;if(c)return h&&o.x.eq(a.x)&&o.y.eq(a.y)?null:o;if(l)return d&&n.x.eq(r.x)&&n.y.eq(r.y)?null:r;if(d&&h)return null;if(d)return n;if(h)return a;const p=((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 ge(i,s,t.y);if(s.y.isZero())return ge(t,e,i.y);const o=de(e,s);if(o.isZero())return null;const n={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=de(n,e).div(o),a=de(n,s).div(o),l=t.x.plus(a.times(e.x)),c=i.x.plus(r.times(s.x)),h=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(o,this.vector(),r,t.vector());return null===p?null:ce(s,p)?le.snap(p):null}split(e){const i=[],s=void 0!==e.events,o=new ve(e,!0),n=new ve(e,!1),r=this.rightSE;this.replaceRightSE(n),i.push(n),i.push(o);const a=new t(o,r,this.rings.slice(),this.windings.slice());return ve.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),ve.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(o.checkForConsuming(),n.checkForConsuming()),i}swapEvents(){const t=this.rightSE;this.rightSE=this.leftSE,this.leftSE=t,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let t=0,e=this.windings.length;t0){const t=i;i=s,s=t}if(i.prev===s){const t=i;i=s,s=t}for(let t=0,e=s.rings.length;t1===t.length&&t[0].isSubject;this._isInResult=i(t)!==i(e);break}}return this._isInResult}},Ce=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=le.snap({x:new Kt(t[0][0]),y:new Kt(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let o=s;for(let e=1,i=t.length;exe.run("union",t,e),Re=(t,...e)=>xe.run("intersection",t,e),ze=(t,...e)=>xe.run("difference",t,e);function Ae(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function Ie(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 Ee(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 Ne(t,e,i=1){const s=t[0].toFixed(i),o=t[1].toFixed(i),n=e[0].toFixed(i),r=e[1].toFixed(i),a=s=3?t.poly:t&&null!=t.x&&null!=t.y&&null!=t.w&&null!=t.h?[[t.x,t.y],[t.x+t.w,t.y],[t.x+t.w,t.y+t.h],[t.x,t.y+t.h]]:null}function Oe(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 Le(t){const e=[],i=new Set;for(const s of t||[]){const t=Fe(s);if(t)for(let s=0;s=90?t-=180:t<-90&&(t+=180),s.step&&s.step>0){const i=Math.sqrt(d),n=Math.min(Math.max(s.length||0,0)/2,i/2);let a=Math.round(p*i/s.step)*s.step;Math.abs(p*i-i/2)<=s.step/2&&(a=i/2),a=Math.max(n,Math.min(i-n,a));const l=a/i;o={x:e+l*c,y:r+l*h,angle:t}}else o={x:u[0],y:u[1],angle:t}}}return o}function qe(t,e,i,s,o,n=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,c=n;for(const e of Le(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(a[0]*(e[1]-t[1])-a[1]*(e[0]-t[0]));if(s(i[0])>n||s(i[1])>n)continue;const o=(i[0][0]-t[0])*a[0]+(i[0][1]-t[1])*a[1],r=(i[1][0]-t[0])*a[0]+(i[1][1]-t[1])*a[1],h=Math.min(o,r),d=Math.max(o,r),p=h>0?h:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],g=(h+d)/2;return{wallA:m(h),wallB:m(d),sideA:u,sideB:_,midA:m((h-p)/2),midB:m((p+d)/2),wallCenter:m(g),centered:Math.abs(g)<=o}}function Ue(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 je(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}function Ge(t,e,i){const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;let r=n?((t[0]-e[0])*s+(t[1]-e[1])*o)/n:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*o))}function We(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let o=0;oo&&r<-o||n<-o&&r>o)&&(a>o&&l<-o||a<-o&&l>o)}function Ze(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1;let c=0,h=0,d=0;for(let e=0;e1e-9?[h/(3*c),d/(3*c)]:[(o+n)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!Be([e,i],t))return-1/0;let s=1/0;for(let o=0;om&&(m=c,_=[s,l])}if(_){const[t,i]=_,s=(n-o)/e,l=(a-r)/e;for(let e=-4;e<=4;e++)for(let o=-4;o<=4;o++){const n=t+s*e/4,r=i+l*o/4,a=u(n,r);a>m&&(m=a,_=[n,r])}}return _||Xe(t)||t[0]}function Xe(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(Ke(s,t,e))return s;for(let s=0;sti(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&ti(e,t,i)))}function si(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 ni(t,e,i){for(let s=0;s1&&je(i[0],i[i.length-1],e)&&i.pop(),i}function ai(t,e,i){const[s,o]=t.split(":");return"device"===s?o:"entity"===s?"lg_"+o:e&&e.startsWith("v_")?e:i()}function li(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 hi(t,e,i,s){if(t.length<2)return;const o=e.x+s,n=e.x+e.w-s,r=e.y+s,a=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;st.startsWith("cover."))||null}const wi=["automation","script","scene"];const ki="—",$i="{}";function xi(t){const e=String(t??"").trim();if(!e)return null;let i=e,s="";const o=e.indexOf(":");if(o>=0)i=e.slice(0,o).trim(),s=e.slice(o+1).trim();else{const t=e.split(".");t.length>2&&(i=t.slice(0,2).join("."),s=t.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?o>=0&&!s||s&&!/^[a-zA-Z0-9_.-]+$/.test(s)?null:s?{entity:i,attr:s}:{entity:i}:null}function Si(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),o=xi(s?`${i}:${s}`:i);return o?`{${o.entity}${o.attr?`:${o.attr}`:""}}`:""}function Mi(t,e,i){const s=String(e??"").trim();if(!s)return null;const o=t?.states?.[s];if(!o)return null;const n=String(i??"").trim(),r=t=>t.slice(0,60);if(n){const e=function(t){if(null==t)return null;if(Array.isArray(t)){const e=t.map(t=>null==t?"":String(t)).join(", ");return e?e.slice(0,60):null}if("object"==typeof t)return null;const e=String(t);return""===e?null:e.slice(0,60)}(o.attributes?.[n]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,o,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=o.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function Ci(t,e,i){const s=String(e??"").trim(),o=String(i??"").trim()||s;if(!o)return t.text;const n=t.formatted&&s?function(t,e){if(!e)return t;const i=t.replace(/\s+$/,"");return i.endsWith(e)?i.slice(0,i.length-e.length).replace(/\s+$/,""):t}(t.text,s):t.text;return`${n} ${o}`}function Di(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],o=s?.state;if(!s||null==o||""===o||"unavailable"===o||"unknown"===o)return ki;const n=(e?.attr||"").trim(),r=Mi(t,i,n||null);if(null===r)return ki;return Ci(r,n?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}const Ti=20;function Pi(t){const e=Number(t?.scale);if(Number.isFinite(e)&&e>0)return Math.min(20,Math.max(.15,e));return{s:.7,m:1,l:1.5}[String(t?.size??"")]??1}function Ri(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 zi="#55606c",Ai=.55,Ii={c:"#607d8b",a:.18};function Ei(t,e=Ii){const i=t&&"object"==typeof t?t:null,s=i?.a;return{c:_t(i?.c,e.c),a:"number"==typeof s&&Number.isFinite(s)?Math.min(1,Math.max(0,s)):e.a}}function Ni(t,e){const i=Ei(t),s=e?.settings?.custom_fill;return s&&"object"==typeof s?Ei(s,i):i}function Fi(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:_t(e.room_color,zi),opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):Ai,fill:["lqi","light","temp","custom"].includes(e.fill_mode)?e.fill_mode:"none",customFill:Ei(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:_t(e.bg_color,null),hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function Oi(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return _t(i,"")}const Li={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 Hi(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(Li)){const s=Li[t],o=i[t];e[t]={c:_t(o?.c,s.c),a:o&&"number"==typeof o.a?Math.min(1,Math.max(0,o.a)):s.a}}return e}function qi(t,e,i){const s=Math.min(1,Math.max(0,i)),o=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),n=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=o.map((t,e)=>Math.round(t+(n[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function Ui(t,e,i,s,o,n,r,a=Ii){const l=function(t,e,i,s,o,n,r,a=Ii){if("custom"===t)return Ei(a);if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:qi(r.lqi_low.c,r.lqi_high.c,t),a:r.lqi_low.a+(r.lqi_high.a-r.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?r.light_none.a>0?r.light_none:null:"on"===i?r.light_on:r.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(o,n),e=Math.max(o,n);return se?r.temp_hot:r.temp_ok}return null}(t,e,i,s,o,n,r,a);return l?{color:l.c,opacity:l.a,mode:t}:null}const ji={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"]},Bi=[["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 Gi(t){for(const e of[...Object.values(ji),...Bi])if(t===e[0]||t===e[1])return e;return null}function Wi(t,e,i,s,o){if(!s||"unavailable"===s||"unknown"===s)return t;if(o){const i="cover"===e?Gi(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=ji[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const o=Gi(t);return o?"closed"===s?o[0]:o[1]:t}return"lock"===e?"locked"===s?"mdi:lock":"mdi:lock-open-variant":"light"===e&&"mdi:lightbulb"===t&&"on"===s?"mdi:lightbulb-on":t}function Vi(t){const e=Math.min(4e4,Math.max(1e3,t))/100,i=e<=66?255:329.698727446*Math.pow(e-60,-.1332047592),s=e<=66?99.4708025861*Math.log(e)-161.1195681661:288.1221695283*Math.pow(e-60,-.0755148492),o=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,n=t=>Math.round(Math.min(255,Math.max(0,t)));return[n(i),n(s),n(o)]}const Ki=1/2.2;function Yi(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 Ji(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=_t(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 Zi(t,e,i){const s=Ji(e),o=t?.attributes||{},n=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:n};const r=ft(o.rgb_color);if(r)return{c:Yi(r)||_t(i,"#ffd9a0"),bri:n};const a=Number(o.color_temp_kelvin)||(Number(o.color_temp)>0?1e6/Number(o.color_temp):NaN);return Number.isFinite(a)&&a>0?{c:(l=Vi(a),"#"+l.slice(0,3).map(t=>Math.min(255,Math.max(0,Math.round(Number(t)||0))).toString(16).padStart(2,"0")).join("")),bri:n}:{c:_t(i,"#ffd9a0"),bri:n};var l}function Xi(t,e,i){return"on"===t?.state?Zi(t,e,i):null}function Qi(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,Ki));return Math.max(0,Math.min(1,s))}function ts(t,e,i,s,o=170,n=0){const r=Math.hypot(e[0]-t[0],e[1]-t[1]),a=Math.hypot(i[0]-t[0],i[1]-t[1]);if(r<1e-6||a<1e-6||Math.min(r,a)>=s)return null;let l,c;if(Number.isFinite(n)&&n>1e-6){const s=i[0]-e[0],o=i[1]-e[1],r=Math.hypot(s,o);if(r<1e-6)return null;const a=-(o/r),h=s/r,d=(e[0]+i[0])/2,p=(e[1]+i[1])/2,u=(t[0]-d)*a+(t[1]-p)*h>=0?1:-1,_=n/2,m=[e[0]+a*_*u,e[1]+h*_*u],g=[i[0]+a*_*u,i[1]+h*_*u],f=[e[0]-a*_*u,e[1]-h*_*u],v=[i[0]-a*_*u,i[1]-h*_*u],y=Math.atan2(p-t[1],d-t[0]),b=e=>{let i=Math.atan2(e[1]-t[1],e[0]-t[0]);for(;i-y>Math.PI;)i-=2*Math.PI;for(;i-y<-Math.PI;)i+=2*Math.PI;return i},w=b(m),k=b(g),$=b(f),x=b(v),S=Math.max(Math.min(w,k),Math.min($,x)),M=Math.min(Math.max(w,k),Math.max($,x));if(!(M-S>1e-9))return null;l=S,c=M-S}else{for(l=Math.atan2(e[1]-t[1],e[0]-t[0]),c=Math.atan2(i[1]-t[1],i[0]-t[0])-l;c>Math.PI;)c-=2*Math.PI;for(;c<-Math.PI;)c+=2*Math.PI}const h=o*Math.PI/180;if(Math.abs(c)>h){const t=l+c/2;c=h*Math.sign(c),l=t-c/2}const d=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=l+c*e/8;d.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return d}function es(t,e,i,s,o){const n=e*Math.PI/180,r=[-Math.sin(n),Math.cos(n)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*o*a,t[1]+r[1]*o*a];return s.some(t=>Ke(l,t,1e-9))}function is(t){return t.startsWith("light.")||t.startsWith("switch.")}function ss(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let o=0;op||l>p)continue;const u=(o[0]-n[0])*h+(o[1]-n[1])*d,_=(r[0]-n[0])*h+(r[1]-n[1])*d,m=Math.max(0,Math.min(u,_)),g=Math.min(c,Math.max(u,_));g-m>i&&s.push([n[0]+h*m,n[1]+d*m,n[0]+h*g,n[1]+d*g])}}return s}function os(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let o=!0;for(;o;){o=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const n of e)if(n.id&&i.has(n.id)&&s(t,n)){i.add(t.id),o=!0;break}}return i}function ns(t,e,i=1e-6){const s=[];for(const o of t){const t=[o[0],o[1]],n=[o[2],o[3]],r=n[0]-t[0],a=n[1]-t[1],l=Math.hypot(r,a);if(ln||o>n)continue;const r=(s[0]-t[0])*c+(s[1]-t[1])*h,a=(s[2]-t[0])*c+(s[3]-t[1])*h,p=Math.max(0,Math.min(r,a)),u=Math.min(l,Math.max(r,a));u-p>i&&d.push([p,u])}if(!d.length){s.push([t[0],t[1],n[0],n[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,o]of d)e-p>i&&s.push([t[0]+c*p,t[1]+h*p,t[0]+c*e,t[1]+h*e]),p=Math.max(p,o);l-p>i&&s.push([t[0]+c*p,t[1]+h*p,n[0],n[1]])}return s}function rs(t,e,i=1e-6){const s=[];for(let e=0;e{if("string"!=typeof t||!t)return;const i=hs(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 hs(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 ds(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i||"custom"===i?i:t}function ps(t,e){const i=e?.settings;return"boolean"==typeof i?.glow?i.glow:"glow"===i?.fill_mode||t}function us(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function _s(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function ms(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function gs(t,e){const i=e[2]-e[0],s=e[3]-e[1],o=i*i+s*s;if(!o)return Math.hypot(t[0]-e[0],t[1]-e[1]);let n=((t[0]-e[0])*i+(t[1]-e[1])*s)/o;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+n*i),t[1]-(e[1]+n*s))}const fs=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);function vs(t,e){return"alarm_control_panel"===t||"siren"===t||"binary_sensor"===t&&!!e&&fs.has(e)}function ys(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 bs={availability:"available",status:"alarm",activity:"none"},ws=new Set(["motion","vibration","sound"]),ks=new Set(["occupancy","presence"]),$s=new Set(["door","window","garage_door","opening"]),xs=new Set(["running","power"]),Ss=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Ms=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),Cs=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping"]),Ds=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),Ts=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,Ps=t=>String(t??"").trim().toLowerCase();function Rs(t,e){if(!e.startsWith("switch."))return!1;const i=t?.entities?.[e]||{},s=t?.states?.[e],o=e.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(o))return!0;if([i.translation_key,i.original_name,i.name].map(Ps).some(t=>["power","main power","power switch","питание"].includes(t)))return!0;const n=Ps(s?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(n)||/(?:^|[\s._-])питание$/.test(n)}function zs(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=Ps(t?.[e]);if(Ms.has(i)||Cs.has(i))return i}return""}function As(t,e){const i=t?.states?.[e],s=i?Ps(i.state):"__missing__",o=String(e||"").split(".")[0],n=Ps(i?.attributes?.device_class),r={eid:e,state:s,availability:Ts(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===r.availability)return r;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&vs(t,e)}(o,n,s))return{...r,status:"alarm"};if("binary_sensor"===o)return ws.has(n)?{...r,edge:"rising"}:ks.has(n)?{...r,activity:"on"===s?"presence":"none"}:$s.has(n)?{...r,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===n?{...r,activity:"on"===s?"transition":"none"}:xs.has(n)&&"on"===s?{...r,status:"working",activity:"running"}:r;if("cover"===o)return{...r,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===o)return{...r,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===o)return{...r,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===o){const t=zs(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(Ps):[],o=!Cs.has(s)&&(Ds.has(s)||Ms.has(s)||e.includes(s));return(t?Ms.has(t):o)?{...r,status:"working",activity:"running"}:r}if(["light","switch","fan","humidifier"].includes(o))return"on"===s?{...r,status:"working",activity:"running"}:r;if("media_player"===o)return"off"===s?{...r,availability:"unavailable"}:r;if("vacuum"===o)return"cleaning"===s?{...r,status:"working",activity:"running"}:"returning"===s?{...r,status:"working",activity:"transition"}:r;if("script"===o)return"on"===s?{...r,status:"working",activity:"running"}:r;if("automation"===o)return r;if("button"===o||"event"===o)return{...r,edge:"change"};const a=zs(i.attributes);return Ms.has(a)||Ms.has(s)&&!Cs.has(s)?{...r,status:"working",activity:"running"}:r}function Is(t,e){if(!(t=>!!t&&!Ts(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 Es=new WeakMap,Ns=new WeakMap;let Fs=1;const Os="houseplan.ha-binding-status.v1";let Ls=null;function Hs(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=Es.get(e);return i||(i={revision:0,authoritative:!1,access:"pending",devices:{},entities:{},lastSuccess:0,listeners:new Set,refs:0},Es.set(e,i)),i}function qs(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 Us(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"})]),o=qs(i,"id"),n=qs(s,"entity_id");if(!o||!n)throw new Error("invalid_registry_response");e.devices=o,e.entities=n,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 js(t,e){void 0===e.reloadTimer&&(e.reloadTimer=globalThis.setTimeout(()=>{e.reloadTimer=void 0,Us(t,e)},80))}async function Bs(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,()=>js(t,e),"device_registry_updated")),e.unsubEntity||(e.unsubEntity=await i.call(t.connection,()=>js(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 Gs(t,e){const i=Hs(t);if(!i)return()=>{};const s=0===i.refs;i.refs++,i.listeners.add(e),("pending"===i.access||s)&&Us(t,i),Bs(t,i);let o=!1;return()=>{o||(o=!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 Ws(t){const e=Hs(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 o=e.liveDevices!==i||e.liveEntities!==s,n=void 0!==e.liveDevices||void 0!==e.liveEntities;if(o&&(e.liveDevices=i,e.liveEntities=s,e.projectedRevision=void 0,n&&js(t,e)),e.projectedRevision!==e.revision||!e.projectedDevices||!e.projectedEntities){const t={...e.devices},o={...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(o,t)||(o[t]=e);e.projectedDevices=t,e.projectedEntities=o,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 Vs(t){if(!t||"object"!=typeof t&&"function"!=typeof t)return 0;let e=Ns.get(t);return e||(e=Fs++,Ns.set(t,e)),e}function Ks(t,e=Ws(t)){return[e.revision,e.access,Vs(e.devices),Vs(e.entities)].join(":")}function Ys(t){return!!t&&null==t.disabled_by}function Js(t,e=Ws(t)){const i={},s={},o={};for(const[t,s]of Object.entries(e.devices||{}))Ys(s)&&(i[t]=s);for(const[t,i]of Object.entries(e.entities||{})){if(!Ys(i))continue;const o=i.device_id?e.devices?.[i.device_id]:null;e.authoritative&&i.device_id&&!o||(o&&!Ys(o)||(s[t]=i))}for(const[i,s]of Object.entries(t?.states||{})){const t=e.entities?.[i];if(e.authoritative&&!t)continue;if(t&&!Ys(t))continue;const n=t?.device_id?e.devices?.[t.device_id]:null;e.authoritative&&t?.device_id&&!n||(n&&!Ys(n)||(o[i]=s))}return{...t,devices:i,entities:s,states:o}}function Zs(t,e=Ws(t)){return{...t,devices:e.devices||{},entities:e.entities||{}}}function Xs(t,e){const i=[];for(const[s,o]of Object.entries(t||{}))o?.device_id===e&&i.push(s);return i}function Qs(){if(Ls)return Ls;if("undefined"==typeof localStorage)return{};try{const t=JSON.parse(localStorage.getItem(Os)||"{}");if(!t||"object"!=typeof t||Array.isArray(t))return{};const e=Date.now(),i={};for(const[s,o]of Object.entries(t))!o||"active"!==o.kind&&"ha_disabled"!==o.kind||!Number.isFinite(o.ts)||e-o.ts>7776e6||(i[s]=o);return Ls=i,Ls}catch{return{}}}function to(t,e,i=Ws(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 o=e.slice(0,s),n=e.slice(s+1);if("device"!==o&&"entity"!==o||!n)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const r=i.devices||{},a=i.entities||{};if(i.authoritative){if("device"===o){const t=r[n];if(!t)return{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[]};const e=Xs(a,n);if(!Ys(t))return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:e};const i=e.filter(t=>Ys(a[t]));return e.length&&!i.length?{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:e}:{kind:"active",enabledEntityIds:i,allEntityIds:e}}const e=a[n];return e&&!Ys(e)?{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[n]}:e?.device_id&&!r[e.device_id]?{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[n]}:e?.device_id&&!Ys(r[e.device_id])?{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[n]}:e||t?.states?.[n]?{kind:"active",enabledEntityIds:[n],allEntityIds:[n]}:{kind:"orphaned",reason:"entity_missing",enabledEntityIds:[],allEntityIds:[]}}const l=function(t){return Qs()[t]||null}(e);if("device"===o){const e=r[n],i=Xs(a,n);if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:i};const s=i.filter(t=>{const e=a[t];return null==e?.disabled_by&&(!e.device_id||null==r[e.device_id]?.disabled_by)});if(i.length&&!s.length&&i.every(t=>null!=a[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=a[n];if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[n]};if(e?.device_id&&null!=r[e.device_id]?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[n]};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"entity",enabledEntityIds:[],allEntityIds:[n]};if(e||t?.states?.[n])return{kind:"active",enabledEntityIds:[n],allEntityIds:[n]}}return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]}}function eo(t){const e={};for(const[i,s]of Object.entries(t.entities)){if(!s?.device_id||!Ys(s))continue;const o=t.devices?.[s.device_id];o&&!Ys(o)||(e[s.device_id]=e[s.device_id]||[]).push(i)}return e}function io(t,e,i){if(e.identifiers?.[0]?.[0])return e.identifiers[0][0];for(const e of i){const i=t.entities[e]?.platform;if(i)return i}return""}function so(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 oo=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],no=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function ro(t,e){const i=e.map(e=>({eid:e,reg:t?.entities?.[e]})).filter(t=>!!t.reg);if(!i.length)return[];const s=i.filter(t=>!t.reg.entity_category),o=s.length?s:i;for(const t of oo){const e=o.filter(e=>e.eid.startsWith(t+"."));if(e.length)return no(e).map(t=>t.eid)}const n=o.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=Ps(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return ws.has(i)||ks.has(i)||$s.has(i)||xs.has(i)||"moving"===i||Ss.has(i)}(t,e.eid));if(n.length)return no(n).map(t=>t.eid);const r=o.filter(t=>t.eid.startsWith("switch."));if(r.length){const e=no(r);return[(e.find(e=>Rs(t,e.eid))||e[0]).eid]}const a=[];for(const t of Tt)a.push(...no(o.filter(e=>e.eid.startsWith(t+"."))));return a.push(...no(o.filter(t=>!Tt.includes(t.eid.split(".")[0])))),a.map(t=>t.eid)}function ao(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),o=[s.filter(t=>!t.reg.hidden&&!t.reg.entity_category),s.filter(t=>!t.reg.entity_category),s.filter(t=>!t.reg.hidden),s];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const e of o){const i=e.find(e=>so(t,e.eid));if(i)return i.eid}return ro(t,e)[0]}function lo(t,e,i=[]){return[...new Set(co(t,e,i))].filter(t=>is(t))}function co(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 ho(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 po(t,e){if(!1===e.marker?.is_light)return[];if(!0===e.marker?.is_light){const i=function(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(is),s=t.primary&&is(t.primary)?t.primary:null;return(e&&is(e)?e:null)||s||i[0]||null}(e);return i&&t.states?.[i]?[{eid:i,via:"forced"}]:[]}const i=e.primary||ro(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"}))}function uo(t,e){const i=lo(e.marker?.binding,e.controls??e.marker?.controls,e.entities);return(null!=e.marker?.is_light||!i.length)&&po(t,e).length>0}function _o(t,e){const i=lo(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"})),s=uo(t,e)?po(t,e):[];for(const t of s)i.some(e=>e.eid===t.eid)||i.push(t);return i}function mo(t,e,i){const s=[],o=new Map;for(const n of e){if(n.hidden||null!=i&&!ho(n,i))continue;const e=_o(t,n);for(const{eid:i,via:r}of e){if(!i)continue;const e="controls"!==r,a={eid:i,device:n,via:r,castsGlow:e,on:"on"===t.states[i]?.state},l=o.get(i);null==l?(o.set(i,s.length),s.push(a)):e&&!s[l].castsGlow&&(s[l]=a)}}return s}function go(t){const e=t.filter(t=>t.castsGlow);return e.find(t=>t.on)||e[0]||null}function fo(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function vo(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const o=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===o){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const n=e.attributes?.linkquality??e.attributes?.lqi;if(null!=n){const t=parseFloat(n);isNaN(t)||i.push(t)}}return li(i)}function yo(t,e){for(const i of e){if(!so(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 bo(t,e){for(const i of e){if(!i.startsWith("climate."))continue;const e=t.states[i];if(!e||"unavailable"===e.state||"unknown"===e.state)continue;const s=parseFloat(e.attributes?.current_temperature);if(Number.isFinite(s))return Math.round(10*s)/10}return null}function wo(t,e){if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_humidity$/.test(e);const s=i.attributes||{};return"humidity"===s.device_class||"%"===s.unit_of_measurement&&/_humidity$/.test(e)||/_humidity$/.test(e)}function ko(t,e){for(const i of e){if(!wo(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(s)}return null}function $o(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden||!Ys(s))continue;let o=null;if("group"===s.platform)o=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if(!Ys(e))continue;if("Group"!==e?.model)continue;o=e.area_id||s.area_id||null}}if(!o)continue;const n=t.states[e];i.push({eid:e,name:s.name||n?.attributes?.friendly_name||e,area:o})}return i}function xo(t,e,i,s,o){const n=Dt(e,i,o);if(n!==Ct)return n;const r=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&r.push(i)}return function(t){for(const e of t){const t=Mt[e];if(t)return t}return null}(r)??Ct}function So(t){const e=new Set,i=new Set;for(const s of t||[]){if(!0!==s?.removed)continue;const t=String(s.binding||"").indexOf(":");if(t<1)continue;const o=s.binding.slice(0,t),n=s.binding.slice(t+1);n&&("device"===o?e.add(n):"entity"===o&&i.add(n))}return{devices:e,entities:i}}function Mo(t,e,i){if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function Co(t,e,i,s,o){const n=lo(e.binding,e.controls,t.entities).filter(t=>!Mo(i,t,s)).filter(t=>"active"===to(i,"entity:"+t,o).kind);t.marker=e,t.controls=n,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 Do(t){const e=t.hass,i=t.registry||Ws(e),s=Js(e,i),o=Zs(e,i),{areaToSpace:n,markers:r,settings:a,excluded:l,showAll:c,firstSpaceId:h,loc:d,iconRules:p}=t,u=!1!==a.group_lights,_=So(r),m=$o(s,u).filter(t=>!Mo(s,t.eid,_)),g=new Set(m.map(t=>t.area)),f=eo(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}(o),y=new Set;for(const t of r){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||y.add(t.binding)}const b=(t,e)=>r.find(i=>i.binding===t+":"+e),w={},k=[];for(const t of Object.values(s.devices)){const o=t.area_id;if(!o||!n[o])continue;if("service"===t.entry_type)continue;if(y.has("device:"+t.id))continue;const r=to(e,"device:"+t.id,i);if("active"!==r.kind)continue;const h=b("device",t.id);if(h&&h.hidden&&!a.filter_seeded)continue;const _=f[t.id]||[],m=io(s,t,_),v=!a.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=$+"|"+o;let S=xo(s,$,t.model,_,p);if(_.some(t=>t.startsWith("lock."))&&(S="mdi:lock"),v&&!c&&u&&"mdi:lightbulb"===S&&g.has(o))continue;w[x]=(w[x]||0)+1;const M=w[x]>1?$+" "+w[x]:$,C={id:t.id,name:M,model:t.model||"",area:o,space:n[o],icon:S,entities:_,allEntities:r.allEntityIds,bindingStatus:r,bindingKind:"device",bindingRef:t.id,pdfs:[]};C.primary=ao(s,_,S),"mdi:thermometer"!==S&&"mdi:air-filter"!==S||(C.temp=yo(s,_)),C.primary&&wo(s,C.primary)&&(C.hum=ko(s,_)),k.push(C)}for(const t of m)n[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:n[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 r){if(t.removed)continue;const[r,l]=t.binding.split(":"),c="device"===r||"entity"===r?to(e,t.binding,i):null;if(!t.hidden||a.filter_seeded||"ha_disabled"===c?.kind)if("device"===r){const e=c;if("unverified"===e.kind)continue;const r=o.devices[l],a=t.area||r?.area_id||"",u=a&&n[a]||t.space||h,m="active"===e.kind?e.enabledEntityIds:[];let g=r?xo(s,r.name_by_user||r.name||"",r.model,m,p):"mdi:help-circle";m.some(t=>t.startsWith("lock."))&&(g="mdi:lock");const f={id:t.id,name:r?.name_by_user||r?.name||d("device.fallback"),model:r?.model||"",area:a,space:u,icon:g,entities:m,allEntities:e.allEntityIds.length?e.allEntityIds:r&&v[r.id]||[],bindingStatus:e,bindingKind:"device",bindingRef:l};f.primary=ao(s,m,g),"mdi:thermometer"!==g&&"mdi:air-filter"!==g||(f.temp=yo(s,m)),f.primary&&wo(s,f.primary)&&(f.hum=ko(s,m)),Co(f,t,o,_,i),k.push(f)}else if("entity"===r){if(Mo(o,l,_))continue;const e=c;if("unverified"===e.kind)continue;const r=o.entities[l],a=t.area||r?.area_id||r?.device_id&&o.devices[r.device_id]?.area_id||"",d=a&&n[a]||t.space||h,u=s.states[l],m=r?.name||u?.attributes?.friendly_name||l;let g=xo(s,m,"",[l],p);l.startsWith("lock.")&&(g="mdi:lock");const f={id:t.id,name:m,model:"",area:a,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=yo(s,f.entities)),f.entities.length&&wo(s,l)&&(f.hum=ko(s,f.entities)),Co(f,t,o,_,i),k.push(f)}else{const e=t.area||"",s=t.space||e&&n[e]||h,r={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};Co(r,t,o,_,i),k.push(r)}}return k}function To(t,e,i,s){if(!e)return null;if(function(t,e,i){if(!e)return!1;const s=e.indexOf(":");if(s<1)return!1;const o=e.slice(0,s),n=e.slice(s+1),r=So(i);return"device"===o?r.devices.has(n):"entity"===o&&Mo(t,n,r)}(t,e,s))return null;const o=e.indexOf(":");if(o<0)return null;const n=e.slice(0,o),r=e.slice(o+1);if(!r)return null;if("entity"===n){const e=parseFloat(t.states[r]?.state);return Number.isFinite(e)?"temp"===i?Math.round(10*e)/10:Math.round(e):null}if("device"===n){const e=Object.entries(t.entities).filter(([,t])=>t.device_id===r).map(([t])=>t);return"temp"===i?yo(t,e):ko(t,e)}return null}const Po=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 Ro(t,e,i){const s=[];for(const o of e){if(o.area!==i||o.virtual)continue;const e=vo(t,o.entities);null!=e&&s.push(e)}return li(s)}function zo(t,e,i){const s=[];for(const o of e){if(o.area!==i)continue;if("mdi:thermometer"!==o.icon&&"mdi:air-filter"!==o.icon)continue;const e=yo(t,o.entities);null!=e&&s.push(e)}return s.length?Math.round(s.reduce((t,e)=>t+e,0)/s.length*10)/10:null}const Ao={sourceKind:"none",visualSources:[],criticalSources:[],samples:[]};function Io(t,e){const i=t?.entities?.[e],s=t?.states?.[e];return String(i?.name||i?.original_name||s?.attributes?.friendly_name||e)}function Eo(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 No(t,e,i,s){return{eid:e,role:i,name:Io(t,e),state:String(t?.states?.[e]?.state??""),stateText:Eo(t,e),integrationDomain:t?.entities?.[e]?.platform||null,sample:s||As(t,e)}}function Fo(t,e){const i=e.hidden&&e.userHidden?{...e,hidden:!1}:e;let s="none",o=[];const n="cover"===i.tapAction?bi(i.entities):null,r=mo(t,[i]);if(n)s="cover",o=[No(t,n,"cover")];else if(r.length)s=r.some(t=>"controls"===t.via)?"controls":"light",o=r.map(e=>No(t,e.eid,"controls"===e.via?"control":"forced"===e.via?"forced_light":"light"));else{const e=ro(t,i.entities);if(e.length){s="device_role";const n=function(t,e,i){const s=e.map(e=>As(t,e)),o=i.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category);return 1===e.length&&o.length>1&&Rs(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,e,i.entities);o=e.map((e,i)=>No(t,e,"device_role",n[i]))}else i.primary&&(s="primary",o=[No(t,i.primary,"primary")])}const a=[];for(const e of i.entities||[]){const i=As(t,e);"alarm"!==i.status||o.some(t=>t.eid===e)||a.push(No(t,e,"critical",i))}return{sourceKind:s,visualSources:o,criticalSources:a,samples:[...o,...a].map(t=>t.sample)}}function Oo(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(!so(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 o=function(t,e){if(!e.primary||!wo(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(o)return{source:{kind:"humidity",eid:o.eid,text:o.text},text:o.text,fallback:null}}if(1!==i.visualSources.length)return{source:null,text:null,fallback:i.visualSources.length?"value_ambiguous_sources":"value_no_state"};const o=i.visualSources[0].eid,n=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 o=String(s).trim().toLowerCase();if("unknown"===o||"unavailable"===o)return{text:"",fallback:"value_no_state"};const n=Mi(t,e);return n?{text:Ci(n,String(i.attributes?.unit_of_measurement||"")),fallback:null}:{text:"",fallback:"value_no_state"}}(t,o);return n.fallback?{source:null,text:null,fallback:n.fallback}:{source:{kind:"entity",eid:o,text:n.text},text:n.text,fallback:null}}function Lo(t,e,i){const s=t.marker?.binding||(t.bindingKind&&t.bindingRef?`${t.bindingKind}:${t.bindingRef}`:t.virtual?"virtual":""),o=[...e.visualSources.map(t=>`${t.role}:${t.eid}`),...e.criticalSources.map(t=>`critical:${t.eid}`)].sort(),n=i?`${i.kind}:${i.eid}:${i.attribute||""}`:"none";return[s,e.sourceKind,...o,`value:${n}`].join("\n")}function Ho(t,e,i=!0,s){const o=s||Fo(t,e);return Lo(e,o,Oo(t,e,o,i).source)}function qo(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 Uo(t,e,i){const s=ui(e.marker?.display),o="static_icon"===s,n=o&&!1===i.sourceDetails?Ao:Fo(t,e),r=e.bindingStatus,a="ha_disabled"===r?.kind,l="orphaned"===r?.kind,c=!0===e.userHidden||!0===e.marker?.hidden,h=a||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 bs;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}}(n.samples);let p=d;h||o?p={availability:"available",status:"neutral",activity:"none"}:"alarm"===d.status||i.liveStates||(p={availability:"available",status:"neutral",activity:"none"});const u=o&&!1===i.sourceDetails?{source:null,text:null,fallback:null}:Oo(t,e,n,i.showTemperature),_=Lo(e,n,u.source),m=i.activityRuntime,g=i.now??Date.now();!o&&!h&&i.liveStates&&"alarm"!==d.status&&m?.sources===_&&m.flashTs&&m.flashKind&&g-m.flashTs<3300&&(p={...p,activity:m.flashKind});const f="icon_ripple"===s&&!h&&i.liveStates&&"alarm"!==p.status?p.activity:"none",v=o||h||!i.showTemperature?null:!0===e.marker?.use_climate_temp?bo(t,e.entities):"mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon?yo(t,e.entities):null,y=!o&&!h&&i.showTemperature&&e.primary&&wo(t,e.primary)?ko(t,e.entities):null,b=o||h||!i.showSignal||e.virtual?null:vo(t,e.entities),w="cover"===n.sourceKind?n.visualSources[0]?.eid:e.primary||n.visualSources[0]?.eid,k=w?t?.states?.[w]:null,$=!i.liveStates||o||h?e.icon:Wi(e.icon,w?.split(".")[0],k?.attributes?.device_class,k?.state,!!e.marker?.icon),x=!i.liveStates||o||h?null:mo(t,[{...e,hidden:!1}]).map(e=>function(t){return t&&"on"===t.state?ft(t.attributes?.rgb_color):null}(t?.states?.[e.eid])).find(t=>!!t)||null,S=Number(e.marker?.size)>0?Number(e.marker.size):1,M=Number(e.marker?.angle)||0,C=Number(e.marker?.ripple_size)>0?Number(e.marker.ripple_size):3,D=_t(e.marker?.ripple_color,null),T=o?null:D||x||null,P="value"!==s||h?null:u.text,R="ha_disabled"===r?.kind?r.reason:null,z=function(t,e,i,s,o,n,r,a){return n?"ha_disabled":r?"orphaned":"static_icon"===a?"static_icon":"alarm"===e.status?"alarm":o?"unavailable"===e.availability?"unavailable":"cover"===s?"cover_icon_state":"presence"===i?"presence":"event"===i?"event":"transition"===i?"transition":"working"===e.status?"icon_ripple"===a&&"none"!==i?"working_activity":"working":"open"===e.status?"open":(t.primary||"").startsWith("media_player.")?"media_neutral":"neutral":"live_states_disabled"}(e,p,f,n.sourceKind,i.liveStates,a,l,s),A=[];i.designPreview&&c&&A.push("hidden_design_preview"),o||!0!==e.marker?.vacuum?.live||A.push("vacuum_live_plan_only");const I=1===n.visualSources.length&&Rs(t,n.visualSources[0].eid),E=e.entities.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category).length;I&&E>1&&A.push("composite_power_source"),"icon_ripple"!==s&&"static_icon"!==s&&"alarm"!==d.status&&"none"!==d.activity&&A.push("activity_display_disabled");const N={binding:e.marker?.binding||(e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:e.virtual?"virtual":""),sourceKind:n.sourceKind,visualSources:n.visualSources,criticalSources:n.criticalSources,valueSource:u.source,sourceSignature:_,visual:p,display:s,icon:$,valueText:P,valueFullText:P,fallbackReason:"value"!==s||P?null:u.fallback,activity:f,activityGeneration:m?.gen||1,classes:[],tempText:null==v?null:String(v),humText:null==y?null:String(y),lqiText:null==b?null:String(b),lqiColor:null==b?null:Ae(b),lightColor:x,scale:S,angle:M,rippleScale:C,rippleColor:T,userHidden:c,effectiveHidden:h,haDisabled:a,disabledReason:R,orphaned:l,vacuumLive:!o&&!0===e.marker?.vacuum?.live,explanation:{reason:z,notices:A}};return{...N,classes:qo(N)}}const jo=new WeakMap;function Bo(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=jo.get(e);return i||(i={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},jo.set(e,i)),i}function Go(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function Wo(t,e){const i=Bo(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,Go(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Go(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 o={...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 n=t?.type||t?.action||t?.data?.action;"removed"===n||"remove"===n?delete o[i]:o[i]=e,s=!0}s&&(e.configEntries=o,Go(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 Vo(t){const e=Bo(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function Ko(t,e,i=Vo(t),s){const o=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(o?.name||"")||String(s||"")||e.replace(/_/g," ")}function Yo(t,e,i,s,o){const n=String(e||"").trim();if(!n)return null;const r=o?s.configEntries[o]:null;return{domain:n,label:Ko(t,n,s,r?.title),configEntryId:o,configEntryTitle:r?.title,confidence:i}}function Jo(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 Zo(t,e,i,s=Vo(t)){const o=i.entities?.[e]||t?.entities?.[e];if(!o)return null;const n=o.config_entry_id||void 0,r=n?s.configEntries[n]?.domain:"";return Yo(t,o.platform||r,"entity-platform",s,n)}var Xo={"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 (lights/switches)","marker.tap_label":"Tap action for this device","tap.toggle_note":"Toggle never applies to locks and alarms; hold the icon to open the info card.","import.title":"Create spaces from HA floors","import.hint":"Your Home Assistant already knows these floors. Pick the ones to turn into plan spaces — you will upload a floor-plan image for each one next. Rooms are then outlined by hand on the plan.","import.start":"Create {n} space(s)","import.manual":"Start from scratch","import.progress":"Floor {i} of {n}","import.done":"Spaces created. Outline the rooms: click grid dots and close the contour.","btn.skip":"Skip","space.scale_label":"Scale (grid cell size)","space.scale_unit":"cm per cell","space.display_section":"Display","space.show_borders":"Always show room borders","space.show_names":"Show room names (drag to move)","space.room_color":"Border & name color","space.opacity":"Opacity","space.fill_label":"Room fill","fill.none":"None","fill.lqi":"Zigbee signal","fill.light":"Lights","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…","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_hint":"empty = default from general settings","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set how thick it is. Empty or zero removes the thickness.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new room-wall segment (1–100 cm). Shared walls keep the neighbour's value.","toast.wallthick_pick":"Click a wall (not an open boundary)","toast.wallthick_open":"Open boundaries have no thickness","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from 1 to {max} {unit}","toast.physical_angle":"Enter a rotation angle from 0° up to, but not including, 90°","toast.physical_limit":"The space has reached the limit for this type of geometry","markup.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":"Use the device's temperature sensor","marker.use_climate_temp_tip":"Air conditioners and thermostats know the room temperature (current_temperature). Shows it as a badge next to the icon and counts it in the room average — like a regular thermometer.","marker.light_role_label":"Is this device a light source?","marker.light_role_tip":"Controls whether this marker itself is a spatial light source. Linked lights above remain independent room-light controls.","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.glow_color_label":"Glow colour and brightness","marker.glow_color_tip":"Use live source values, override only its colour, or fix both 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_brightness_hint":"Minimum 1%. To disable this marker as a light source, choose “Never” above.","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.","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.","run.automation":"automation","run.script":"script","run.scene":"scene","confirm.tap_run":'Run "{name}"?',"confirm.tap_toggle":'Toggle "{name}"?',"confirm.tap_cover":'Open/close "{name}"?',"toast.run_started":"Started: {name}","toast.run_target_missing":"Run target not found — check the device settings","toast.run_target_required":"Pick an automation, script or scene","btn.run":"Run","vac.section":"Robot vacuum: live position","vac.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"};const Qo={en:Xo,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":"Toggle никогда не применяется к замкам и сигнализациям; долгое нажатие всегда открывает инфо-карточку.","import.title":"Создать пространства из этажей HA","import.hint":"Home Assistant уже знает эти этажи. Отметьте, какие превратить в пространства плана — далее для каждого попросим картинку плана. Комнаты затем обводятся вручную по плану.","import.start":"Создать: {n}","import.manual":"Начать с нуля","import.progress":"Этаж {i} из {n}","import.done":"Пространства созданы. Обведите комнаты: кликайте по точкам сетки и замкните контур.","btn.skip":"Пропустить","space.scale_label":"Масштаб (размер клетки сетки)","space.scale_unit":"см на клетку","space.display_section":"Отображение","space.show_borders":"Всегда отображать границы комнат","space.show_names":"Отображать названия комнат (перетаскиваются)","space.room_color":"Цвет границ и названий","space.opacity":"Прозрачность","space.fill_label":"Заливка комнат","fill.none":"Нет","fill.lqi":"По силе зигби-сигнала","fill.light":"По освещению","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":"Загрузка…","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_hint":"пусто = по умолчанию из общих настроек","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены комнаты (1–100 см). Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от 1 до {max} {unit}","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","markup.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.light_role_label":"Является источником света","marker.light_role_tip":"Определяет, является ли сам маркер пространственным источником света. Привязанные выше лампы остаются независимыми источниками для комнаты.","marker.light_role_auto_yes":"Автоматически (источник света)","marker.light_role_auto_no":"Автоматически (не источник света)","marker.light_role_always":"Всегда источник света","marker.light_role_never":"Никогда не источник света","marker.glow_color_label":"Цвет и яркость свечения","marker.glow_color_tip":"Использовать данные источника, задать только цвет либо зафиксировать цвет и яркость.","marker.glow_mode_auto":"Из источника","marker.glow_mode_color":"Задать цвет","marker.glow_mode_fixed":"Задать цвет и яркость","marker.glow_color":"Цвет свечения","marker.glow_brightness":"Яркость","marker.glow_brightness_hint":"Минимум 1%. Чтобы отключить источник света, выберите «Никогда» выше.","marker.glow_disabled_never":"Настройки свечения недоступны: маркер явно исключён из источников света.","marker.glow_disabled_auto":"В автоматическом режиме у этого маркера не найден пространственный источник света.","marker.glow_disabled_no_entity":"Для пространственного источника нет активной управляемой сущности 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":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","run.automation":"автоматизация","run.script":"скрипт","run.scene":"сцена","confirm.tap_run":"Запустить «{name}»?","confirm.tap_toggle":"Переключить «{name}»?","confirm.tap_cover":"Открыть/закрыть «{name}»?","toast.run_started":"Запущено: {name}","toast.run_target_missing":"Цель запуска не найдена — проверьте настройки устройства","toast.run_target_required":"Выберите автоматизацию, скрипт или сцену","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.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":"Применить"}};function tn(t,e){if(e&&e in Qo)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function en(t,e,i){return Ri(Qo[t][e]??Xo[e]??e,i)}class sn extends lt{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=Wo(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0}get _lang(){return tn(this.hass)}_t(t,e){return en(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=Vo(t)){if("virtual"===e)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const o=e.indexOf(":");if(o<1)return[];const n=e.slice(0,o),r=e.slice(o+1);if("entity"===n){const e=Zo(t,r,i,s);return e?[e]:[]}if("device"!==n)return[];const a=i.devices?.[r]||t?.devices?.[r];if(!a)return[];const l=[a.config_entry_id,...Array.isArray(a.config_entries)?a.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const i=s.configEntries[e];return Yo(t,i?.domain,"registry-owner",s,e)});if(l.some(Boolean))return Jo(l);const c=[];for(const[e,o]of Object.entries(i.entities||{}))o?.device_id===r&&null==o?.disabled_by&&c.push(Zo(t,e,i,s));return c.some(Boolean)?Jo(c):Jo((Array.isArray(a.identifiers)?a.identifiers:[]).map(e=>{const i=Array.isArray(e)?e[0]:null;return Yo(t,i,"identifier-fallback",s)}))}(this.hass,this.presentation.binding,this.registry,Vo(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=Zo(this.hass,t,this.registry,Vo(this.hass));return e?this._providerText(e):this._t("marker.preview.unknown_provider")}render(){const t=this.presentation;if(!t)return W;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:qo(i)}}(t,this._demoGeneration):t,s=this._providers(),o="alarm"===t.visual.status||"none"!==t.activity,n=i.scale*("none"!==i.activity?i.rippleScale:1),r=Math.min(1,2.5/Math.max(1,n)),a=Math.round(100*r),l=["dev",...i.classes,"virtual"===i.binding?"virtual":"",i.haDisabled?"ghost ha-disabled":"",null!=i.valueText?"valonly":""].filter(Boolean).join(" "),c=bt(i).join(";"),h=e?this._t("marker.preview.demo_notice"):this._reason(t.explanation.reason),d=[this.deviceName,this._providerSummary(s),this._stateSummary(t),h,t.valueFullText||""].filter(Boolean).join(". ");return j`
${this._t("marker.preview.title")} @@ -3191,7 +3220,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f
`)}
- `:G} + `:W} ${"icon_ripple"===t.display?j``:G} -
`}}Vn.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},Vn.styles=[yt,o` + `:W} +
`}}sn.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},sn.styles=[yt,n` :host { display: block; min-width: 0; } .devicepreview { margin: 4px 0 14px; @@ -3292,74 +3321,74 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f .previewstage { min-height: 142px; } .previewfacts > div { grid-template-columns: minmax(0, 1fr); gap: 2px; } } - `],customElements.get("hp-device-preview")||customElements.define("hp-device-preview",Vn);const Kn=(t,e)=>[t[0]-e[0],t[1]-e[1]],Yn=(t,e)=>[t[0]+e[0],t[1]+e[1]],Jn=(t,e)=>t[0]*e[0]+t[1]*e[1],Zn=t=>Math.hypot(t[0],t[1]);function Xn(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r)+o&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function eo(t,e,i,s){const n=s||to(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 io(t,e,i,s){const n=[],o=Kn(i,e),r=Zn(o);if(rs||h>s)continue;const d=Jn(Kn(o,e),a),p=Jn(Kn(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&n.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return n}function so(t,e,i,s,n){const o=io(t,e,i,n);if(!o.length)return null;const r=t=>o.some(([e,i])=>Qn(t,e,i)<=n),a=t.length,l=[];for(let o=0;on||f>n)continue;const v=Jn(Kn(e,c),u),y=Jn(Kn(i,c),u),b=Math.max(0,Math.min(v,y)),w=Math.min(p,Math.max(v,y));if(!(w-b<=n)){if(b>n&&bn&&wZn(Kn(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const n=i[(s-1+i.length)%i.length],o=i[(s+1)%i.length],r=(t[0]-n[0])*(o[1]-n[1])-(t[1]-n[1])*(o[0]-n[0]),a=Zn(Kn(o,n))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function oo(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-n[0])*a[1]-(t[1]-n[1])*a[0],c=t=>(t[0]-n[0])*a[0]+(t[1]-n[1])*a[1],h=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const n=c(e);n<=h||n>=d||tMath.abs(r+(t-p)/g*(a-r));s=Math.min(s,f(_),f(m))}}return s}function ao(t,e,i){if(ei(t,e,i))return!0;if(ti(t,e,i)||ti(e,t,i))return!1;let s=0;try{const i=Re([[...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+=si(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function lo(t,e,i,s,n){const o=[i.n[0]*s,i.n[1]*s],r={polys:{},openings:{},movedSpans:{}};if(Math.abs(s)<1e-9)return r;for(const e of t){if(e.id===i.roomId){r.polys[e.id]=eo(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[Yn(i.a,o),Yn(i.b,o)]];continue}const t=io(e.poly,i.a,i.b,n);if(!t.length)continue;const a=so(e.poly,i.a,i.b,o,n);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[Yn(t,o),Yn(e,o)]))}for(const t of e)Qn([t.x,t.y],i.a,i.b)<=n&&(r.openings[t.id]=[t.x+o[0],t.y+o[1]]);return r}function co(t,e,i){for(const s of e)for(let e=0;ei)continue;const c=(t.x-n[0])*l[0]+(t.y-n[1])*l[1];if(c-t.length/2>=-i&&c+t.length/2<=a+i)return!0}return!1}function ho(t,e,i){return t.filter(t=>e.some(e=>{for(let s=0;sa.polys[t.id]||t.poly;for(const e of l){const s=t.find(t=>t.id===e),n=a.polys[e];if(!oo(n))return!1;const l=Xn(s.poly),h=Xn(n);if(Math.abs(h)t.find(t=>t.id===e).poly),d=t.map(c);for(const t of ho(e,h,2*r)){const e=a.openings[t.id];if(!co(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function uo(t,e,i,s,n,o){const r=t=>[s[0]+(t[0]-s[0])*n,s[1]+(t[1]-s[1])*n],a={poly:t.poly.map(r),openings:{}};for(const s of e){let e=!1;for(let i=0;i{for(let e=0;et.id===i);if(!l)return!1;const c=t.filter(t=>t.id!==i).map(t=>t.poly),h=uo(l,e,c,s,n,2*a),d=h.poly,p=function(t){const e=function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<3)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const n=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;n.length>=2&&i(n[n.length-2],n[n.length-1],s)<=0;)n.pop();n.push(s)}return s.pop(),n.pop(),s.concat(n)}(t);if(e.length<3)return 0;let i=1/0;for(let t=0;tt.id===i?d:t.poly);for(const t of ho(e,[l.poly],2*a)){const e=h.openings[t.id];if(!co(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function mo(t,e,i){const s=i/e;return si(t)*s*s/1e4}function go(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const fo=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],vo=t=>Math.min(1,Math.max(0,t));function yo(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=fo[fo.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&&We(o,t.poly))||null},r=o(1),a=o(-1);return r&&a?null:r||a?r?{normal:[-n[0],-n[1]],roomId:r.id}:{normal:n,roomId:a.id}:null}function wo(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function ko(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 $o(t,e){try{const i=Re([[...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 xo(t,e,i,s,n,o,r){if(!(s>0))return[];const a=function(t,e){const i=function(t,e){return function(t){const e=t%360;return e<0?e+360:e}(t-e)}(t,e)*Math.PI/180;return[Math.sin(i),-Math.cos(i)]}(i,n),l=[-a[0],-a[1]],c=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),h=[];for(const i of e){if(!(i.length>0))continue;const e=bo(i,t);if(!e||!wo(e.normal,a,s))continue;const n=t.find(t=>t.id===e.roomId);if(!n)continue;const d=o&&o[e.roomId]||n.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],m=Math.max(0,r?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(p)*u,y=Math.sin(p)*u,b=[g-v,f-y],w=[g+v,f+y],k=c*i.length,$=$o(ko(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 So(t){return Math.round(10*(Number(t)||0))/10}const Mo=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Co(t,e){const i=Mo(e?.north_deg);return null!==i?i:Mo(t?.north_deg)}function Do(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function To(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Po(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 Ro={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},zo=(t,e=1)=>{const i=Number(t);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):e},Ao=t=>{let e=Number(t);return Number.isFinite(e)?(e=(e%360+360)%360,e>180?e-360:e):0};function Io(t,e,i){return Ao(e)?[t.x,t.y]:i([t.x,t.y])}const Eo=(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},No=(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},Fo=(t,e,i,s=Ro.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?No(o,e,i):s};function Oo(t,e){return{color:t.color,opacity:zo(t.opacity),width_cm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),...e?{fill:t.fill,fill_color:t.fillColor,fill_opacity:zo(t.fillOpacity,.25)}:{}}}function Lo(t){const e=t.x+t.w/2,i=t.y+t.h/2,s=Ao(t.angle)*Math.PI/180,n=Math.cos(s),o=Math.sin(s),r=(t,s)=>{const r=t-e,a=s-i;return[e+r*n-a*o,i+r*o+a*n]};return[r(t.x,t.y),r(t.x+t.w,t.y),r(t.x+t.w,t.y+t.h),r(t.x,t.y+t.h)]}function Ho(t){const e=Lo(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 qo(t,e,i,s,n,o,r,a){const l=Ao(t.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l),d=-Math.sin(l),p=Math.cos(l),u=t.x+t.w/2,_=t.y+t.h/2,m=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,f=u+m*c+g*d,v=_+m*h+g*p,y=s-f,b=n-v;let w=(y*c+b*h)*(e>0?1:-1),k=(y*d+b*p)*(i>0?1:-1),$=null;if(o){const e=w/Math.max(t.w,a),i=k/Math.max(t.h,a);$=Math.max(a/Math.max(t.w,a),a/Math.max(t.h,a),e,i),w=t.w*$,k=t.h*$}if(r>0)if(o){const e=Math.max(1,Math.round(t.w/r)),i=Math.max(1,Math.round(t.h/r));if(Math.abs(t.w-e*r)<1e-6&&Math.abs(t.h-i*r)<1e-6){const s=(t,e)=>{let i=Math.abs(t),s=Math.abs(e);for(;s;)[i,s]=[s,i%s];return Math.max(1,i)},n=1/s(e,i),o=Math.max(a/t.w,a/t.h),r=Math.max(o,Math.round(($??1)/n)*n);w=t.w*r,k=t.h*r}else{t.w>=t.h?(w=Math.round(w/r)*r,k=w*(t.h/Math.max(t.w,a))):(k=Math.round(k/r)*r,w=k*(t.w/Math.max(t.h,a)))}}else w=Math.round(w/r)*r,k=Math.round(k/r)*r;w=Math.max(a,w),k=Math.max(a,k);return{x:f+(e>0?w/2:-w/2)*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)*p-k/2,w:w,h:k,angle:Ao(t.angle)||void 0}}const Uo=(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 r=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*n)/o));return[e[0]+s*r,e[1]+n*r]};const jo=15;function Wo(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function Bo(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function Go(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function Vo(t,e,i){const s=Number(e)>0?Number(e):5;return Bo(t)/s*i}function Ko(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function Yo(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 Jo(t,e,i){const s=Ko((t[0]+e[0])/2,i),n=Ko((t[1]+e[1])/2,i),[o,r]=Yo(t,e);let a=Math.atan2(r,o);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,c=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(c)},${n.toFixed(c)}@${l.toFixed(4)}`}function Zo(t,e,i,s){return 1===s?Jo(t,e,i):Jo([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function Xo(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 Qo(t,e,i,s,n){const o=n>0?n:1;return{key:Zo(t,e,s,o),cm:Bo(i),a:[t[0]/o,t[1]/o],b:[e[0]/o,e[1]/o]}}function tr(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[n,o]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[n,o,r].every(Number.isFinite)&&s.push({w:e,x:n*i,y:o*i,ang:r})}return s}function er(t,e){const[i,s]=Yo(t,e);let n=Math.atan2(s,i);return n<0&&(n+=Math.PI),n}function ir(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function sr(t,e,i,s,n=1){if(!t?.length)return null;const o=Zo(e,i,s,n),r=t.find(t=>t.key===o);if(r)return r;const a=n>0?n:1,l=(e[0]+i[0])/2,c=(e[1]+i[1])/2,h=er(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of tr(t,a))if(ir(e.ang,h)&&Math.hypot(e.x-l,e.y-c)<=d)return e.w;return null}function nr(t,e,i,s,n=1){const o=sr(t,e,i,s,n);return o&&o.cm>0?Bo(o.cm):0}function or(t,e,i,s=1,n=[]){if(!t?.length)return[];const o=new Set,r=Le(e);for(const t of r)o.add(Zo([t[0],t[1]],[t[2],t[3]],i,s));const a=e||[],l=Math.max(i*s*.02,1e-9);for(let t=0;t(o.has(t.key)||(t=>{const e=Xo(t,s);if(!e)return!1;const[i,o]=e,a=o[0]-i[0],c=o[1]-i[1],h=Math.hypot(a,c);if(h<=l)return!1;if(!r.some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];return ir(er(i,o),er(e,s))&&Sr(i[0],i[1],e[0],e[1],s[0],s[1])<=l&&Sr(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(!ir(er(i,o),er(e,s)))return!1;const n=t=>Math.abs((t[0]-i[0])*c-(t[1]-i[1])*a)/h;if(n(e)>l||n(s)>l)return!1;const r=h*h,d=((e[0]-i[0])*a+(e[1]-i[1])*c)/r,p=((s[0]-i[0])*a+(s[1]-i[1])*c)/r;return Math.min(1,Math.max(d,p))-Math.max(0,Math.min(d,p))>l/h});return!d})(t))&&t.cm>=1&&t.cm<=100)}function rr(t,e,i,s=8){const[n,o]=Yo(t,e);let r=Math.atan2(o,n);r<0&&(r+=Math.PI);let a=i*Math.PI/180%Math.PI;a<0&&(a+=Math.PI);let l=Math.abs(r-a);return l>Math.PI/2&&(l=Math.PI-l),l<=s*Math.PI/180}function ar(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,a=Math.max(.5*s,1e-9)*r,l=new Set,c=[];for(const n of t){let t="",h=null;const d=Xo(n,r);if(d)for(let n=0;na||Sr(d[1][0],d[1][1],o[0],o[1],l[0],l[1])>a)continue;const u=l[0]-o[0],_=l[1]-o[1],m=u*u+_*_;if(m<1e-18)continue;const g=t=>{const e=Math.max(0,Math.min(1,((t[0]-o[0])*u+(t[1]-o[1])*_)/m));return[c[0]+(p[0]-c[0])*e,c[1]+(p[1]-c[1])*e]};h=[g(d[0]),g(d[1])],t=Zo(h[0],h[1],s,r);break}if(d||(t=o.get(n.key)||""),!t){const o=tr([n],r)[0];if(o)for(let n=0;n1.000001)continue;if(Sr(o.x,o.y,l[0],l[1],c[0],c[1])>a)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]=Yo(h,d),b=Math.max(s*r,1e-6);t=Zo([g-v*b,f-y*b],[g+v*b,f+y*b],s,r);break}}t||(t=n.key),l.has(t)||(l.add(t),c.push(h?Qo(h[0],h[1],n.cm,s,r):{...n,key:t,cm:Bo(n.cm)}))}return c}function lr(t,e,i,s,n,o=1){const r=Zo(e,i,n,o),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,Qo(e,i,s,n,o)]}function cr(t,e,i,s,n,o=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,n=1,o=[]){const r=yr(t,e,i,s,n,o);if(!r)return[];const a=[];for(let t=0;t0)||!t||t.length<2)return"";if(i&&t.length>=3){let i=t;const s=t[t.length-1];if(t.length>=4&&Math.hypot(t[0][0]-s[0],t[0][1]-s[1])<1e-9&&(i=t.slice(0,-1)),i.length>=3){const t=i.map(()=>e),s=Tr(i,t),n=gr(i,t);if(s&&n)return`${fr(s)} ${fr(vr(n))}`}}let s="";for(let i=0;i.05)&&Sr(r,a,t[0],t[1],t[2],t[3])<=o)return!0}return!1}function pr(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function ur(t,e){const i=t[e],s=t[(e+1)%t.length],n=s[0]-i[0],o=s[1]-i[1],r=Math.hypot(n,o)||1;let a=-o/r,l=n/r;const c=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,n=e.length-1;st[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r+0)+o&&(i=!i)}return i}([c[0]+.001*a,c[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function _r(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 mr(t,e,i,s){const n=e[0]*s[1]-e[1]*s[0];if(Math.abs(n)<1e-12)return null;const o=[i[0]-t[0],i[1]-t[1]],r=(o[0]*s[1]-o[1]*s[0])/n;return[t[0]+r*e[0],t[1]+r*e[1]]}function gr(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(_r(b,w)){const e=t[n],i=[e[0]+p*h,e[1]+u*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=mr(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]+p*h,t[n][1]+u*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 fr(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),a=Fe(r);if(!a||a.length<3)return null;const l=pr(s,n),c=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Fe(i);if(t)for(const e of Zi(a,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=Xo(t,n);e&&c.push(e[0],e[1])}const h=[],d=[];for(let t=0;tl)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=n||r>=1-n||(o.some(t=>Math.abs(t-r)*s<=2*l)||o.push(r))}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:a}}function br(t,e,i,s){const n=e.poly.length,o=new Array(n).fill(0);if(!t?.length)return o;const r=new Set,a=[];for(let l=0;l0?(o[l]=Bo(c.cm),r.add(c.key)):a.push(l)}if(!a.length)return o;const l=s>0?s:1,c=Math.max(.5*i,1e-9)*l,h=tr(t,l).filter(t=>t.w.cm>0),d=new Map;for(const t of a){const i=e.parent[t],s=d.get(i);s?s.push(t):d.set(i,[t])}for(const[t,i]of d){const s=e.orig[t],n=e.orig[(t+1)%e.orig.length],a=er(s,n),d=(s[0]+n[0])/2,p=(s[1]+n[1])/2;let u=null;const _=Math.hypot(n[0]-s[0],n[1]-s[1]);for(const t of h){if(r.has(t.w.key))continue;if(!ir(t.ang,a))continue;const e=Xo(t.w,l);let i=!1,o=0;if(e){if(!ir(er(e[0],e[1]),a))continue;if(Sr(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>c||Sr(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(Sr(t.x,t.y,s[0],s[1],n[0],n[1])>c)continue;o=Math.hypot(t.x-d,t.y-p)}(!u||i&&!u.exact||i===u.exact&&ot?.id===e),n=Fe(s);if(!n)return[];const o=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Fe(s);if(t)for(const e of Zi(n,t,i))o.push(e)}return o}(t,e,pr(n,a)),h=function(t,e,i,s,n){const o=pr(s,n),r=[];for(let a=0;aSr(h,d,t[0],t[1],t[2],t[3])<=o);r.push(p?"shared":"outer")}return r}(l.poly,c,s,n,a),d=br(i,l,n,a),p=d.map((t,e)=>h[e]&&t>0?Vo(t,o,r)/2:0);return{...l,kinds:h,cms:d,offsets:p}}function kr(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const c=wr(t,l.id,e,i,s,n,o,r);if(c)for(let t=0;t0&&!l.has(c.key)&&(l.add(c.key),a.push(c));const c=[];for(const a of t||[]){if(!a?.id)continue;const l=wr(t,a.id,e,i,s,n,o,r);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:Zo(h,d,s,r),cm:n,len:p}),t=o+1}}}c.sort((t,e)=>e.len-t.len||t.key.localeCompare(e.key));const h=[],d=new Set,p=new Set,u=4*pr(s,r);for(const t of c){const e=a.filter(e=>!p.has(e.key)&&e.cm===t.cm&&ir(er(e.a,e.b),er(t.a,t.b))&&Sr(e.a[0],e.a[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u&&Sr(e.b[0],e.b[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u);if(e.length){for(const t of e)p.add(t.key);d.has(t.key)||(d.add(t.key),h.push(Qo(t.a,t.b,t.cm,s,r)))}}for(const t of a)p.has(t.key)||d.has(t.key)||(d.add(t.key),h.push(Qo(t.a,t.b,t.cm,s,r)));return h}function xr(t,e,i,s,n,o,r,a=1){const l=pr(n,a),c=(s[0]+s[2])/2,h=(s[1]+s[3])/2,d=er([s[0],s[1]],[s[2],s[3]]);let p=null;for(const s of kr(t,e,i,n,o,r,a)){if(!ir(er(s.a,s.b),d))continue;const t=Sr(c,h,s.a[0],s.a[1],s.b[0],s.b[1]);t>4*l||(!p||tt?.id===e),c=Fe(l);if(!c||c.length<3)return null;if(!i?.length)return c.map(t=>[t[0],t[1]]);const h=wr(t,e,i,s,n,o,r,a);return h&&h.offsets.some(t=>t>0)&&gr(h.poly,h.offsets)||c.map(t=>[t[0],t[1]])}function Cr(t){const e=t.map(t=>[t[0],t[1]]);return e.push([t[0][0],t[0][1]]),[e]}function Dr(t,e,i,s=[],n,o,r,a=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=wr(t,s.id,e,i,n,o,r,a);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=Tr(l.poly,l.offsets),p=gr(l.poly,l.offsets);d&&c.push({outset:d,inset:p})}for(const t of l){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);if(e.length){const s=Math.min(Math.max(...e)-Math.min(...e),Math.max(...i)-Math.min(...i)),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,r){if(!e?.length||!i?.length)return[];const a=4*pr(s,r),l=new Map;for(const a of kr(t,e,i,s,n,o,r))!a.open&&a.half>0&&!l.has(a.key)&&l.set(a.key,a);const c=[...l.values()];if(c.length<2)return[];const h=[];for(const t of i)for(const e of[[t[0],t[1]],[t[2],t[3]]])h.some(t=>Math.hypot(t[0]-e[0],t[1]-e[1])<=a)||h.push(e);const d=[],p=(t,e)=>{let i=0,s=0;if(Math.hypot(t.a[0]-e[0],t.a[1]-e[1])<=a)i=t.b[0]-t.a[0],s=t.b[1]-t.a[1];else{if(!(Math.hypot(t.b[0]-e[0],t.b[1]-e[1])<=a))return null;i=t.a[0]-t.b[0],s=t.a[1]-t.b[1]}const n=Math.hypot(i,s);return n>a?[i/n,s/n]:null};for(const t of h){const e=c.map(e=>({iv:e,u:p(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(r>0?[t.slice(),h,u,p]:[t.slice(),p,u,h])}}return d}(t,e,i,n,o,r,a),p=s.length?Rr(t,e,i,n,o,r,a):null;try{const t=t=>{const e=Cr(t.outset);return t.inset?ze(e,Cr(t.inset)):e};let e=c.length?t(c[0]):null;for(let i=1;i0))continue;const i=Ir(p,t,!0);if(!i.negative&&!i.positive)continue;const s=t.angle*Math.PI/180,o=Math.cos(s),r=Math.sin(s),l=-r,c=o,d=t.length/2,u=1.25*Math.max(h,n*a),_=[[t.x-o*d-l*u,t.y-r*d-c*u],[t.x+o*d-l*u,t.y+r*d-c*u],[t.x+o*d+l*u,t.y+r*d+c*u],[t.x-o*d+l*u,t.y-r*d+c*u]];e&&(e=ze(e,Cr(_)))}for(const t of l)t.length<3||(e=e?Pe(e,Cr(t)):[Cr(t)]);const i=function(t){if(!t)return"";let e="";for(const i of t)if(Array.isArray(i))for(const t of i){if(!Array.isArray(t)||t.length<4)continue;const i=t.slice(0,t.length-1);i.length<3||(e+=(e?" ":"")+fr(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:h,fillRule:"evenodd"}:null}catch{const s=function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const c=wr(t,l.id,e,i,s,n,o,r);if(!c||c.poly.length<3||!c.offsets.some(t=>t>0))continue;const h=Tr(c.poly,c.offsets),d=gr(c.poly,c.offsets);if(!h||!d)continue;const p=`${fr(h)} ${fr(vr(d))}`;let u="",_="outer",m=0,g=0;for(let t=0;t0){u=Zo(c.poly[t],c.poly[(t+1)%c.poly.length],s,r),_=c.kinds[t]||"outer",m=c.cms[t],g=Vo(m,n,o);break}a.push({d:p,key:u,kind:_,cm:m,depthUnits:g})}return a}(t,e,i,n,o,r,a),c=l.map(t=>fr(t)).join(" ");return s.length||c?{d:[s.map(t=>t.d).join(" "),c].filter(Boolean).join(" "),depthUnits:h,fillRule:"nonzero"}:null}}function Tr(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]]);vr(t),e.slice().reverse();const s=[];for(let n=0;n0||a>0)){s.push([t[n][0],t[n][1]]);continue}if(_r(b,w)){const e=t[n],i=[e[0]-l*r,e[1]-c*r],o=[e[0]-h*a,e[1]-d*a];s.push(i),Math.hypot(o[0]-i[0],o[1]-i[1])>1e-9&&s.push(o);continue}const x=mr(k,b,$,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[n][0],x[1]-t[n][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[n][0]-l*r,t[n][1]-c*r]),a>0&&s.push([t[n][0]-h*a,t[n][1]-d*a])}return s.length>=3?s:null}function Pr(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){const c=Fe(l);if(c&&c.length>=3){const h=wr(t,l.id,e,i,s,n,o,r),d=h&&h.offsets.some(t=>t>0)?Tr(h.poly,h.offsets):null,p=d||c;a.push({poly:p.map(t=>t.join(",")).join(" ")})}else l&&null!=l.x&&null!=l.y&&null!=l.w&&null!=l.h&&a.push({rect:{x:l.x,y:l.y,w:l.w,h:l.h,rx:.03*Math.min(l.w,l.h)}})}return a}function Rr(t,e,i,s,n,o,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const c=wr(t,l.id,e,i,s,n,o,r);if(!c)continue;const h=Math.abs(si(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],r=n[0][1],a=0,l=o<=e+s;for(let t=1;t=i-s,{coverage:a,full:l}}function Ar(t,e){return Number(e.full)-Number(t.full)||t.faceDistance-e.faceDistance||t.area-e.area||t.roomId.localeCompare(e.roomId)}function Ir(t,e,i=!1){const s=Number(e?.x),n=Number(e?.y),o=Number(e?.angle),r=Number(e?.length);if(!([s,n,o,r].every(Number.isFinite)&&r>0))return{negative:null,positive:null};const a=o*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=-c,d=l,p=r/2,u=Math.max(1e-9,t.adjacencyEps),_=new Map;let m=0;for(const e of t.edges){if(i&&!(e.half>0))continue;if(!rr(e.a,e.b,o))continue;const[t,r]=Yo(e.a,e.b);if(Math.abs((s-e.a[0])*r-(n-e.a[1])*t)>u)continue;const a=(e.a[0]-s)*l+(e.a[1]-n)*c,g=(e.b[0]-s)*l+(e.b[1]-n)*c,f=Math.max(-p,Math.min(a,g)),v=Math.min(p,Math.max(a,g));if(v-f<=u)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,r]},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=zr(t.pieces,-p,p,u);t.coverage=e.coverage,t.full=e.full}const g=t=>{const e=[..._.values()].filter(e=>e.side===t&&e.coverage>u);return e.sort(Ar),e[0]||null};return{negative:g(-1),positive:g(1)}}function Er(t,e){const i=Ir(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,r=(-1===o?i.negative:i.positive)||n,a=[...r.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(!(a.half>0&&a.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*a.half,oy:h*o*a.half,cm:a.cm,side:o}}function Nr(t,e){const i=1e-9,s=[];for(const t of[...e].filter(t=>t.x1>t.x0&&t.half>0).sort((t,e)=>t.x0-e.x0||t.x1-e.x1||t.half-e.half)){const e=s[s.length-1];e&&t.x0<=e.x1+i&&Math.abs(t.half-e.half)<=i?e.x1=Math.max(e.x1,t.x1):s.push({...t})}const n=[];for(let e=0;e=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 a}function Or(t,e){const i=new Map;return e.map(e=>function(t,e,i){const s=Ir(t,e,!0),n=s.negative,o=s.positive;if(!n&&!o)return null;let r;if(n&&o)r=[{candidate:n,side:-1},{candidate:o,side:1}];else{const t=n||o;r=[{candidate:t,side:-1},{candidate:t,side:1}]}const a=r.map(({candidate:t,side:s})=>({candidate:t,side:s,pieces:Fr(e,s,t.pieces,i)})),l=a.map(({candidate:t,side:e,pieces:i})=>({side:e,roomId:t.roomId,d:Nr(e,i)})),c=a.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 Lr=150;function Hr(t){return Number.isFinite(t)?Math.max(1,Math.min(Lr,t)):1}function qr(t){return((Number.isFinite(Number(t))?Number(t):0)%90+90)%90}const Ur=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 Wr(t,e,i,s,n){const o=e[0]-t[0],r=e[1]-t[1],a=Math.hypot(o,r);if(!(a>1e-9))return null;const l=Vo(i,s,n)/2,c=-r/a*l,h=o/a*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 Br(t,e,i){const s=Number(e)>0?Number(e):5,n=Hr(t.cm)/s*i,o=t.center[0],r=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,r+Math.sin(s)*t]})}const a=n/2,l=qr(t.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l);return[[-a,-a],[a,-a],[a,a],[-a,a]].map(([t,e])=>[o+t*c-e*h,r+t*h+e*c])}function Gr(t,e,i){const s=[];for(let n=0;n+1t.length>=3).map(t=>Ur(t));return e.length?Pe(e[0],...e.slice(1)):null}catch{return null}}(e);if(i)return ze(Ur(t),i)}catch{}let i=[Ur(t)];for(const t of e)if(!(t.length<3))try{i=ze(i,Ur(t))}catch{}return i}function Kr(t){let e=0;for(const i of t||[])if(i?.length){e+=si(i[0]||[]);for(let t=1;t=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]))}return e}function Jr(t,e,i){if(!(i>0))return[];const s=Math.max(1e-9,1e-9*i);if(t.some(t=>Zr(e,t)||(t=>t.some((i,n)=>{const o=t[(n+1)%t.length],r=o[0]-i[0],a=o[1]-i[1],l=r*r+a*a;if(!(l>0))return Math.hypot(e[0]-i[0],e[1]-i[1])<=s;const c=Math.max(0,Math.min(1,((e[0]-i[0])*r+(e[1]-i[1])*a)/l));return Math.hypot(e[0]-(i[0]+c*r),e[1]-(i[1]+c*a))<=s}))(t)))return[Array.from({length:128},(t,s)=>{const n=s/128*Math.PI*2;return[e[0]+Math.cos(n)*i*1.01,e[1]+Math.sin(n)*i*1.01]})];const n=[];for(const o of t)if(!(o.length<2)){n.push(o);for(let t=0;ts&&u>s))continue;const _=Math.max(-1,Math.min(1,(l*h+c*d)/(p*u))),m=Math.sqrt(Math.max(0,(1+_)/2)),g=Math.max(2*i,1.02*i/Math.max(m,.001)),f=[e[0]+l/p*g,e[1]+c/p*g],v=[e[0]+h/u*g,e[1]+d/u*g];n.push([r,a,v,f])}}return n}function Zr(t,e){let i=!1;for(let s=0,n=e.length-1;st[1]!=l>t[1]&&t[0]<(a-o)*(t[1]-r)/(l-r||1e-12)+o&&(i=!i)}return i}function Xr(t,e,i){if(Math.hypot(t.center[0]-e.center[0],t.center[1]-e.center[1])>i)return!1;if(Math.abs(Hr(t.cm)-Hr(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(qr(t.angle)-qr(e.angle));return Math.min(s,90-s)<=1e-6}const Qr=1e3;function ta(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 ea=.01,ia=100;function sa(t,e=1e3){const i=ta(t?.plan_aspect,e),s=Number(t?.plan_scale),n=Number.isFinite(s)&&s>0?Math.min(ia,Math.max(ea,s)):1,o=Number(t?.plan_scale_x),r=Number(t?.plan_scale_y),a=Number.isFinite(o)&&o>0?Math.min(ia,Math.max(ea,o)):n,l=Number.isFinite(r)&&r>0?Math.min(ia,Math.max(ea,r)):n,c=Number(t?.plan_x),h=Number(t?.plan_y),d=Ao(t?.plan_angle);return{x:i.x+(Number.isFinite(c)?_a(c):0)*e,y:i.y+(Number.isFinite(h)?_a(h):0)*e,w:i.w*a,h:i.h*l,...d?{angle:d}:{}}}function na(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 oa(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=Qr,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]*Qr,i[1]*e,i[2]*Qr,i[3]*e],bg:t.plan_url?{href:ns(t.plan_url),...sa(t,Qr)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...na(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*Qr:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*Qr:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*Qr,t[1]*e]):void 0}}),room_drafts:(t.room_drafts||[]).map(t=>({id:t.id,points:(t.points||[]).map(t=>[t[0]*Qr,t[1]*e]),segments:(t.segments||[]).map(t=>({cm:Number(t.cm)}))})),partitions:(t.partitions||[]).map(t=>({id:t.id,a:[t.a[0]*Qr,t.a[1]*e],b:[t.b[0]*Qr,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]*Qr,t.center[1]*e],cm:Number(t.cm),..."circle"===t.shape?{}:{angle:qr(t.angle)}}))}}):[]}const ra=5e3,aa=ra*Qr,la=240,ca=Qr/la,ha=1/la;function da(t){if(!Number.isFinite(t))return t;const e=Math.round(t*la/Qr)*Qr/la;return Math.abs(e-t)<=1e-9*ca?t:e}function pa(t){return{x:da(t.x),y:da(t.y)}}function ua(t){return Number.isFinite(t)?Math.min(aa,Math.max(-aa,t)):0}function _a(t){return Number.isFinite(t)?Math.min(ra,Math.max(-ra,t)):0}const ma=1/3,ga=200;function fa(t){let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const o of t){const t=Number(o[0]),r=Number(o[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>n&&(n=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:n}}function va(t){return t.poly&&t.poly.length?fa(t.poly):null==t.x||null==t.y?null:fa([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function ya(t,e){const i=[];for(const e of t.rooms||[]){const t=va(e);t&&i.push(t)}if(t.bg){const e=fa(Lo(t.bg));e&&i.push(e)}for(const t of e||[])if(Array.isArray(t)){const e=fa([t]);e&&i.push(e)}else i.push(t);return i}const ba=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},wa=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 ka(t,e){let{x:i,y:s,w:n,h:o}=t;n<30&&(i=i+n/2-100,n=ga),o<30&&(s=s+o/2-100,o=ga);const r=Math.max(n,o)*e;return{x:i-r,y:s-r,w:n+2*r,h:o+2*r}}function $a(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)<=aa&&Math.abs(t.maxX)<=aa&&Math.abs(t.minY)<=aa&&Math.abs(t.maxY)<=aa);if(!o.length)return{core:null,all:null,outliers:0};const r=wa(o);if(o.length<4){const t=ka(r,i);return{core:t,all:t,outliers:0}}const a=o.map(t=>(t.minX+t.maxX)/2),l=o.map(t=>(t.minY+t.maxY)/2),c=ba(a),h=ba(l),d=o.map((t,e)=>Math.max(Math.abs(a[e]-c),Math.abs(l[e]-h))),p=Math.max(((t,e)=>t.length?t[Math.min(t.length-1,Math.max(0,Math.round(e*(t.length-1))))]:0)([...d].sort((t,e)=>t-e),.75),n),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,m=_&&_<=o.length*ma?o.filter((t,e)=>!u[e]):o;return{core:ka(wa(m)||r,i),all:ka(r,i),outliers:m===o?0:_}}function xa(t,e,i=.05){const s=$a(ya(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,Qr,Qr];return{x:n[0],y:n[1],w:n[2],h:n[3]}}function Sa(t){const e=xa(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function Ma(t){const e=[];for(const i of t.rooms||[]){const t=va(i);t&&e.push(t)}const i=$a(e,{pad:0}).core;return i?Math.max(Qr,Math.min(aa,Math.max(i.w,i.h))):Qr}function Ca(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*Ma(e)*o/n}const Da=[1,2,5,10,20,50,100,200,500,1e3];function Ta(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 Pa(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 Ra=["furniture","appliance","sanitary","other"],za=[{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]]}],Aa=new Map(za.map(t=>[t.id,t]));function Ia(t){return t&&Aa.get(t)||null}function Ea(t){return za.filter(e=>e.group===t)}function Na(t,e,i=ca,s=1e3){const n=Number(e)>0?Number(e):5;return(Number(t)||0)/n*i/s}const Fa=5e-4,Oa=ra;function La(t){return Number.isFinite(t)?Math.max(Fa,Math.min(Oa,t)):Fa}const Ha=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function qa(t,e,i){const s=Ia(t);if(!(s&&e>0&&i>0))return"";const n=t=>Ha(t*e),o=t=>Ha(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}H${n(e+s)}V${o(i+a)}H${n(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${n(e)} ${o(i)}L${n(s)} ${o(a)}`)}else if("e"===t[0]){const[,s,a,l,c]=t;r.push(`M${n(s-l)} ${o(a)}A${Ha(l*e)} ${Ha(c*i)} 0 0 1 ${n(s+l)} ${o(a)}A${Ha(l*e)} ${Ha(c*i)} 0 0 1 ${n(s-l)} ${o(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${n(e[0])} ${o(e[1])}`;for(let t=2;t+1{let e=(t%360+360)%360;return e>180&&(e-=360),e};function ja(t,e,i,s,n,o=0){let r=null,a=n;for(const n of s){const[s,l,c,h]=n,d=c-s,p=h-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let m=((t-s)*d+(e-l)*p)/u;m=Math.max(0,Math.min(1,m));let g=s+m*d,f=l+m*p;const v=Math.hypot(t-g,e-f);if(!(v0){const t=Math.round(m*_/o)*o;g=s+t/_*d,f=l+t/_*p}r={cx:g+y*(i/2),cy:f+b*(i/2),angle:Ua(180*Math.atan2(-y,b)/Math.PI),dist:v}}return r}function Wa(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function Ba(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function Ga(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function Va(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!Ga(t.a)||!Ga(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 Ka(t,e){const i=e[0],s=e[1],n=e[2]-i,o=e[3]-s,r=n*n+o*o;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*n+(t[1]-s)*o)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*n,s+a*o];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function Ya(t,e){return Ka(t,e).q}function Ja(t,e,i,s,n){const o=Ka(t,e).q;let r=o,a=1/0;for(const t of i){const e=Math.hypot(o[0]-t[0],o[1]-t[1]);e<=n&&e0?s:1,m=Math.round(u/_)*_,g=Math.max(0,Math.min(p,m))/p;return[l+h*g,c+d*g]}function Za(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt?.id),r=Va(e);if(r.length)return rl(r,t,i,s).map(t=>Ba(t,i));if(!n)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;tWa([t[0],t[1]],[t[2],t[3]],e))}function tl(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const n=(e||[]).filter(t=>t?.id);new Map(n.map(t=>[t.id,t]));const o=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=o.get(t),s=o.get(e);i&&s&&((i.open_to||[]).includes(e)||(i.open_to=[...i.open_to||[],e]),(s.open_to||[]).includes(t)||(s.open_to=[...s.open_to||[],t]))};for(let t=0;ths(i,t)<4*s)){r(n[t].id,n[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}function el(t,e,i,s=0){const n=e[2]-e[0],o=e[3]-e[1],r=Math.hypot(n,o);if(!(r>1e-9))return null;const a=n/r,l=o/r,c=(t[0]-e[0])*a+(t[1]-e[1])*l;if(c<-s||c>r+s)return null;return Math.abs((t[0]-e[0])*l-(t[1]-e[1])*a)>i?null:hs(t,e)}function il(t,e,i){const s=t[2]-t[0],n=t[3]-t[1],o=e[2]-e[0],r=e[3]-e[1],a=Math.hypot(s,n),l=Math.hypot(o,r);return a>1e-9&&l>1e-9&&(!(Math.abs(s/a*(r/l)-n/a*(o/l))>1e-6)&&(hs([e[0],e[1]],t)<=4*i||hs([t[0],t[1]],e)<=4*i))}function sl(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||!il(s.seg,o.seg,i))return"ambiguous"}return s}function nl(t,e,i,s,n=1,o=15){const r=function(t,e,i,s,n=1,o=15){const[r,a]=Yo([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]=Yo([e[0],e[1]],[e[2],e[3]]);if(Math.abs(r*o-a*i)>.05)continue;const d=nr(t,[e[0],e[1]],[e[2],e[3]],s,n);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-h[0],p[1]-h[1]);u0?l:o}(t,e,i,s,n,o);return lr(t,[e[0],e[1]],[e[2],e[3]],r,s,n)}function ol(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(!(hs(n,o)>Math.hypot(t[2]-t[0],t[3]-t[1])&&hs([o[0],o[1]],t)>i))for(const e of[[o[0],o[1]],[o[2],o[3]]])hs(e,t)<=2*i&&s.push(e);return s}function rl(t,e,i,s){const n=Va(t);if(!n.length)return[];const o=Za(e,s);if(!o.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of n){const e=Ba(t,i),n=e[0],l=e[1],c=e[2]-n,h=e[3]-l,d=Math.hypot(c,h);if(d4*s||o>4*s)continue;const r=(t[0]-n)*p+(t[1]-l)*u,c=(t[2]-n)*p+(t[3]-l)*u,h=Math.max(0,Math.min(r,c)),m=Math.min(d,Math.max(r,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+a?e.hi=Math.max(e.hi,t.hi):i.push({...t})}for(const e of i){const i=[n+p*e.lo,l+u*e.lo],s=[n+p*e.hi,l+u*e.hi];Math.hypot(s[0]-i[0],s[1]-i[1])i.pair===t&&Math.abs(i.ux*r-i.uy*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:r,ranges:[]},l.push(h));const d=(e[0]-h.origin[0])*h.ux+(e[1]-h.origin[1])*h.uy,p=(e[2]-h.origin[0])*h.ux+(e[3]-h.origin[1])*h.uy;h.ranges.push({lo:Math.min(d,p),hi:Math.max(d,p)})}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+a?t.hi=Math.max(t.hi,i.hi):e.push({...i})}for(const s of e){const e=[t.origin[0]+t.ux*s.lo,t.origin[1]+t.uy*s.lo],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])>=a&&h.push(Wa(e,n,i))}}return h}function al(t,e,i,s){const n=Va(t);if(!n.length)return[];if(e.length!==i.length)return n;const o=[];for(const t of n){const n=Ba(t,s),r=[n[0],n[1]],a=[n[2],n[3]];let l=r,c=a;for(let t=0;t{const e=Ka(t,[s[0],s[1],n[0],n[1]]);if(e.d>.001)return t;const i=h[0]-o[0],r=h[1]-o[1];return[o[0]+i*e.t,o[1]+r*e.t]};if(Ka([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],n[0],n[1]]).d<.01){l=d(r),c=d(a);break}}o.push(Wa(l,c,s))}return o}class ll{constructor(t,e=()=>Date.now()){this.onUpdate=t,this.now=e,this.signed={},this.queued=new Set,this.inFlight=new Map,this.retry=new Map,this.disposed=!1}start(t,e){this.disposed=!1,this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(t(),e()),288e5)}dispose(){this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.queued.clear(),this.inFlight.clear()}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(t,e){const i=ns(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],n=s?this.now()-s.at:1/0;return n{const e=[...this.queued];this.queued.clear(),this.sign(t,e)},30))}sign(t,e){if(e.length&&t?.callWS)for(const i of function(t,e){const i=Math.max(1,Math.floor(e)),s=[];for(let e=0;e{if(this.disposed)return;const e=this.now(),s={...this.signed};let n=0;for(const o of i){const i=t?.urls?.[o];"string"==typeof i&&i?(s[o]={url:i,at:e},this.retry.delete(o),n++):this.backOff(o)}n&&(this.signed=s,this.onUpdate())}).catch(()=>{for(const t of i)this.backOff(t)}).finally(()=>{for(const t of i)this.inFlight.get(t)===e&&this.inFlight.delete(t)})}}backOff(t){const e=this.retry.get(t)?.delay||0,i=Math.min(6e4,e?2*e:2e3);this.retry.set(t,{notBefore:this.now()+i,delay:i})}resign(t,e){const i=this.now(),s={};for(const[t,n]of Object.entries(this.signed))e.has(t)&&i-n.at{if(null==t||""===t)return null;const e=Number(t);return Number.isFinite(e)?e:null},pl=t=>{const e=t,i=dl(Array.isArray(e)?e[0]:e?.x),s=dl(Array.isArray(e)?e[1]:e?.y);return null==i||null==s?null:[i,s]};function ul(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=pl(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 _l(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=pl(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=ul(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 ml(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(pl).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),r=n.map(t=>s*t/o),a=r.map(Math.floor);let l=s-a.reduce((t,e)=>t+e,0);const c=r.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 vl(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=dl(e.x)&&null!=dl(e.y)?{x:dl(e.x),y:dl(e.y),a:dl(e.a??e.angle??e.theta)}:null,s=ml(t.path?.path??t.path?.points??t.path),n=[],o=t.rooms,r=Array.isArray(o)?o.map((t,e)=>[String(t?.id??e),t]):o&&"object"==typeof o?Object.entries(o):[];for(const[t,e]of r){if(!e||"object"!=typeof e)continue;const i=String(e.name??e.label??"").trim(),s=[[dl(e.cx),dl(e.cy)],[dl(e.center?.x),dl(e.center?.y)],[dl(e.x),dl(e.y)]].find(([t,e])=>null!=t&&null!=e);let o=s?.[0]??null,r=s?.[1]??null;const a=_l(e.outline),l=dl(e.x0),c=dl(e.y0),h=dl(e.x1),d=dl(e.y1),p=(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)||a?.bbox||null;if(null!=o&&null!=r||!a||([o,r]=a.centroid),null!=o&&null!=r||!p||(o=(p[0]+p[2])/2,r=(p[1]+p[3])/2),i&&null!=o&&null!=r){const e={id:t,name:i,cx:o,cy:r};p&&([e.x0,e.y0,e.x1,e.y1]=p),n.push(e)}}const a=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:a}:null}function yl(t,e,i){if(!t||!e)return null;const s=e.attributes||{},n=t.split(".")[0]||"",o=null!=i?.platform?String(i.platform):null,r=!!pl(s.vacuum_position??s.robot_position),a=!(!s.rooms||"object"!=typeof s.rooms),l=ml(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=a||l||c;let p=null;if(r?p="compatible":h?p="known_xcme_incomplete":d?p="partial":"camera"===n&&(p="camera"),!p)return null;const u=r?"camera"===n?300:200:h?100:d?50:0;return{entityId:t,name:String(s.friendly_name||t),platform:o,category:p,hasPosition:r,hasRooms:a,hasPath:l,hasMapId:c,score:u}}const bl=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function wl(t,e){const i=new Map(e.map(t=>[bl(t.name),t])),s=[],n=[];for(const e of t){const t=i.get(bl(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),n.push(e.name))}if(s.length<3)return null;const o=function(t){if(t.length<3)return null;let e=0,i=0,s=0,n=0,o=0,r=0,a=0,l=0,c=0,h=0,d=0,p=0;for(const[[u,_],[m,g]]of t){if(![u,_,m,g].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,n+=_*_,o+=_,r+=1,a+=u*m,l+=_*m,c+=m,h+=u*g,d+=_*g,p+=g}const u=[e,i,s,i,n,o,s,o,r],_=t=>{const[e,i,s,n,o,r,a,l,c]=u,h=e*(o*c-r*l)-i*(n*c-r*a)+s*(n*l-o*a);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(o*c-r*l)/h,(s*l-i*c)/h,(i*r-s*o)/h,(r*a-n*c)/h,(e*c-s*a)/h,(s*n-e*r)/h,(n*l-o*a)/h,(i*a-e*l)/h,(e*o-i*n)/h];return[d[0]*t[0]+d[1]*t[1]+d[2]*t[2],d[3]*t[0]+d[4]*t[1]+d[5]*t[2],d[6]*t[0]+d[7]*t[1]+d[8]*t[2]]},m=_([a,l,c]),g=_([h,d,p]);if(!m||!g)return null;const f=[m[0],m[1],m[2],g[0],g[1],g[2]];return f.every(Number.isFinite)?f:null}(s);return o?{matrix:o,matched:n,residual:hl(o,s)}:null}function kl(t,e,i){const s=t[t.length-1];if(s&&s[0]===e[0]&&s[1]===e[1])return t;if(t.push(e),t.length<=600)return t;let n=function(t,e){if(t.length<3)return t.slice();const i=new Uint8Array(t.length);i[0]=i[t.length-1]=1;const s=[[0,t.length-1]];for(;s.length;){const[n,o]=s.pop(),[r,a]=t[n],[l,c]=t[o],h=l-r,d=c-a,p=Math.hypot(h,d)||1e-9;let u=0,_=-1;for(let e=n+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([n,_],[_,o]))}const n=[];for(let e=0;e600&&(n=n.filter((t,e)=>e%2==0||e===n.length-1)),n}function $l(t){return"cleaning"===t||"returning"===t||"on"===t}const xl={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function Sl(t){const[e,i]=xl[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 Ml(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 Cl(t,e,i,s){const[n,o]=cl(Sl(e),i,s),r=Sl({...t,ox:0,oy:0}),[a,l]=cl(r,i,s);return{...t,ox:n-a,oy:o-l}}function Dl(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}class Tl extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return Bn(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:Gn(e,"editor.lang_auto")},{value:"en",label:Gn(e,"editor.lang_en")},{value:"ru",label:Gn(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:Gn(t,"editor.title"),default_floor:Gn(t,"editor.default_floor"),language:Gn(t,"editor.language"),icon_size:Gn(t,"editor.icon_size"),show_temperature:Gn(t,"editor.show_temperature"),live_states:Gn(t,"editor.live_states"),show_signal:Gn(t,"editor.show_signal"),kiosk:Gn(t,"editor.kiosk"),cycle:Gn(t,"editor.cycle")};return j`[t[0]-e[0],t[1]-e[1]],nn=(t,e)=>[t[0]+e[0],t[1]+e[1]],rn=(t,e)=>t[0]*e[0]+t[1]*e[1],an=t=>Math.hypot(t[0],t[1]);function ln(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function dn(t,e,i,s){const o=s||hn(t,e),n=(e+1)%t.length;return t.map((t,s)=>s===e||s===n?[t[0]+o[0]*i,t[1]+o[1]*i]:[...t])}function pn(t,e,i,s){const o=[],n=on(i,e),r=an(n);if(rs||h>s)continue;const d=rn(on(n,e),a),p=rn(on(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&o.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return o}function un(t,e,i,s,o){const n=pn(t,e,i,o);if(!n.length)return null;const r=t=>n.some(([e,i])=>cn(t,e,i)<=o),a=t.length,l=[];for(let n=0;no||f>o)continue;const v=rn(on(e,c),u),y=rn(on(i,c),u),b=Math.max(0,Math.min(v,y)),w=Math.min(p,Math.max(v,y));if(!(w-b<=o)){if(b>o&&bo&&wan(on(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const o=i[(s-1+i.length)%i.length],n=i[(s+1)%i.length],r=(t[0]-o[0])*(n[1]-o[1])-(t[1]-o[1])*(n[0]-o[0]),a=an(on(n,o))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function mn(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-o[0])*a[1]-(t[1]-o[1])*a[0],c=t=>(t[0]-o[0])*a[0]+(t[1]-o[1])*a[1],h=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const o=c(e);o<=h||o>=d||tMath.abs(r+(t-p)/g*(a-r));s=Math.min(s,f(_),f(m))}}return s}function fn(t,e,i){if(ei(t,e,i))return!0;if(ti(t,e,i)||ti(e,t,i))return!1;let s=0;try{const i=Re([[...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+=si(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function vn(t,e,i,s,o){const n=[i.n[0]*s,i.n[1]*s],r={polys:{},openings:{},movedSpans:{}};if(Math.abs(s)<1e-9)return r;for(const e of t){if(e.id===i.roomId){r.polys[e.id]=dn(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[nn(i.a,n),nn(i.b,n)]];continue}const t=pn(e.poly,i.a,i.b,o);if(!t.length)continue;const a=un(e.poly,i.a,i.b,n,o);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[nn(t,n),nn(e,n)]))}for(const t of e)cn([t.x,t.y],i.a,i.b)<=o&&(r.openings[t.id]=[t.x+n[0],t.y+n[1]]);return r}function yn(t,e,i){for(const s of e)for(let e=0;ei)continue;const c=(t.x-o[0])*l[0]+(t.y-o[1])*l[1];if(c-t.length/2>=-i&&c+t.length/2<=a+i)return!0}return!1}function bn(t,e,i){return t.filter(t=>e.some(e=>{for(let s=0;sa.polys[t.id]||t.poly;for(const e of l){const s=t.find(t=>t.id===e),o=a.polys[e];if(!mn(o))return!1;const l=ln(s.poly),h=ln(o);if(Math.abs(h)t.find(t=>t.id===e).poly),d=t.map(c);for(const t of bn(e,h,2*r)){const e=a.openings[t.id];if(!yn(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function kn(t,e,i,s,o,n){const r=t=>[s[0]+(t[0]-s[0])*o,s[1]+(t[1]-s[1])*o],a={poly:t.poly.map(r),openings:{}};for(const s of e){let e=!1;for(let i=0;i{for(let e=0;et.id===i);if(!l)return!1;const c=t.filter(t=>t.id!==i).map(t=>t.poly),h=kn(l,e,c,s,o,2*a),d=h.poly,p=function(t){const e=function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<3)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.pop(),o.pop(),s.concat(o)}(t);if(e.length<3)return 0;let i=1/0;for(let t=0;tt.id===i?d:t.poly);for(const t of bn(e,[l.poly],2*a)){const e=h.openings[t.id];if(!yn(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function xn(t,e,i){const s=i/e;return si(t)*s*s/1e4}function Sn(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const Mn=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],Cn=t=>Math.min(1,Math.max(0,t));function Dn(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=Mn[Mn.length-1][1];for(let t=1;t{const n=[t.x+o[0]*i*s,t.y+o[1]*i*s];return e.find(t=>t.poly.length>=3&&Be(n,t.poly))||null},r=n(1),a=n(-1);return r&&a?null:r||a?r?{normal:[-o[0],-o[1]],roomId:r.id}:{normal:o,roomId:a.id}:null}function Pn(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function Rn(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 zn(t,e){try{const i=Re([[...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 An(t,e,i,s,o,n,r){if(!(s>0))return[];const a=function(t,e){const i=function(t,e){return function(t){const e=t%360;return e<0?e+360:e}(t-e)}(t,e)*Math.PI/180;return[Math.sin(i),-Math.cos(i)]}(i,o),l=[-a[0],-a[1]],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=Tn(i,t);if(!e||!Pn(e.normal,a,s))continue;const o=t.find(t=>t.id===e.roomId);if(!o)continue;const d=n&&n[e.roomId]||o.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],m=Math.max(0,r?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(p)*u,y=Math.sin(p)*u,b=[g-v,f-y],w=[g+v,f+y],k=c*i.length,$=zn(Rn(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 In(t){return Math.round(10*(Number(t)||0))/10}const En=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Nn(t,e){const i=En(e?.north_deg);return null!==i?i:En(t?.north_deg)}function Fn(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function On(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Ln(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 Hn={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},qn=(t,e=1)=>{const i=Number(t);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):e},Un=t=>{let e=Number(t);return Number.isFinite(e)?(e=(e%360+360)%360,e>180?e-360:e):0};function jn(t,e,i){return Un(e)?[t.x,t.y]:i([t.x,t.y])}const Bn=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&s>0?s/o*i:0},Gn=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&i>0?s/i*o:0},Wn=(t,e,i,s=Hn.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return o;const n=Number(t?.width);return Number.isFinite(n)&&n>0?Gn(n,e,i):s};function Vn(t,e){return{color:t.color,opacity:qn(t.opacity),width_cm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),...e?{fill:t.fill,fill_color:t.fillColor,fill_opacity:qn(t.fillOpacity,.25)}:{}}}function Kn(t){const e=t.x+t.w/2,i=t.y+t.h/2,s=Un(t.angle)*Math.PI/180,o=Math.cos(s),n=Math.sin(s),r=(t,s)=>{const r=t-e,a=s-i;return[e+r*o-a*n,i+r*n+a*o]};return[r(t.x,t.y),r(t.x+t.w,t.y),r(t.x+t.w,t.y+t.h),r(t.x,t.y+t.h)]}function Yn(t){const e=Kn(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 Jn(t,e,i,s,o,n,r,a){const l=Un(t.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l),d=-Math.sin(l),p=Math.cos(l),u=t.x+t.w/2,_=t.y+t.h/2,m=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,f=u+m*c+g*d,v=_+m*h+g*p,y=s-f,b=o-v;let w=(y*c+b*h)*(e>0?1:-1),k=(y*d+b*p)*(i>0?1:-1),$=null;if(n){const e=w/Math.max(t.w,a),i=k/Math.max(t.h,a);$=Math.max(a/Math.max(t.w,a),a/Math.max(t.h,a),e,i),w=t.w*$,k=t.h*$}if(r>0)if(n){const e=Math.max(1,Math.round(t.w/r)),i=Math.max(1,Math.round(t.h/r));if(Math.abs(t.w-e*r)<1e-6&&Math.abs(t.h-i*r)<1e-6){const s=(t,e)=>{let i=Math.abs(t),s=Math.abs(e);for(;s;)[i,s]=[s,i%s];return Math.max(1,i)},o=1/s(e,i),n=Math.max(a/t.w,a/t.h),r=Math.max(n,Math.round(($??1)/o)*o);w=t.w*r,k=t.h*r}else{t.w>=t.h?(w=Math.round(w/r)*r,k=w*(t.h/Math.max(t.w,a))):(k=Math.round(k/r)*r,w=k*(t.w/Math.max(t.h,a)))}}else w=Math.round(w/r)*r,k=Math.round(k/r)*r;w=Math.max(a,w),k=Math.max(a,k);return{x:f+(e>0?w/2:-w/2)*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)*p-k/2,w:w,h:k,angle:Un(t.angle)||void 0}}const Zn=(t,e,i)=>{const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;if(n<1e-12)return[...e];const r=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*o)/n));return[e[0]+s*r,e[1]+o*r]};const Xn=15;function Qn(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function tr(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function er(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function ir(t,e,i){const s=Number(e)>0?Number(e):5;return tr(t)/s*i}function sr(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function or(t,e){let i=e[0]-t[0],s=e[1]-t[1];const o=Math.hypot(i,s);return o<1e-12?[1,0]:(i/=o,s/=o,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function nr(t,e,i){const s=sr((t[0]+e[0])/2,i),o=sr((t[1]+e[1])/2,i),[n,r]=or(t,e);let a=Math.atan2(r,n);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,c=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(c)},${o.toFixed(c)}@${l.toFixed(4)}`}function rr(t,e,i,s){return 1===s?nr(t,e,i):nr([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function ar(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 lr(t,e,i,s,o){const n=o>0?o:1;return{key:rr(t,e,s,n),cm:tr(i),a:[t[0]/n,t[1]/n],b:[e[0]/n,e[1]/n]}}function cr(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[o,n]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[o,n,r].every(Number.isFinite)&&s.push({w:e,x:o*i,y:n*i,ang:r})}return s}function hr(t,e){const[i,s]=or(t,e);let o=Math.atan2(s,i);return o<0&&(o+=Math.PI),o}function dr(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function pr(t,e,i,s,o=1){if(!t?.length)return null;const n=rr(e,i,s,o),r=t.find(t=>t.key===n);if(r)return r;const a=o>0?o:1,l=(e[0]+i[0])/2,c=(e[1]+i[1])/2,h=hr(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of cr(t,a))if(dr(e.ang,h)&&Math.hypot(e.x-l,e.y-c)<=d)return e.w;return null}function ur(t,e,i,s,o=1){const n=pr(t,e,i,s,o);return n&&n.cm>0?tr(n.cm):0}function _r(t,e,i,s=1,o=[]){if(!t?.length)return[];const n=new Set,r=Le(e);for(const t of r)n.add(rr([t[0],t[1]],[t[2],t[3]],i,s));const a=e||[],l=Math.max(i*s*.02,1e-9);for(let t=0;t(n.has(t.key)||(t=>{const e=ar(t,s);if(!e)return!1;const[i,n]=e,a=n[0]-i[0],c=n[1]-i[1],h=Math.hypot(a,c);if(h<=l)return!1;if(!r.some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];return dr(hr(i,n),hr(e,s))&&Ir(i[0],i[1],e[0],e[1],s[0],s[1])<=l&&Ir(n[0],n[1],e[0],e[1],s[0],s[1])<=l}))return!1;const d=(o||[]).some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];if(!dr(hr(i,n),hr(e,s)))return!1;const o=t=>Math.abs((t[0]-i[0])*c-(t[1]-i[1])*a)/h;if(o(e)>l||o(s)>l)return!1;const r=h*h,d=((e[0]-i[0])*a+(e[1]-i[1])*c)/r,p=((s[0]-i[0])*a+(s[1]-i[1])*c)/r;return Math.min(1,Math.max(d,p))-Math.max(0,Math.min(d,p))>l/h});return!d})(t))&&t.cm>=1&&t.cm<=100)}function mr(t,e,i,s=8){const[o,n]=or(t,e);let r=Math.atan2(n,o);r<0&&(r+=Math.PI);let a=i*Math.PI/180%Math.PI;a<0&&(a+=Math.PI);let l=Math.abs(r-a);return l>Math.PI/2&&(l=Math.PI-l),l<=s*Math.PI/180}function gr(t,e,i,s,o=1){if(!t?.length)return[];if(e.length!==i.length)return t.slice();const n=new Map;for(let t=0;t0?o:1,a=Math.max(.5*s,1e-9)*r,l=new Set,c=[];for(const o of t){let t="",h=null;const d=ar(o,r);if(d)for(let o=0;oa||Ir(d[1][0],d[1][1],n[0],n[1],l[0],l[1])>a)continue;const u=l[0]-n[0],_=l[1]-n[1],m=u*u+_*_;if(m<1e-18)continue;const g=t=>{const e=Math.max(0,Math.min(1,((t[0]-n[0])*u+(t[1]-n[1])*_)/m));return[c[0]+(p[0]-c[0])*e,c[1]+(p[1]-c[1])*e]};h=[g(d[0]),g(d[1])],t=rr(h[0],h[1],s,r);break}if(d||(t=n.get(o.key)||""),!t){const n=cr([o],r)[0];if(n)for(let o=0;o1.000001)continue;if(Ir(n.x,n.y,l[0],l[1],c[0],c[1])>a)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]=or(h,d),b=Math.max(s*r,1e-6);t=rr([g-v*b,f-y*b],[g+v*b,f+y*b],s,r);break}}t||(t=o.key),l.has(t)||(l.add(t),c.push(h?lr(h[0],h[1],o.cm,s,r):{...o,key:t,cm:tr(o.cm)}))}return c}function fr(t,e,i,s,o,n=1){const r=rr(e,i,o,n),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,lr(e,i,s,o,n)]}function vr(t,e,i,s,o,n=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,o=1,n=[]){const r=Dr(t,e,i,s,o,n);if(!r)return[];const a=[];for(let t=0;t0)||!t||t.length<2)return"";if(i&&t.length>=3){let i=t;const s=t[t.length-1];if(t.length>=4&&Math.hypot(t[0][0]-s[0],t[0][1]-s[1])<1e-9&&(i=t.slice(0,-1)),i.length>=3){const t=i.map(()=>e),s=Or(i,t),o=Sr(i,t);if(s&&o)return`${Mr(s)} ${Mr(Cr(o))}`}}let s="";for(let i=0;i.05)&&Ir(r,a,t[0],t[1],t[2],t[3])<=n)return!0}return!1}function wr(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function kr(t,e){const i=t[e],s=t[(e+1)%t.length],o=s[0]-i[0],n=s[1]-i[1],r=Math.hypot(o,n)||1;let a=-n/r,l=o/r;const c=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r+0)+n&&(i=!i)}return i}([c[0]+.001*a,c[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function $r(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 xr(t,e,i,s){const o=e[0]*s[1]-e[1]*s[0];if(Math.abs(o)<1e-12)return null;const n=[i[0]-t[0],i[1]-t[1]],r=(n[0]*s[1]-n[1]*s[0])/o;return[t[0]+r*e[0],t[1]+r*e[1]]}function Sr(t,e){const i=t?.length||0;if(i<3||e.length!==i)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);const s=[];for(let o=0;o0||d>0)){s.push([t[o][0],t[o][1]]);continue}if($r(b,w)){const e=t[o],i=[e[0]+p*h,e[1]+u*h],n=[e[0]+_*d,e[1]+m*d];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=xr(k,b,$,w),S=Math.max(h,d,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}h>0&&s.push([t[o][0]+p*h,t[o][1]+u*h]),d>0&&s.push([t[o][0]+_*d,t[o][1]+m*d]),h>0||d>0||s.push([t[o][0],t[o][1]])}return s.length>=3?s:null}function Mr(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),a=Fe(r);if(!a||a.length<3)return null;const l=wr(s,o),c=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Fe(i);if(t)for(const e of ss(a,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 n||[]){const e=ar(t,o);e&&c.push(e[0],e[1])}const h=[],d=[];for(let t=0;tl)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=o||r>=1-o||(n.some(t=>Math.abs(t-r)*s<=2*l)||n.push(r))}n.sort((t,e)=>t-e);for(const s of n)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:a}}function Tr(t,e,i,s){const o=e.poly.length,n=new Array(o).fill(0);if(!t?.length)return n;const r=new Set,a=[];for(let l=0;l0?(n[l]=tr(c.cm),r.add(c.key)):a.push(l)}if(!a.length)return n;const l=s>0?s:1,c=Math.max(.5*i,1e-9)*l,h=cr(t,l).filter(t=>t.w.cm>0),d=new Map;for(const t of a){const i=e.parent[t],s=d.get(i);s?s.push(t):d.set(i,[t])}for(const[t,i]of d){const s=e.orig[t],o=e.orig[(t+1)%e.orig.length],a=hr(s,o),d=(s[0]+o[0])/2,p=(s[1]+o[1])/2;let u=null;const _=Math.hypot(o[0]-s[0],o[1]-s[1]);for(const t of h){if(r.has(t.w.key))continue;if(!dr(t.ang,a))continue;const e=ar(t.w,l);let i=!1,n=0;if(e){if(!dr(hr(e[0],e[1]),a))continue;if(Ir(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>c||Ir(o[0],o[1],e[0][0],e[0][1],e[1][0],e[1][1])>c)continue;i=!0,n=Math.max(0,Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1])-_)}else{if(Ir(t.x,t.y,s[0],s[1],o[0],o[1])>c)continue;n=Math.hypot(t.x-d,t.y-p)}(!u||i&&!u.exact||i===u.exact&&nt?.id===e),o=Fe(s);if(!o)return[];const n=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Fe(s);if(t)for(const e of ss(o,t,i))n.push(e)}return n}(t,e,wr(o,a)),h=function(t,e,i,s,o){const n=wr(s,o),r=[];for(let a=0;aIr(h,d,t[0],t[1],t[2],t[3])<=n);r.push(p?"shared":"outer")}return r}(l.poly,c,s,o,a),d=Tr(i,l,o,a),p=d.map((t,e)=>h[e]&&t>0?ir(t,n,r)/2:0);return{...l,kinds:h,cms:d,offsets:p}}function Rr(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Pr(t,l.id,e,i,s,o,n,r);if(c)for(let t=0;t0&&!l.has(c.key)&&(l.add(c.key),a.push(c));const c=[];for(const a of t||[]){if(!a?.id)continue;const l=Pr(t,a.id,e,i,s,o,n,r);if(l)for(let t=0;t0)||null===l.kinds[i]){t++;continue}let n=t;for(;n+10&&c.push({a:[h[0],h[1]],b:[d[0],d[1]],key:rr(h,d,s,r),cm:o,len:p}),t=n+1}}}c.sort((t,e)=>e.len-t.len||t.key.localeCompare(e.key));const h=[],d=new Set,p=new Set,u=4*wr(s,r);for(const t of c){const e=a.filter(e=>!p.has(e.key)&&e.cm===t.cm&&dr(hr(e.a,e.b),hr(t.a,t.b))&&Ir(e.a[0],e.a[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u&&Ir(e.b[0],e.b[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u);if(e.length){for(const t of e)p.add(t.key);d.has(t.key)||(d.add(t.key),h.push(lr(t.a,t.b,t.cm,s,r)))}}for(const t of a)p.has(t.key)||d.has(t.key)||(d.add(t.key),h.push(lr(t.a,t.b,t.cm,s,r)));return h}function Ar(t,e,i,s,o,n,r,a=1){const l=wr(o,a),c=(s[0]+s[2])/2,h=(s[1]+s[3])/2,d=hr([s[0],s[1]],[s[2],s[3]]);let p=null;for(const s of Rr(t,e,i,o,n,r,a)){if(!dr(hr(s.a,s.b),d))continue;const t=Ir(c,h,s.a[0],s.a[1],s.b[0],s.b[1]);t>4*l||(!p||tt?.id===e),c=Fe(l);if(!c||c.length<3)return null;if(!i?.length)return c.map(t=>[t[0],t[1]]);const h=Pr(t,e,i,s,o,n,r,a);return h&&h.offsets.some(t=>t>0)&&Sr(h.poly,h.offsets)||c.map(t=>[t[0],t[1]])}function Nr(t){const e=t.map(t=>[t[0],t[1]]);return e.push([t[0][0],t[0][1]]),[e]}function Fr(t,e,i,s=[],o,n,r,a=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=Pr(t,s.id,e,i,o,n,r,a);if(!l||l.poly.length<3||!l.offsets.some(t=>t>0))continue;for(const t of l.offsets)t>0&&(h=Math.max(h,2*t));const d=Or(l.poly,l.offsets),p=Sr(l.poly,l.offsets);d&&c.push({outset:d,inset:p})}for(const t of l){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);if(e.length){const s=Math.min(Math.max(...e)-Math.min(...e),Math.max(...i)-Math.min(...i)),o=Math.min(...t.map((e,i)=>{const s=t[(i+1)%t.length];return Math.hypot(s[0]-e[0],s[1]-e[1])}));h=Math.max(h,t.length>16?s:o)}}const d=function(t,e,i,s,o,n,r){if(!e?.length||!i?.length)return[];const a=4*wr(s,r),l=new Map;for(const a of Rr(t,e,i,s,o,n,r))!a.open&&a.half>0&&!l.has(a.key)&&l.set(a.key,a);const c=[...l.values()];if(c.length<2)return[];const h=[];for(const t of i)for(const e of[[t[0],t[1]],[t[2],t[3]]])h.some(t=>Math.hypot(t[0]-e[0],t[1]-e[1])<=a)||h.push(e);const d=[],p=(t,e)=>{let i=0,s=0;if(Math.hypot(t.a[0]-e[0],t.a[1]-e[1])<=a)i=t.b[0]-t.a[0],s=t.b[1]-t.a[1];else{if(!(Math.hypot(t.b[0]-e[0],t.b[1]-e[1])<=a))return null;i=t.a[0]-t.b[0],s=t.a[1]-t.b[1]}const o=Math.hypot(i,s);return o>a?[i/o,s/o]:null};for(const t of h){const e=c.map(e=>({iv:e,u:p(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(r>0?[t.slice(),h,u,p]:[t.slice(),p,u,h])}}return d}(t,e,i,o,n,r,a),p=s.length?Hr(t,e,i,o,n,r,a):null;try{const t=t=>{const e=Nr(t.outset);return t.inset?ze(e,Nr(t.inset)):e};let e=c.length?t(c[0]):null;for(let i=1;i0))continue;const i=jr(p,t,!0);if(!i.negative&&!i.positive)continue;const s=t.angle*Math.PI/180,n=Math.cos(s),r=Math.sin(s),l=-r,c=n,d=t.length/2,u=1.25*Math.max(h,o*a),_=[[t.x-n*d-l*u,t.y-r*d-c*u],[t.x+n*d-l*u,t.y+r*d-c*u],[t.x+n*d+l*u,t.y+r*d+c*u],[t.x-n*d+l*u,t.y-r*d+c*u]];e&&(e=ze(e,Nr(_)))}for(const t of l)t.length<3||(e=e?Pe(e,Nr(t)):[Nr(t)]);const i=function(t){if(!t)return"";let e="";for(const i of t)if(Array.isArray(i))for(const t of i){if(!Array.isArray(t)||t.length<4)continue;const i=t.slice(0,t.length-1);i.length<3||(e+=(e?" ":"")+Mr(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:h,fillRule:"evenodd"}:null}catch{const s=function(t,e,i,s,o,n,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Pr(t,l.id,e,i,s,o,n,r);if(!c||c.poly.length<3||!c.offsets.some(t=>t>0))continue;const h=Or(c.poly,c.offsets),d=Sr(c.poly,c.offsets);if(!h||!d)continue;const p=`${Mr(h)} ${Mr(Cr(d))}`;let u="",_="outer",m=0,g=0;for(let t=0;t0){u=rr(c.poly[t],c.poly[(t+1)%c.poly.length],s,r),_=c.kinds[t]||"outer",m=c.cms[t],g=ir(m,o,n);break}a.push({d:p,key:u,kind:_,cm:m,depthUnits:g})}return a}(t,e,i,o,n,r,a),c=l.map(t=>Mr(t)).join(" ");return s.length||c?{d:[s.map(t=>t.d).join(" "),c].filter(Boolean).join(" "),depthUnits:h,fillRule:"nonzero"}:null}}function Or(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]]);Cr(t),e.slice().reverse();const s=[];for(let o=0;o0||a>0)){s.push([t[o][0],t[o][1]]);continue}if($r(b,w)){const e=t[o],i=[e[0]-l*r,e[1]-c*r],n=[e[0]-h*a,e[1]-d*a];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=xr(k,b,$,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[o][0]-l*r,t[o][1]-c*r]),a>0&&s.push([t[o][0]-h*a,t[o][1]-d*a])}return s.length>=3?s:null}function Lr(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){const c=Fe(l);if(c&&c.length>=3){const h=Pr(t,l.id,e,i,s,o,n,r),d=h&&h.offsets.some(t=>t>0)?Or(h.poly,h.offsets):null,p=d||c;a.push({poly:p.map(t=>t.join(",")).join(" ")})}else l&&null!=l.x&&null!=l.y&&null!=l.w&&null!=l.h&&a.push({rect:{x:l.x,y:l.y,w:l.w,h:l.h,rx:.03*Math.min(l.w,l.h)}})}return a}function Hr(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const c=Pr(t,l.id,e,i,s,o,n,r);if(!c)continue;const h=Math.abs(si(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(!o.length)return{coverage:0,full:!1};let n=o[0][0],r=o[0][1],a=0,l=n<=e+s;for(let t=1;t=i-s,{coverage:a,full:l}}function Ur(t,e){return Number(e.full)-Number(t.full)||t.faceDistance-e.faceDistance||t.area-e.area||t.roomId.localeCompare(e.roomId)}function jr(t,e,i=!1){const s=Number(e?.x),o=Number(e?.y),n=Number(e?.angle),r=Number(e?.length);if(!([s,o,n,r].every(Number.isFinite)&&r>0))return{negative:null,positive:null};const a=n*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=-c,d=l,p=r/2,u=Math.max(1e-9,t.adjacencyEps),_=new Map;let m=0;for(const e of t.edges){if(i&&!(e.half>0))continue;if(!mr(e.a,e.b,n))continue;const[t,r]=or(e.a,e.b);if(Math.abs((s-e.a[0])*r-(o-e.a[1])*t)>u)continue;const a=(e.a[0]-s)*l+(e.a[1]-o)*c,g=(e.b[0]-s)*l+(e.b[1]-o)*c,f=Math.max(-p,Math.min(a,g)),v=Math.min(p,Math.max(a,g));if(v-f<=u)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-o)*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,r]},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=qr(t.pieces,-p,p,u);t.coverage=e.coverage,t.full=e.full}const g=t=>{const e=[..._.values()].filter(e=>e.side===t&&e.coverage>u);return e.sort(Ur),e[0]||null};return{negative:g(-1),positive:g(1)}}function Br(t,e){const i=jr(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 o=s[0],n=e.flip_v?-o.side:o.side,r=(-1===n?i.negative:i.positive)||o,a=[...r.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(!(a.half>0&&a.cm>0))return{ox:0,oy:0,cm:0,side:n};const l=e.angle*Math.PI/180,c=-Math.sin(l),h=Math.cos(l);return{ox:c*n*a.half,oy:h*n*a.half,cm:a.cm,side:n}}function Gr(t,e){const i=1e-9,s=[];for(const t of[...e].filter(t=>t.x1>t.x0&&t.half>0).sort((t,e)=>t.x0-e.x0||t.x1-e.x1||t.half-e.half)){const e=s[s.length-1];e&&t.x0<=e.x1+i&&Math.abs(t.half-e.half)<=i?e.x1=Math.max(e.x1,t.x1):s.push({...t})}const o=[];for(let e=0;e=o-l?i.push([s,o]):(t>s+l&&i.push([s,Math.min(o,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 a}function Vr(t,e){const i=new Map;return e.map(e=>function(t,e,i){const s=jr(t,e,!0),o=s.negative,n=s.positive;if(!o&&!n)return null;let r;if(o&&n)r=[{candidate:o,side:-1},{candidate:n,side:1}];else{const t=o||n;r=[{candidate:t,side:-1},{candidate:t,side:1}]}const a=r.map(({candidate:t,side:s})=>({candidate:t,side:s,pieces:Wr(e,s,t.pieces,i)})),l=a.map(({candidate:t,side:e,pieces:i})=>({side:e,roomId:t.roomId,d:Gr(e,i)})),c=a.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 Kr=150;function Yr(t){return Number.isFinite(t)?Math.max(1,Math.min(Kr,t)):1}function Jr(t){return((Number.isFinite(Number(t))?Number(t):0)%90+90)%90}const Zr=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 Xr(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 Qr(t,e,i,s,o){const n=e[0]-t[0],r=e[1]-t[1],a=Math.hypot(n,r);if(!(a>1e-9))return null;const l=ir(i,s,o)/2,c=-r/a*l,h=n/a*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 ta(t,e,i){const s=Number(e)>0?Number(e):5,o=Yr(t.cm)/s*i,n=t.center[0],r=t.center[1];if("circle"===t.shape){const t=o/2;return Array.from({length:96},(e,i)=>{const s=i/96*Math.PI*2;return[n+Math.cos(s)*t,r+Math.sin(s)*t]})}const a=o/2,l=Jr(t.angle)*Math.PI/180,c=Math.cos(l),h=Math.sin(l);return[[-a,-a],[a,-a],[a,a],[-a,a]].map(([t,e])=>[n+t*c-e*h,r+t*h+e*c])}function ea(t,e,i){const s=[];for(let o=0;o+1t.length>=3).map(t=>Zr(t));return e.length?Pe(e[0],...e.slice(1)):null}catch{return null}}(e);if(i)return ze(Zr(t),i)}catch{}let i=[Zr(t)];for(const t of e)if(!(t.length<3))try{i=ze(i,Zr(t))}catch{}return i}function sa(t){let e=0;for(const i of t||[])if(i?.length){e+=si(i[0]||[]);for(let t=1;t=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]))}return e}function na(t,e,i){if(!(i>0))return[];const s=Math.max(1e-9,1e-9*i);if(t.some(t=>ra(e,t)||(t=>t.some((i,o)=>{const n=t[(o+1)%t.length],r=n[0]-i[0],a=n[1]-i[1],l=r*r+a*a;if(!(l>0))return Math.hypot(e[0]-i[0],e[1]-i[1])<=s;const c=Math.max(0,Math.min(1,((e[0]-i[0])*r+(e[1]-i[1])*a)/l));return Math.hypot(e[0]-(i[0]+c*r),e[1]-(i[1]+c*a))<=s}))(t)))return[Array.from({length:128},(t,s)=>{const o=s/128*Math.PI*2;return[e[0]+Math.cos(o)*i*1.01,e[1]+Math.sin(o)*i*1.01]})];const o=[];for(const n of t)if(!(n.length<2)){o.push(n);for(let t=0;ts&&u>s))continue;const _=Math.max(-1,Math.min(1,(l*h+c*d)/(p*u))),m=Math.sqrt(Math.max(0,(1+_)/2)),g=Math.max(2*i,1.02*i/Math.max(m,.001)),f=[e[0]+l/p*g,e[1]+c/p*g],v=[e[0]+h/u*g,e[1]+d/u*g];o.push([r,a,v,f])}}return o}function ra(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r||1e-12)+n&&(i=!i)}return i}function aa(t,e,i){if(Math.hypot(t.center[0]-e.center[0],t.center[1]-e.center[1])>i)return!1;if(Math.abs(Yr(t.cm)-Yr(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(Jr(t.angle)-Jr(e.angle));return Math.min(s,90-s)<=1e-6}const la=1e3;function ca(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,o=s>=1?e:e*s,n=s>=1?e/s:e;return{x:(e-o)/2,y:(e-n)/2,w:o,h:n}}const ha=.01,da=100;function pa(t,e=1e3){const i=ca(t?.plan_aspect,e),s=Number(t?.plan_scale),o=Number.isFinite(s)&&s>0?Math.min(da,Math.max(ha,s)):1,n=Number(t?.plan_scale_x),r=Number(t?.plan_scale_y),a=Number.isFinite(n)&&n>0?Math.min(da,Math.max(ha,n)):o,l=Number.isFinite(r)&&r>0?Math.min(da,Math.max(ha,r)):o,c=Number(t?.plan_x),h=Number(t?.plan_y),d=Un(t?.plan_angle);return{x:i.x+(Number.isFinite(c)?$a(c):0)*e,y:i.y+(Number.isFinite(h)?$a(h):0)*e,w:i.w*a,h:i.h*l,...d?{angle:d}:{}}}function ua(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 _a(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=la,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]*la,i[1]*e,i[2]*la,i[3]*e],bg:t.plan_url?{href:hs(t.plan_url),...pa(t,la)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...ua(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*la:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*la:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*la,t[1]*e]):void 0}}),room_drafts:(t.room_drafts||[]).map(t=>({id:t.id,points:(t.points||[]).map(t=>[t[0]*la,t[1]*e]),segments:(t.segments||[]).map(t=>({cm:Number(t.cm)}))})),partitions:(t.partitions||[]).map(t=>({id:t.id,a:[t.a[0]*la,t.a[1]*e],b:[t.b[0]*la,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]*la,t.center[1]*e],cm:Number(t.cm),..."circle"===t.shape?{}:{angle:Jr(t.angle)}}))}}):[]}const ma=5e3,ga=ma*la,fa=240,va=la/fa,ya=1/fa;function ba(t){if(!Number.isFinite(t))return t;const e=Math.round(t*fa/la)*la/fa;return Math.abs(e-t)<=1e-9*va?t:e}function wa(t){return{x:ba(t.x),y:ba(t.y)}}function ka(t){return Number.isFinite(t)?Math.min(ga,Math.max(-ga,t)):0}function $a(t){return Number.isFinite(t)?Math.min(ma,Math.max(-ma,t)):0}const xa=1/3,Sa=200;function Ma(t){let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t){const t=Number(n[0]),r=Number(n[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>o&&(o=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:o}}function Ca(t){return t.poly&&t.poly.length?Ma(t.poly):null==t.x||null==t.y?null:Ma([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function Da(t,e){const i=[];for(const e of t.rooms||[]){const t=Ca(e);t&&i.push(t)}if(t.bg){const e=Ma(Kn(t.bg));e&&i.push(e)}for(const t of e||[])if(Array.isArray(t)){const e=Ma([t]);e&&i.push(e)}else i.push(t);return i}const Ta=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},Pa=t=>{let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t)n.minXs&&(s=n.maxX),n.maxY>o&&(o=n.maxY);return e>s||i>o?null:{x:e,y:i,w:s-e,h:o-i}};function Ra(t,e){let{x:i,y:s,w:o,h:n}=t;o<30&&(i=i+o/2-100,o=Sa),n<30&&(s=s+n/2-100,n=Sa);const r=Math.max(o,n)*e;return{x:i-r,y:s-r,w:o+2*r,h:n+2*r}}function za(t,e={}){const i=e.pad??.05,s=e.k??10,o=e.minSpread??50,n=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=ga&&Math.abs(t.maxX)<=ga&&Math.abs(t.minY)<=ga&&Math.abs(t.maxY)<=ga);if(!n.length)return{core:null,all:null,outliers:0};const r=Pa(n);if(n.length<4){const t=Ra(r,i);return{core:t,all:t,outliers:0}}const a=n.map(t=>(t.minX+t.maxX)/2),l=n.map(t=>(t.minY+t.maxY)/2),c=Ta(a),h=Ta(l),d=n.map((t,e)=>Math.max(Math.abs(a[e]-c),Math.abs(l[e]-h))),p=Math.max(((t,e)=>t.length?t[Math.min(t.length-1,Math.max(0,Math.round(e*(t.length-1))))]:0)([...d].sort((t,e)=>t-e),.75),o),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,m=_&&_<=n.length*xa?n.filter((t,e)=>!u[e]):n;return{core:Ra(Pa(m)||r,i),all:Ra(r,i),outliers:m===n?0:_}}function Aa(t,e,i=.05){const s=za(Da(t,e),{pad:i});if(s.core)return s.core;const o=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,la,la];return{x:o[0],y:o[1],w:o[2],h:o[3]}}function Ia(t){const e=Aa(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function Ea(t){const e=[];for(const i of t.rooms||[]){const t=Ca(i);t&&e.push(t)}const i=za(e,{pad:0}).core;return i?Math.max(la,Math.min(ga,Math.max(i.w,i.h))):la}function Na(t,e,i,s=1){const o=Number(i),n=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(o)||o<=0?t*n:t*Ea(e)*n/o}const Fa=[1,2,5,10,20,50,100,200,500,1e3];function Oa(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function La(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 Ha=["furniture","appliance","sanitary","other"],qa=[{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]]}],Ua=new Map(qa.map(t=>[t.id,t]));function ja(t){return t&&Ua.get(t)||null}function Ba(t){return qa.filter(e=>e.group===t)}function Ga(t,e,i=va,s=1e3){const o=Number(e)>0?Number(e):5;return(Number(t)||0)/o*i/s}const Wa=5e-4,Va=ma;function Ka(t){return Number.isFinite(t)?Math.max(Wa,Math.min(Va,t)):Wa}const Ya=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function Ja(t,e,i){const s=ja(t);if(!(s&&e>0&&i>0))return"";const o=t=>Ya(t*e),n=t=>Ya(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}H${o(e+s)}V${n(i+a)}H${o(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}L${o(s)} ${n(a)}`)}else if("e"===t[0]){const[,s,a,l,c]=t;r.push(`M${o(s-l)} ${n(a)}A${Ya(l*e)} ${Ya(c*i)} 0 0 1 ${o(s+l)} ${n(a)}A${Ya(l*e)} ${Ya(c*i)} 0 0 1 ${o(s-l)} ${n(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${o(e[0])} ${n(e[1])}`;for(let t=2;t+1{let e=(t%360+360)%360;return e>180&&(e-=360),e};function Xa(t,e,i,s,o,n=0){let r=null,a=o;for(const o of s){const[s,l,c,h]=o,d=c-s,p=h-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let m=((t-s)*d+(e-l)*p)/u;m=Math.max(0,Math.min(1,m));let g=s+m*d,f=l+m*p;const v=Math.hypot(t-g,e-f);if(!(v0){const t=Math.round(m*_/n)*n;g=s+t/_*d,f=l+t/_*p}r={cx:g+y*(i/2),cy:f+b*(i/2),angle:Za(180*Math.atan2(-y,b)/Math.PI),dist:v}}return r}function Qa(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function tl(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function el(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function il(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!el(t.a)||!el(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],o=[Number(t.b[0]),Number(t.b[1])];Math.hypot(o[0]-s[0],o[1]-s[1])<.001||e.push({a:s,b:o})}return e}function sl(t,e){const i=e[0],s=e[1],o=e[2]-i,n=e[3]-s,r=o*o+n*n;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*o+(t[1]-s)*n)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*o,s+a*n];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function ol(t,e){return sl(t,e).q}function nl(t,e,i,s,o){const n=sl(t,e).q;let r=n,a=1/0;for(const t of i){const e=Math.hypot(n[0]-t[0],n[1]-t[1]);e<=o&&e0?s:1,m=Math.round(u/_)*_,g=Math.max(0,Math.min(p,m))/p;return[l+h*g,c+d*g]}function rl(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt?.id),r=il(e);if(r.length)return ml(r,t,i,s).map(t=>tl(t,i));if(!o)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;tQa([t[0],t[1]],[t[2],t[3]],e))}function cl(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const o=(e||[]).filter(t=>t?.id);new Map(o.map(t=>[t.id,t]));const n=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=n.get(t),s=n.get(e);i&&s&&((i.open_to||[]).includes(e)||(i.open_to=[...i.open_to||[],e]),(s.open_to||[]).includes(t)||(s.open_to=[...s.open_to||[],t]))};for(let t=0;tgs(i,t)<4*s)){r(o[t].id,o[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}function hl(t,e,i,s=0){const o=e[2]-e[0],n=e[3]-e[1],r=Math.hypot(o,n);if(!(r>1e-9))return null;const a=o/r,l=n/r,c=(t[0]-e[0])*a+(t[1]-e[1])*l;if(c<-s||c>r+s)return null;return Math.abs((t[0]-e[0])*l-(t[1]-e[1])*a)>i?null:gs(t,e)}function dl(t,e,i){const s=t[2]-t[0],o=t[3]-t[1],n=e[2]-e[0],r=e[3]-e[1],a=Math.hypot(s,o),l=Math.hypot(n,r);return a>1e-9&&l>1e-9&&(!(Math.abs(s/a*(r/l)-o/a*(n/l))>1e-6)&&(gs([e[0],e[1]],t)<=4*i||gs([t[0],t[1]],e)<=4*i))}function pl(t,e,i){if(!t.length)return null;t.sort((t,e)=>t.distance-e.distance);const s=t[0];for(let o=1;oe)break;if(n.semantic!==s.semantic||!dl(s.seg,n.seg,i))return"ambiguous"}return s}function ul(t,e,i,s,o=1,n=15){const r=function(t,e,i,s,o=1,n=15){const[r,a]=or([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,n]=or([e[0],e[1]],[e[2],e[3]]);if(Math.abs(r*n-a*i)>.05)continue;const d=ur(t,[e[0],e[1]],[e[2],e[3]],s,o);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-h[0],p[1]-h[1]);u0?l:n}(t,e,i,s,o,n);return fr(t,[e[0],e[1]],[e[2],e[3]],r,s,o)}function _l(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],o=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const n of e)if(!(gs(o,n)>Math.hypot(t[2]-t[0],t[3]-t[1])&&gs([n[0],n[1]],t)>i))for(const e of[[n[0],n[1]],[n[2],n[3]]])gs(e,t)<=2*i&&s.push(e);return s}function ml(t,e,i,s){const o=il(t);if(!o.length)return[];const n=rl(e,s);if(!n.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of o){const e=tl(t,i),o=e[0],l=e[1],c=e[2]-o,h=e[3]-l,d=Math.hypot(c,h);if(d4*s||n>4*s)continue;const r=(t[0]-o)*p+(t[1]-l)*u,c=(t[2]-o)*p+(t[3]-l)*u,h=Math.max(0,Math.min(r,c)),m=Math.min(d,Math.max(r,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+a?e.hi=Math.max(e.hi,t.hi):i.push({...t})}for(const e of i){const i=[o+p*e.lo,l+u*e.lo],s=[o+p*e.hi,l+u*e.hi];Math.hypot(s[0]-i[0],s[1]-i[1])i.pair===t&&Math.abs(i.ux*r-i.uy*n)<=1e-6&&Math.abs((e[0]-i.origin[0])*i.uy-(e[1]-i.origin[1])*i.ux)<=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:n,uy:r,ranges:[]},l.push(h));const d=(e[0]-h.origin[0])*h.ux+(e[1]-h.origin[1])*h.uy,p=(e[2]-h.origin[0])*h.ux+(e[3]-h.origin[1])*h.uy;h.ranges.push({lo:Math.min(d,p),hi:Math.max(d,p)})}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+a?t.hi=Math.max(t.hi,i.hi):e.push({...i})}for(const s of e){const e=[t.origin[0]+t.ux*s.lo,t.origin[1]+t.uy*s.lo],o=[t.origin[0]+t.ux*s.hi,t.origin[1]+t.uy*s.hi];Math.hypot(o[0]-e[0],o[1]-e[1])>=a&&h.push(Qa(e,o,i))}}return h}function gl(t,e,i,s){const o=il(t);if(!o.length)return[];if(e.length!==i.length)return o;const n=[];for(const t of o){const o=tl(t,s),r=[o[0],o[1]],a=[o[2],o[3]];let l=r,c=a;for(let t=0;t{const e=sl(t,[s[0],s[1],o[0],o[1]]);if(e.d>.001)return t;const i=h[0]-n[0],r=h[1]-n[1];return[n[0]+i*e.t,n[1]+r*e.t]};if(sl([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],o[0],o[1]]).d<.01){l=d(r),c=d(a);break}}n.push(Qa(l,c,s))}return n}class fl{constructor(t,e=()=>Date.now()){this.onUpdate=t,this.now=e,this.signed={},this.queued=new Set,this.inFlight=new Map,this.retry=new Map,this.disposed=!1}start(t,e){this.disposed=!1,this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(t(),e()),288e5)}dispose(){this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.queued.clear(),this.inFlight.clear()}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(t,e){const i=hs(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],o=s?this.now()-s.at:1/0;return o{const e=[...this.queued];this.queued.clear(),this.sign(t,e)},30))}sign(t,e){if(e.length&&t?.callWS)for(const i of function(t,e){const i=Math.max(1,Math.floor(e)),s=[];for(let e=0;e{if(this.disposed)return;const e=this.now(),s={...this.signed};let o=0;for(const n of i){const i=t?.urls?.[n];"string"==typeof i&&i?(s[n]={url:i,at:e},this.retry.delete(n),o++):this.backOff(n)}o&&(this.signed=s,this.onUpdate())}).catch(()=>{for(const t of i)this.backOff(t)}).finally(()=>{for(const t of i)this.inFlight.get(t)===e&&this.inFlight.delete(t)})}}backOff(t){const e=this.retry.get(t)?.delay||0,i=Math.min(6e4,e?2*e:2e3);this.retry.set(t,{notBefore:this.now()+i,delay:i})}resign(t,e){const i=this.now(),s={};for(const[t,o]of Object.entries(this.signed))e.has(t)&&i-o.at{if(null==t||""===t)return null;const e=Number(t);return Number.isFinite(e)?e:null},wl=t=>{const e=t,i=bl(Array.isArray(e)?e[0]:e?.x),s=bl(Array.isArray(e)?e[1]:e?.y);return null==i||null==s?null:[i,s]};function kl(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=wl(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,o=0;for(let t=0;tt+e[0],0)/e.length,e.reduce((t,e)=>t+e[1],0)/e.length]:[s/(3*i),o/(3*i)]}function $l(t){if(!Array.isArray(t))return null;const e=[];for(const i of t){const t=wl(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=kl(e);if(!i)return null;const s=e.map(t=>t[0]),o=e.map(t=>t[1]);return{centroid:i,bbox:[Math.min(...s),Math.min(...o),Math.max(...s),Math.max(...o)]}}function xl(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(wl).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,o=i.map(t=>t.length-2),n=o.reduce((t,e)=>t+e,0),r=o.map(t=>s*t/n),a=r.map(Math.floor);let l=s-a.reduce((t,e)=>t+e,0);const c=r.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 Cl(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=bl(e.x)&&null!=bl(e.y)?{x:bl(e.x),y:bl(e.y),a:bl(e.a??e.angle??e.theta)}:null,s=xl(t.path?.path??t.path?.points??t.path),o=[],n=t.rooms,r=Array.isArray(n)?n.map((t,e)=>[String(t?.id??e),t]):n&&"object"==typeof n?Object.entries(n):[];for(const[t,e]of r){if(!e||"object"!=typeof e)continue;const i=String(e.name??e.label??"").trim(),s=[[bl(e.cx),bl(e.cy)],[bl(e.center?.x),bl(e.center?.y)],[bl(e.x),bl(e.y)]].find(([t,e])=>null!=t&&null!=e);let n=s?.[0]??null,r=s?.[1]??null;const a=$l(e.outline),l=bl(e.x0),c=bl(e.y0),h=bl(e.x1),d=bl(e.y1),p=(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)||a?.bbox||null;if(null!=n&&null!=r||!a||([n,r]=a.centroid),null!=n&&null!=r||!p||(n=(p[0]+p[2])/2,r=(p[1]+p[3])/2),i&&null!=n&&null!=r){const e={id:t,name:i,cx:n,cy:r};p&&([e.x0,e.y0,e.x1,e.y1]=p),o.push(e)}}const a=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||o.length||s.length?{pos:i,path:s,rooms:o,mapId:a}:null}function Dl(t,e,i){if(!t||!e)return null;const s=e.attributes||{},o=t.split(".")[0]||"",n=null!=i?.platform?String(i.platform):null,r=!!wl(s.vacuum_position??s.robot_position),a=!(!s.rooms||"object"!=typeof s.rooms),l=xl(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"===n,d=a||l||c;let p=null;if(r?p="compatible":h?p="known_xcme_incomplete":d?p="partial":"camera"===o&&(p="camera"),!p)return null;const u=r?"camera"===o?300:200:h?100:d?50:0;return{entityId:t,name:String(s.friendly_name||t),platform:n,category:p,hasPosition:r,hasRooms:a,hasPath:l,hasMapId:c,score:u}}const Tl=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function Pl(t,e){const i=new Map(e.map(t=>[Tl(t.name),t])),s=[],o=[];for(const e of t){const t=i.get(Tl(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),o.push(e.name))}if(s.length<3)return null;const n=function(t){if(t.length<3)return null;let e=0,i=0,s=0,o=0,n=0,r=0,a=0,l=0,c=0,h=0,d=0,p=0;for(const[[u,_],[m,g]]of t){if(![u,_,m,g].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,o+=_*_,n+=_,r+=1,a+=u*m,l+=_*m,c+=m,h+=u*g,d+=_*g,p+=g}const u=[e,i,s,i,o,n,s,n,r],_=t=>{const[e,i,s,o,n,r,a,l,c]=u,h=e*(n*c-r*l)-i*(o*c-r*a)+s*(o*l-n*a);if(!Number.isFinite(h)||Math.abs(h)<1e-9)return null;const d=[(n*c-r*l)/h,(s*l-i*c)/h,(i*r-s*n)/h,(r*a-o*c)/h,(e*c-s*a)/h,(s*o-e*r)/h,(o*l-n*a)/h,(i*a-e*l)/h,(e*n-i*o)/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=_([a,l,c]),g=_([h,d,p]);if(!m||!g)return null;const f=[m[0],m[1],m[2],g[0],g[1],g[2]];return f.every(Number.isFinite)?f:null}(s);return n?{matrix:n,matched:o,residual:yl(n,s)}:null}function Rl(t,e,i){const s=t[t.length-1];if(s&&s[0]===e[0]&&s[1]===e[1])return t;if(t.push(e),t.length<=600)return t;let o=function(t,e){if(t.length<3)return t.slice();const i=new Uint8Array(t.length);i[0]=i[t.length-1]=1;const s=[[0,t.length-1]];for(;s.length;){const[o,n]=s.pop(),[r,a]=t[o],[l,c]=t[n],h=l-r,d=c-a,p=Math.hypot(h,d)||1e-9;let u=0,_=-1;for(let e=o+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([o,_],[_,n]))}const o=[];for(let e=0;e600&&(o=o.filter((t,e)=>e%2==0||e===o.length-1)),o}function zl(t){return"cleaning"===t||"returning"===t||"on"===t}const Al={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function Il(t){const[e,i]=Al[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 El(t){const e=t[0]*t[4]-t[1]*t[3];if(!Number.isFinite(e)||Math.abs(e)<1e-12)return null;const i=e<0,s=Math.sqrt(Math.abs(e));let o=180*Math.atan2(-t[1],t[4])/Math.PI;return o=(90*Math.round(o/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:o,mir:i}}function Nl(t,e,i,s){const[o,n]=vl(Il(e),i,s),r=Il({...t,ox:0,oy:0}),[a,l]=vl(r,i,s);return{...t,ox:o-a,oy:n-l}}function Fl(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}class Ol extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return tn(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:en(e,"editor.lang_auto")},{value:"en",label:en(e,"editor.lang_en")},{value:"ru",label:en(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 W;this._loadSpaces();const t=this._lang,e={title:en(t,"editor.title"),default_floor:en(t,"editor.default_floor"),language:en(t,"editor.language"),icon_size:en(t,"editor.icon_size"),show_temperature:en(t,"editor.show_temperature"),live_states:en(t,"editor.live_states"),show_signal:en(t,"editor.show_signal"),kiosk:en(t,"editor.kiosk"),cycle:en(t,"editor.cycle")};return j`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 Pl(t){const e=oa(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):kt,n=xt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:$t);return yn({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=>Gn(t.lang,e),iconRules:n})}function Rl(t){const e=oa(t.cfg).find(e=>e.id===t.spaceId);if(!e)return null;const i=Fi(t.cfg.spaces.find(e=>e.id===t.spaceId)),s=Hi(t.cfg.settings),n=t.iconSize??2.5,o=n>8?2.5:n,r=t.registry?Ws(t.hass,t.registry):t.hass,a=(t.devices||Pl(t)).filter(e=>e.space===t.spaceId),l=a.filter(t=>!t.hidden),c=function(t,e,i){const s={},n=i/100*Ma(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=Ta(i),r=.1*Math.min(o.w,o.h),a=o.w-2*r,l=o.h-2*r,c=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),h=a/c,d=l/Math.max(Math.ceil(e.length/c),1),p=e.map((t,e)=>({x:o.x+r+h*(e%c+.5),y:o.y+r+d*(Math.floor(e/c)+.5)}));hi(p,o,n,.5*r),e.forEach((t,e)=>s[t.id]=pa(p[e]))}return s}(a,e,o),h=[];for(const e of l){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*Qr,e=i.y*Qr;h.push({minX:t,minY:e,maxX:t,maxY:e})}}const d=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},p=Array.isArray(d.walls)?d.walls:[],u=Number(d.cell_cm)>0?Number(d.cell_cm):5,_=function(t,e,i){const s=[];for(const n of t.partitions||[]){const t=Wr(n.a,n.b,n.cm,e,i);t&&s.push(t)}for(const n of t.room_drafts||[])s.push(...Gr(n,e,i));for(const n of t.wall_columns||[])s.push(Br(n,e,i));return s}(e,u,ca);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=xa(e,h),g=[m.x,m.y,m.w,m.h],f=new Map(e.rooms.map(t=>{const e=os(i.fill,t);return[t,Ui(e,"lqi"===e&&t.area?kn(r,a,t.area):null,"light"===e?ln(an(r,a,t)):"none","temp"===e&&t.area?$n(r,a,t.area):null,i.tempMin,i.tempMax,s,Ni(i.customFill,t))]})),v=e.rooms.filter(t=>t.area||i.showBorders||"none"!==os(i.fill,t)).map(t=>{let s="room "+(e.bg?"overlay":"yard"),n="";const o=os(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 r=t.id||G,a=t.area||G,l=t.poly?W``:W``;return l}),y=e.rooms.filter(t=>{const e=f.get(t);return rs(i.glow,t)&&(!e||e.opacity<=0)}).map(t=>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 Ll(t){const e=_a(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):kt,o=xt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:$t);return Do({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=>en(t.lang,e),iconRules:o})}function Hl(t){const e=_a(t.cfg).find(e=>e.id===t.spaceId);if(!e)return null;const i=Fi(t.cfg.spaces.find(e=>e.id===t.spaceId)),s=Hi(t.cfg.settings),o=t.iconSize??2.5,n=o>8?2.5:o,r=t.registry?Js(t.hass,t.registry):t.hass,a=(t.devices||Ll(t)).filter(e=>e.space===t.spaceId),l=a.filter(t=>!t.hidden),c=function(t,e,i){const s={},o=i/100*Ea(e)*1.3;for(const i of e.rooms){if(!i.area)continue;const e=t.filter(t=>t.area===i.area);if(!e.length)continue;const n=Oa(i),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,c=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),h=a/c,d=l/Math.max(Math.ceil(e.length/c),1),p=e.map((t,e)=>({x:n.x+r+h*(e%c+.5),y:n.y+r+d*(Math.floor(e/c)+.5)}));hi(p,n,o,.5*r),e.forEach((t,e)=>s[t.id]=wa(p[e]))}return s}(a,e,n),h=[];for(const e of l){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*la,e=i.y*la;h.push({minX:t,minY:e,maxX:t,maxY:e})}}const d=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},p=Array.isArray(d.walls)?d.walls:[],u=Number(d.cell_cm)>0?Number(d.cell_cm):5,_=function(t,e,i){const s=[];for(const o of t.partitions||[]){const t=Qr(o.a,o.b,o.cm,e,i);t&&s.push(t)}for(const o of t.room_drafts||[])s.push(...ea(o,e,i));for(const o of t.wall_columns||[])s.push(ta(o,e,i));return s}(e,u,va);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=Aa(e,h),g=[m.x,m.y,m.w,m.h],f=new Map(e.rooms.map(t=>{const e=ds(i.fill,t);return[t,Ui(e,"lqi"===e&&t.area?Ro(r,a,t.area):null,"light"===e?fo(mo(r,a,t)):"none","temp"===e&&t.area?zo(r,a,t.area):null,i.tempMin,i.tempMax,s,Ni(i.customFill,t))]})),v=e.rooms.filter(t=>t.area||i.showBorders||"none"!==ds(i.fill,t)).map(t=>{let s="room "+(e.bg?"overlay":"yard"),o="";const n=ds(i.fill,t);if(i.showBorders||"none"!==n){s+=" styled";const e=[`--room-stroke:${i.color}`,`--room-stroke-op:${i.showBorders?i.opacity:0}`],n=f.get(t)||null;n?(s+=" filled",e.push(`--room-fill:${n.color}`,`--room-fill-op:${n.opacity.toFixed(3)}`)):e.push("--room-fill:transparent","--room-fill-op:0"),o=e.join(";")}const r=t.id||W,a=t.area||W,l=t.poly?B``:B``;return l}),y=e.rooms.filter(t=>{const e=f.get(t);return ps(i.glow,t)&&(!e||e.opacity<=0)}).map(t=>t.poly?B``:W`
`),o.push(W``)}const a=this._rszSel?r.find(t=>t.id===this._rszSel):null;if(a){const t=a.poly.map(t=>t[0]),i=a.poly.map(t=>t[1]),s=Math.min(...t),n=Math.max(...t),r=Math.min(...i),l=Math.max(...i);o.push(W``);for(const[t,i,c,h]of[[s,r,n,l],[n,r,s,l],[n,l,s,r],[s,l,n,r]])o.push(W`this._rszPointerCancel(t)}>`),n.push(B``)}const a=this._rszSel?r.find(t=>t.id===this._rszSel):null;if(a){const t=a.poly.map(t=>t[0]),i=a.poly.map(t=>t[1]),s=Math.min(...t),o=Math.max(...t),r=Math.min(...i),l=Math.max(...i);n.push(B``);for(const[t,i,c,h]of[[s,r,o,l],[o,r,s,l],[o,l,s,r],[s,l,o,r]])n.push(B`this._rszCornerDown(e,a.id,[t,i],[c,h])} @pointermove=${t=>this._rszMove(t)} @pointerup=${t=>this._rszUp(t)} @pointercancel=${t=>this._rszPointerCancel(t)} - @lostpointercapture=${t=>this._rszPointerCancel(t)}>`),o.push(W``)}return W`${o}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH;return(t?.openings||[]).map(t=>({...t,rx:t.x*Cc,ry:t.y*e,rlen:t.length*Cc}))}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return Cc}_decorResolvedStyle(t){return function(t,e,i,s=Ro){const n="rect"===t?.kind||"ellipse"===t?.kind,o=t,r=_t(o?.color,s.color);return{color:r,opacity:zo(o?.opacity,s.opacity),widthCm:Fo(t,e,i,s.widthCm),fill:!!n&&!0===o?.fill,fillColor:_t(o?.fill_color,o?.fill?r:s.fillColor),fillOpacity:n&&o?.fill?zo(o?.fill_opacity,.25):s.fillOpacity}}(t,this._cellCm,this._gridPitch,Ro)}_decorWidthUnits(t){return((t,e,i,s=Ro.widthCm)=>{const n=Number(t?.width_cm);if(Number.isFinite(n)&&n>0)return Eo(n,e,i);const o=Number(t?.width);return Number.isFinite(o)&&o>0?o:Eo(s,e,i)})(t,this._cellCm,this._gridPitch,Ro.widthCm)}_decorTextSizeCm(t){if("text"===t?.kind){const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?e:No(Ti*Pi(t),this._cellCm,this._gridPitch)}return No(Ti,this._cellCm,this._gridPitch)}_decorTextUnits(t){if("text"!==t.kind)return Ti;const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?Eo(e,this._cellCm,this._gridPitch):Ti*Pi(t)}_decorSmallField(t){return Math.round(100*(this._imperial?t/2.54:t))/100}_decorSmallCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(100,e)):.1}_decorTextCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(lc,e)):.1}_decorLargeField(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_decorLargeCm(t){const e=this._imperial?30.48*t:100*t;return Number.isFinite(e)?Math.max(.1,Math.min(ra*this._cellCm,e)):.1}_angleField(t){const e=Number(t);return Number.isFinite(e)?String(Number(e.toFixed(3))):"0"}_decorBoxOf(t){return"rect"!==t.kind&&"ellipse"!==t.kind&&"furniture"!==t.kind?null:{x:t.x*Cc,y:t.y*this._decorH,w:t.w*Cc,h:t.h*this._decorH,angle:Ao(t.angle)||void 0}}_decorSnapGeometry(t){const e=t||"",i=this._decorSnapCache;if(i&&i.epoch===this._cfgEpoch&&i.space===this._space&&i.height===this._decorH&&i.exclude===e)return i.geometry;const s=[];for(const e of this._decorList)if(e.id!==t)if("line"===e.kind){const t=[e.x1*Cc,e.y1*this._decorH],i=[e.x2*Cc,e.y2*this._decorH];s.push({points:[t,i,[(t[0]+i[0])/2,(t[1]+i[1])/2]],segments:[{a:t,b:i}]})}else if("text"===e.kind)s.push({points:[[e.x*Cc,e.y*this._decorH]],segments:[]});else{const t=this._decorBoxOf(e);t&&s.push(Ho(t))}for(const t of this._spaceModel().rooms){const e=Fe(t);e?.length&&s.push({points:e.flatMap((t,i)=>{const s=e[(i+1)%e.length];return[t,[(t[0]+s[0])/2,(t[1]+s[1])/2]]}),segments:e.map((t,i)=>({a:t,b:e[(i+1)%e.length]}))})}const n=function(t){return{points:t.flatMap(t=>t.points),segments:t.flatMap(t=>t.segments)}}(s);return this._decorSnapCache={epoch:this._cfgEpoch,space:this._space,height:this._decorH,exclude:e,geometry:n},n}_decorSnap(t,e="mouse",i){const s=this._stageEl,n=this._viewOr(this._baseVb()),o="touch"===e||"pen"===e?14:8,r=s?n.w/Math.max(1,s.clientWidth)*o:this._gridPitch;return function(t,e,i,s){const n=s(t);let o=null;for(const n of e.points){const e=s(n),r=Math.hypot(e[0]-t[0],e[1]-t[1]);r<=i&&(!o||rthis._snap(t)).point}_replaceDecor(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>i.id===t?{...i,...e}:i),this.requestUpdate())}_cancelDecorGesture(){const t=this._decorMove?.before||this._dtDrag?.before||this._bdDrag?.before,e=t&&this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(t&&e){const i=t=>JSON.parse(JSON.stringify(t));void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];Object.assign(e,i(t.plan_transform||{})),this._cfgEpoch++}this._decorMove=null,this._dtDrag=null,this._bdDrag=null,this.requestUpdate()}_decorPointerDown(t){const e=this._decorTool;if("select"===e||"erase"===e?t.target.closest?.(".dshape"):null)return!0;if("line"===e||"rect"===e||"ellipse"===e){t.preventDefault();const i=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},Ac(t),!0}if("text"===e){const e=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorTextDialog={x:_a(e[0]/Cc),y:_a(e[1]/this._decorH),text:"",color:this._decorStyle.color,opacity:this._decorStyle.opacity,angle:"0",sizeCm:No(Ti,this._cellCm,this._gridPitch)},this._decorTextSelection={start:0,end:0},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t.shiftKey),!0);if("select"===e&&(this._decorSel=null),this._bdMovable){const e=this._bdRect,i=this._svgPoint(t),s=e.x+e.w/2,n=e.y+e.h/2,o=-Ao(e.angle)*Math.PI/180,r=i[0]-s,a=i[1]-n,l=[s+r*Math.cos(o)-a*Math.sin(o),n+r*Math.sin(o)+a*Math.cos(o)];if(l[0]>=e.x&&l[0]<=e.x+e.w&&l[1]>=e.y&&l[1]<=e.y+e.h)return t.preventDefault(),this._bdStart(t)}return!1}_decorCommitDraft(){const t=this._decorDraft;if(this._decorDraft=null,!t)return;const e=.5*this._gridPitch;if(!function(t,e,i,s){const n=Math.abs(i[0]-e[0]),o=Math.abs(i[1]-e[1]);return"line"===t?Math.hypot(n,o)>=s:n>=s&&o>=s}(t.kind,t.a,t.b,e))return;const i=Cc,s=this._decorH,n=this._decorStyle,o=this._geometrySnapshot(),r="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),a=_a;let l;if("line"===t.kind)l={id:r,kind:"line",x1:a(t.a[0]/i),y1:a(t.a[1]/s),x2:a(t.b[0]/i),y2:a(t.b[1]/s),...Oo(n,!1)};else{const e=a(Math.min(t.a[0],t.b[0])/i),o=a(Math.min(t.a[1],t.b[1])/s),c=Math.abs(t.b[0]-t.a[0])/i,h=Math.abs(t.b[1]-t.a[1])/s;l={id:r,kind:t.kind,x:e,y:o,w:c,h:h,...Oo(n,!0)}}this._curSpaceCfg.decor=[...this._decorList,l],this._decorSel=r,this._recordGeometry(this._t("history.decor_add"),o),this._saveConfig(),this.requestUpdate()}_decorShapeDown(t,e){if("decor"!==this._mode)return;const i=this._decorTool;if("text"===i){if("text"!==e.kind)return;return t.stopPropagation(),t.preventDefault(),void this._decorOpenText(e)}"select"!==i&&"erase"!==i||(t.stopPropagation(),t.preventDefault(),"erase"!==i?(this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1,before:this._geometrySnapshot()},Ac(t)):this._decorEraseConfirm={id:e.id,kind:e.kind})}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,n=("line"===s.kind?s.x1:s.x)*Cc,o=("line"===s.kind?s.y1:s.y)*this._decorH,r=this._decorSnap([n+(i[0]-e.start[0]),o+(i[1]-e.start[1])],t.pointerType,e.id);let a=(r[0]-n)/Cc,l=(r[1]-o)/this._decorH;const c=e.orig,h="line"===c.kind?Math.min(c.x1,c.x2):c.x,d="line"===c.kind?Math.min(c.y1,c.y2):c.y,p="line"===c.kind?Math.abs(c.x2-c.x1):c.w||0,u="line"===c.kind?Math.abs(c.y2-c.y1):c.h||0,_=ra;a=Math.max(-_-h,Math.min(_-h-p,a)),l=Math.max(-_-d,Math.min(_-d-u,l)),(a||l)&&(e.moved=!0);this._curSpaceCfg.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i=e.orig;return"line"===t.kind?{...t,x1:i.x1+a,y1:i.y1+l,x2:i.x2+a,y2:i.y2+l}:{...t,x:i.x+a,y:i.y+l}}),this.requestUpdate()}_decorShapeDbl(t,e){"decor"===this._mode&&"select"===this._decorTool&&(t.preventDefault(),t.stopPropagation(),this._decorMove=null,this._decorSel=e.id,this._openDecorProperties(e))}_openDecorProperties(t){if("decor"!==this._mode||"select"!==this._decorTool)return;if("text"===t.kind)return void this._decorOpenText(t);if(!["line","rect","ellipse","furniture"].includes(t.kind))return;const e=this._decorResolvedStyle(t),i="line"===t.kind?t:null,s=this._decorBoxOf(t);this._decorShapeDialog={id:t.id,kind:t.kind,color:e.color,opacity:e.opacity,widthCm:e.widthCm,angle:this._angleField(Ao(i?ls([i.x1*Cc,i.y1*this._decorH],[i.x2*Cc,i.y2*this._decorH]):t.angle)),...i?{lengthCm:No(Math.hypot((i.x2-i.x1)*Cc,(i.y2-i.y1)*this._decorH),this._cellCm,this._gridPitch),lineStyle:"dashed"===i.line_style?"dashed":"solid"}:{},...s?{sizeWCm:No(s.w,this._cellCm,this._gridPitch),sizeHCm:No(s.h,this._cellCm,this._gridPitch)}:{},..."furniture"===t.kind?{symbol:t.symbol}:{},..."rect"===t.kind||"ellipse"===t.kind?{fill:e.fill,fillColor:e.fillColor,fillOpacity:e.fillOpacity}:{}}}_decorOpenText(t){if("text"!==t.kind)return;let e=String(t.text??"");const i=[...e.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!xi(t[1])),s=String(t.unit??"").trim(),n="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,o=i||s?"":Si(t.entity,n),r=!(!String(t.entity??"").trim()||i||o&&!s);if(o&&!r){const t=e.indexOf("{}");e=t>=0?e.slice(0,t)+o+e.slice(t+2):`${e}${e?" ":""}${o}`}this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:e,color:_t(t.color,this._decorStyle.color),opacity:zo(t.opacity,this._decorStyle.opacity),angle:this._angleField(t.angle),sizeCm:this._decorTextSizeCm(t),pickerEntity:t.entity||"",preserveLegacy:r||void 0},this._decorTextSelection={start:e.length,end:e.length}}_decorRememberTextSelection(t){this._decorTextSelection={start:t.selectionStart??t.value.length,end:t.selectionEnd??t.value.length}}_decorInsertLiveVariable(t){const e=this._decorTextDialog;if(!e)return;const i=Si(e.pickerEntity,t);if(!i)return;const s=e.text,n=Math.max(0,Math.min(s.length,this._decorTextSelection.start)),o=Math.max(n,Math.min(s.length,this._decorTextSelection.end));if(s.length-(o-n)+i.length>200)return;const r=s.slice(0,n)+i+s.slice(o),a=n+i.length;this._decorTextSelection={start:a,end:a},this._decorTextDialog={...e,text:r,preserveLegacy:void 0},this.updateComplete.then(()=>{const t=this.renderRoot.querySelector("textarea.dtarea");t&&(t.focus(),t.setSelectionRange(a,a))})}_decorSaveText(){const t=this._decorTextDialog,e=String(t?.text??"").replace(/\r\n?/g,"\n").trim();if(!t||!e)return void(this._decorTextDialog=null);const i=this._geometrySnapshot(),s=this._curSpaceCfg,n={color:t.color,opacity:zo(t.opacity),size_cm:Number(Math.max(.1,Math.min(lc,t.sizeCm)).toFixed(4)),...Ao(t.angle)?{angle:Ao(t.angle)}:{}};if(t.id)s.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;if("text"!==i.kind)return i;if(t.preserveLegacy){const{angle:t,size:s,scale:o,...r}=i;return{...r,text:e,...n}}const{entity:s,attr:o,unit:r,...a}=i,{angle:l,size:c,scale:h,...d}=a;return{...d,text:e,...n}});else{const i="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);s.decor=[...this._decorList,{id:i,kind:"text",x:t.x,y:t.y,text:e,...n}],this._decorSel=i}this._decorTextDialog=null,this._recordGeometry(this._t(t.id?"history.decor_edit":"history.decor_add"),i),this._saveConfig(),this.requestUpdate()}_decorSaveShape(){const t=this._decorShapeDialog;if(!t)return;const e=this._geometrySnapshot(),i={color:t.color,opacity:zo(t.opacity),widthCm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),fill:!!t.fill,fillColor:t.fillColor||t.color,fillOpacity:zo(t.fillOpacity,.25)};this._curSpaceCfg.decor=this._decorList.map(e=>{if(e.id!==t.id)return e;const s="rect"===t.kind||"ellipse"===t.kind,n=Oo(i,s);if("line"===e.kind){const i=(e.x1+e.x2)/2*Cc,s=(e.y1+e.y2)/2*this._decorH,o=Eo(Math.max(.1,Number(t.lengthCm)||.1),this._cellCm,this._gridPitch),r=Ao(t.angle)*Math.PI/180,a=Math.cos(r)*o/2,l=Math.sin(r)*o/2,c=this._snap([i-a,s-l]),h=this._snap([i+a,s+l]),{width:d,line_style:p,...u}=e;return{...u,...n,x1:_a(c[0]/Cc),y1:_a(c[1]/this._decorH),x2:_a(h[0]/Cc),y2:_a(h[1]/this._decorH),..."dashed"===t.lineStyle?{line_style:"dashed"}:{}}}if("rect"===e.kind||"ellipse"===e.kind||"furniture"===e.kind){const i=e.w*Cc,s=e.h*this._decorH,o=Math.max(this._gridPitch,Ie(Eo(Number(t.sizeWCm),this._cellCm,this._gridPitch),this._gridPitch)),r=Math.max(this._gridPitch,Ie(Eo(Number(t.sizeHCm),this._cellCm,this._gridPitch),this._gridPitch)),a=e.x*Cc+i/2,l=e.y*this._decorH+s/2,c=Ao(t.angle),h=Io({x:a-o/2,y:l-r/2},c,t=>this._snap(t)),{width:d,angle:p,...u}=e;return{...u,...n,x:_a(h[0]/Cc),y:_a(h[1]/this._decorH),w:o/Cc,h:r/this._decorH,..."furniture"===e.kind&&t.symbol?{symbol:t.symbol}:{},...c?{angle:c}:{}}}return e}),this._decorStyle={...i,fill:"rect"===t.kind||"ellipse"===t.kind?i.fill:this._decorStyle.fill,fillColor:"rect"===t.kind||"ellipse"===t.kind?i.fillColor:this._decorStyle.fillColor,fillOpacity:"rect"===t.kind||"ellipse"===t.kind?i.fillOpacity:this._decorStyle.fillOpacity},this._decorShapeDialog=null,this._recordGeometry(this._t("history.decor_edit"),e),this._saveConfig(),this.requestUpdate()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(t=>t.id===this._decorSel)||null}_dtPivot(t){return"line"===t.kind?[(t.x1+t.x2)/2*Cc,(t.y1+t.y2)/2*this._decorH]:"furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind?[(t.x+t.w/2)*Cc,(t.y+t.h/2)*this._decorH]:[t.x*Cc,t.y*this._decorH]}_dtApply(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const s={...i};"text"===i.kind&&void 0!==e.textSizeCm&&(delete s.size,delete s.scale);const n={...s};return void 0!==e.textSizeCm&&(n.size_cm=Number(Math.max(.1,Math.min(lc,e.textSizeCm)).toFixed(4))),void 0!==e.angle&&(e.angle?n.angle=Number(e.angle.toFixed(2)):delete n.angle),n}),this._cfgEpoch++,this.requestUpdate())}_dtStart(t,e,i,s){const n=this._dtSel;if(!n)return;t.stopPropagation(),t.preventDefault();const[o,r]=this._dtPivot(n),a=this._svgPoint(t),l=this._decorBoxOf(n);this._dtDrag={id:n.id,kind:e,pid:t.pointerId,ax:o,ay:r,r0:Math.hypot(a[0]-o,a[1]-r),a0:180*Math.atan2(a[1]-r,a[0]-o)/Math.PI,textSizeCm0:"text"===n.kind?this._decorTextSizeCm(n):1,angle0:"line"===n.kind?0:Number(n.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:l||void 0,origShape:JSON.parse(JSON.stringify(n)),before:this._geometrySnapshot(),lineEnd:s,moved:!1},Ac(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if(void 0!==e.lineEnd&&"line"===e.origShape.kind){const s=this._decorSnap(i,t.pointerType,e.id),n=_a(s[0]/Cc),o=_a(s[1]/this._decorH),r=e.origShape,a=0===e.lineEnd?r.x1:r.x2,l=0===e.lineEnd?r.y1:r.y2;return(Math.abs(n-a)>1e-9||Math.abs(o-l)>1e-9)&&(e.moved=!0),void this._replaceDecor(e.id,0===e.lineEnd?{x1:n,y1:o}:{x2:n,y2:o})}if("scale"===e.kind&&e.orig){const s=qo(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],!t.shiftKey,this._gridPitch,this._gridPitch),n=Math.abs(s.x-e.orig.x)>1e-6||Math.abs(s.y-e.orig.y)>1e-6||Math.abs(s.w-e.orig.w)>1e-6||Math.abs(s.h-e.orig.h)>1e-6;if(!n&&!e.moved)return;return e.moved||=n,void this._decorApplyBox(e.id,s)}if("scale"===e.kind){const t=Math.hypot(i[0]-e.ax,i[1]-e.ay);if(e.r0<1e-6)return;const s=Math.max(.1,Math.min(lc,e.textSizeCm0*(t/e.r0))),n=Math.abs(s-e.textSizeCm0)>1e-6;if(!n&&!e.moved)return;return e.moved||=n,void this._dtApply(e.id,{textSizeCm:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let n=e.angle0+(s-e.a0);t.shiftKey||(n=5*Math.round(n/5)),n=(n%360+360)%360,n>180&&(n-=360);const o=Math.abs(n-e.angle0)>1e-6;(o||e.moved)&&(e.moved||=o,this._dtApply(e.id,{angle:n}))}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&(this._recordGeometry(this._t("history.decor_transform"),t.before),this._saveConfig()),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("line"===t.kind){const i=t.x1*Cc,s=t.y1*this._decorH,n=t.x2*Cc,o=t.y2*this._decorH;e={id:t.id,x:Math.min(i,n),y:Math.min(s,o),w:Math.abs(n-i),h:Math.abs(o-s)}}else if("furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind)e={id:t.id,x:t.x*Cc,y:t.y*this._decorH,w:t.w*Cc,h:t.h*this._decorH};else{const i=this.renderRoot.querySelector(`text.dtext[data-id="${t.id}"]`);if(!i||"function"!=typeof i.getBBox)return;let s;try{s=i.getBBox()}catch{return}if(!s||!s.width&&!s.height)return;e={id:t.id,x:s.x,y:s.y,w:s.width,h:s.height}}const i=this._dtBox;i&&i.id===e.id&&Math.abs(i.x-e.x)<.01&&Math.abs(i.y-e.y)<.01&&Math.abs(i.w-e.w)<.01&&Math.abs(i.h-e.h)<.01||(this._dtBox=e,this.requestUpdate())}_deleteDecor(t){if(!this._decorList.some(e=>e.id===t))return;const e=this._geometrySnapshot();this._curSpaceCfg.decor=this._decorList.filter(e=>e.id!==t),this._decorSel===t&&(this._decorSel=null),this._recordGeometry(this._t("history.decor_delete"),e),this._saveConfig(),this.requestUpdate()}_decorDeleteSel(){this._decorSel&&this._deleteDecor(this._decorSel)}_confirmDecorErase(){const t=this._decorEraseConfirm;this._decorEraseConfirm=null,t&&this._deleteDecor(t.id)}get _furnWalls(){const t=this._physicalBodiesR().flatMap(t=>t.map((e,i)=>{const s=t[(i+1)%t.length];return[e[0],e[1],s[0],s[1]]}));return[...this._segments,...t]}get _furnWallReach(){return 6*this._gridPitch}_furnFieldValue(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_furnFieldToCm(t){return e=this._imperial?30.48*t:100*t,Number.isFinite(e)?Math.max(1,Math.min(1e4,e)):1;var e}_furnPick(t){const e=function(t){const e=Ia(t);return e?{w:e.w,h:e.h}:{w:60,h:60}}(t);this._furnPalette={symbol:t,w:e.w,h:e.h}}_furnPlace(t,e=!1){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const n=Cc,o=this._decorH,r=La(Na(i.w,this._cellCm,this._gridPitch,n)),a=La(Na(i.h,this._cellCm,this._gridPitch,n)),l=this._geometrySnapshot(),c=this._decorSnap(t);let h=c[0],d=c[1],p=0;const u=e?null:ja(h,d,a*o,this._furnWalls,this._furnWallReach,this._gridPitch);u&&(h=u.cx,d=u.cy,p=u.angle);const _="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),m={id:_,kind:"furniture",symbol:i.symbol,x:_a(h/n-r/2),y:_a(d/o-a/2),w:r,h:a,...Oo(this._decorStyle,!1)};p&&(m.angle=Number(p.toFixed(2))),s.decor=[...this._decorList,m],this._decorSel=_,this._decorTool="select",this._furnPalette=null,this._recordGeometry(this._t("history.decor_add"),l),this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(t){const e=this._decorMove;if("furniture"!==e.orig.kind)return;const i=e.orig,s=this._curSpaceCfg;if(!s)return;const n=Cc,o=this._decorH,r=this._svgPoint(t),a=(i.x+i.w/2)*n+(r[0]-e.start[0]),l=(i.y+i.h/2)*o+(r[1]-e.start[1]);let c,h,d=Number(i.angle)||0;const p=t.shiftKey?null:ja(a,l,i.h*o,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)c=p.cx/n-i.w/2,h=p.cy/o-i.h/2,d=p.angle;else{const s=this._decorSnap([a-i.w/2*n,l-i.h/2*o],t.pointerType,e.id);c=s[0]/n,h=s[1]/o}c=_a(c),h=_a(h),(Math.abs(c-i.x)>1e-9||Math.abs(h-i.y)>1e-9||Math.abs(d-(Number(i.angle)||0))>1e-9)&&(e.moved=!0),s.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i={...t,x:c,y:h};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_decorApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=Cc,n=this._decorH;i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const o=Io(e,i.angle,t=>this._snap(t));return{...i,x:_a(o[0]/s),y:_a(o[1]/n),w:Math.max(this._gridPitch/s,Math.min(2*ra,e.w/s)),h:Math.max(this._gridPitch/n,Math.min(2*ra,e.h/n))}}),this._cfgEpoch++,this.requestUpdate()}get _furnLive(){const t=this._dtDrag;if(!t||"scale"!==t.kind||!t.orig)return null;const e=this._decorList.find(e=>e.id===t.id);if(!e||"furniture"!==e.kind)return null;const i=Cc,s=this._decorH,n=e.w*i,o=e.h*s,r=function(t,e,i,s,n){const o=t+i/2,r=e+s/2,a=(Number(n)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=(t,e)=>{const i=t-o,s=e-r;return[o+i*l-s*c,r+i*c+s*l]};return[h(t,e),h(t+i,e),h(t+i,e+s),h(t,e+s)]}(e.x*i,e.y*s,n,o,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],l=a(r[0],r[1]),c=a(r[0],r[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[n,0])},{x:c[0],y:c[1],text:this._fmtLen([0,0],[0,o])}]}_renderFurnPalette(){const t=this._furnPalette,e=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return j`
t.stopPropagation()}> + @lostpointercapture=${t=>this._rszPointerCancel(t)}>`),n.push(B``)}return B`${n}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH;return(t?.openings||[]).map(t=>({...t,rx:t.x*Nc,ry:t.y*e,rlen:t.length*Nc}))}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return Nc}_decorResolvedStyle(t){return function(t,e,i,s=Hn){const o="rect"===t?.kind||"ellipse"===t?.kind,n=t,r=_t(n?.color,s.color);return{color:r,opacity:qn(n?.opacity,s.opacity),widthCm:Wn(t,e,i,s.widthCm),fill:!!o&&!0===n?.fill,fillColor:_t(n?.fill_color,n?.fill?r:s.fillColor),fillOpacity:o&&n?.fill?qn(n?.fill_opacity,.25):s.fillOpacity}}(t,this._cellCm,this._gridPitch,Hn)}_decorWidthUnits(t){return((t,e,i,s=Hn.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return Bn(o,e,i);const n=Number(t?.width);return Number.isFinite(n)&&n>0?n:Bn(s,e,i)})(t,this._cellCm,this._gridPitch,Hn.widthCm)}_decorTextSizeCm(t){if("text"===t?.kind){const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?e:Gn(Ti*Pi(t),this._cellCm,this._gridPitch)}return Gn(Ti,this._cellCm,this._gridPitch)}_decorTextUnits(t){if("text"!==t.kind)return Ti;const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?Bn(e,this._cellCm,this._gridPitch):Ti*Pi(t)}_decorSmallField(t){return Math.round(100*(this._imperial?t/2.54:t))/100}_decorSmallCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(100,e)):.1}_decorTextCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(fc,e)):.1}_decorLargeField(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_decorLargeCm(t){const e=this._imperial?30.48*t:100*t;return Number.isFinite(e)?Math.max(.1,Math.min(ma*this._cellCm,e)):.1}_angleField(t){const e=Number(t);return Number.isFinite(e)?String(Number(e.toFixed(3))):"0"}_decorBoxOf(t){return"rect"!==t.kind&&"ellipse"!==t.kind&&"furniture"!==t.kind?null:{x:t.x*Nc,y:t.y*this._decorH,w:t.w*Nc,h:t.h*this._decorH,angle:Un(t.angle)||void 0}}_decorSnapGeometry(t){const e=t||"",i=this._decorSnapCache;if(i&&i.epoch===this._cfgEpoch&&i.space===this._space&&i.height===this._decorH&&i.exclude===e)return i.geometry;const s=[];for(const e of this._decorList)if(e.id!==t)if("line"===e.kind){const t=[e.x1*Nc,e.y1*this._decorH],i=[e.x2*Nc,e.y2*this._decorH];s.push({points:[t,i,[(t[0]+i[0])/2,(t[1]+i[1])/2]],segments:[{a:t,b:i}]})}else if("text"===e.kind)s.push({points:[[e.x*Nc,e.y*this._decorH]],segments:[]});else{const t=this._decorBoxOf(e);t&&s.push(Yn(t))}for(const t of this._spaceModel().rooms){const e=Fe(t);e?.length&&s.push({points:e.flatMap((t,i)=>{const s=e[(i+1)%e.length];return[t,[(t[0]+s[0])/2,(t[1]+s[1])/2]]}),segments:e.map((t,i)=>({a:t,b:e[(i+1)%e.length]}))})}const o=function(t){return{points:t.flatMap(t=>t.points),segments:t.flatMap(t=>t.segments)}}(s);return this._decorSnapCache={epoch:this._cfgEpoch,space:this._space,height:this._decorH,exclude:e,geometry:o},o}_decorSnap(t,e="mouse",i){const s=this._stageEl,o=this._viewOr(this._baseVb()),n="touch"===e||"pen"===e?14:8,r=s?o.w/Math.max(1,s.clientWidth)*n:this._gridPitch;return function(t,e,i,s){const o=s(t);let n=null;for(const o of e.points){const e=s(o),r=Math.hypot(e[0]-t[0],e[1]-t[1]);r<=i&&(!n||rthis._snap(t)).point}_replaceDecor(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>i.id===t?{...i,...e}:i),this.requestUpdate())}_cancelDecorGesture(){const t=this._decorMove?.before||this._dtDrag?.before||this._bdDrag?.before,e=t&&this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(t&&e){const i=t=>JSON.parse(JSON.stringify(t));void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];Object.assign(e,i(t.plan_transform||{})),this._cfgEpoch++}this._decorMove=null,this._dtDrag=null,this._bdDrag=null,this.requestUpdate()}_decorPointerDown(t){const e=this._decorTool;if("select"===e||"erase"===e?t.target.closest?.(".dshape"):null)return!0;if("line"===e||"rect"===e||"ellipse"===e){t.preventDefault();const i=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},Uc(t),!0}if("text"===e){const e=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorTextDialog={x:$a(e[0]/Nc),y:$a(e[1]/this._decorH),text:"",color:this._decorStyle.color,opacity:this._decorStyle.opacity,angle:"0",sizeCm:Gn(Ti,this._cellCm,this._gridPitch)},this._decorTextSelection={start:0,end:0},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t.shiftKey),!0);if("select"===e&&(this._decorSel=null),this._bdMovable){const e=this._bdRect,i=this._svgPoint(t),s=e.x+e.w/2,o=e.y+e.h/2,n=-Un(e.angle)*Math.PI/180,r=i[0]-s,a=i[1]-o,l=[s+r*Math.cos(n)-a*Math.sin(n),o+r*Math.sin(n)+a*Math.cos(n)];if(l[0]>=e.x&&l[0]<=e.x+e.w&&l[1]>=e.y&&l[1]<=e.y+e.h)return t.preventDefault(),this._bdStart(t)}return!1}_decorCommitDraft(){const t=this._decorDraft;if(this._decorDraft=null,!t)return;const e=.5*this._gridPitch;if(!function(t,e,i,s){const o=Math.abs(i[0]-e[0]),n=Math.abs(i[1]-e[1]);return"line"===t?Math.hypot(o,n)>=s:o>=s&&n>=s}(t.kind,t.a,t.b,e))return;const i=Nc,s=this._decorH,o=this._decorStyle,n=this._geometrySnapshot(),r="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),a=$a;let l;if("line"===t.kind)l={id:r,kind:"line",x1:a(t.a[0]/i),y1:a(t.a[1]/s),x2:a(t.b[0]/i),y2:a(t.b[1]/s),...Vn(o,!1)};else{const e=a(Math.min(t.a[0],t.b[0])/i),n=a(Math.min(t.a[1],t.b[1])/s),c=Math.abs(t.b[0]-t.a[0])/i,h=Math.abs(t.b[1]-t.a[1])/s;l={id:r,kind:t.kind,x:e,y:n,w:c,h:h,...Vn(o,!0)}}this._curSpaceCfg.decor=[...this._decorList,l],this._decorSel=r,this._recordGeometry(this._t("history.decor_add"),n),this._saveConfig(),this.requestUpdate()}_decorShapeDown(t,e){if("decor"!==this._mode)return;const i=this._decorTool;if("text"===i){if("text"!==e.kind)return;return t.stopPropagation(),t.preventDefault(),void this._decorOpenText(e)}"select"!==i&&"erase"!==i||(t.stopPropagation(),t.preventDefault(),"erase"!==i?(this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1,before:this._geometrySnapshot()},Uc(t)):this._decorEraseConfirm={id:e.id,kind:e.kind})}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,o=("line"===s.kind?s.x1:s.x)*Nc,n=("line"===s.kind?s.y1:s.y)*this._decorH,r=this._decorSnap([o+(i[0]-e.start[0]),n+(i[1]-e.start[1])],t.pointerType,e.id);let a=(r[0]-o)/Nc,l=(r[1]-n)/this._decorH;const c=e.orig,h="line"===c.kind?Math.min(c.x1,c.x2):c.x,d="line"===c.kind?Math.min(c.y1,c.y2):c.y,p="line"===c.kind?Math.abs(c.x2-c.x1):c.w||0,u="line"===c.kind?Math.abs(c.y2-c.y1):c.h||0,_=ma;a=Math.max(-_-h,Math.min(_-h-p,a)),l=Math.max(-_-d,Math.min(_-d-u,l)),(a||l)&&(e.moved=!0);this._curSpaceCfg.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i=e.orig;return"line"===t.kind?{...t,x1:i.x1+a,y1:i.y1+l,x2:i.x2+a,y2:i.y2+l}:{...t,x:i.x+a,y:i.y+l}}),this.requestUpdate()}_decorShapeDbl(t,e){"decor"===this._mode&&"select"===this._decorTool&&(t.preventDefault(),t.stopPropagation(),this._decorMove=null,this._decorSel=e.id,this._openDecorProperties(e))}_openDecorProperties(t){if("decor"!==this._mode||"select"!==this._decorTool)return;if("text"===t.kind)return void this._decorOpenText(t);if(!["line","rect","ellipse","furniture"].includes(t.kind))return;const e=this._decorResolvedStyle(t),i="line"===t.kind?t:null,s=this._decorBoxOf(t);this._decorShapeDialog={id:t.id,kind:t.kind,color:e.color,opacity:e.opacity,widthCm:e.widthCm,angle:this._angleField(Un(i?_s([i.x1*Nc,i.y1*this._decorH],[i.x2*Nc,i.y2*this._decorH]):t.angle)),...i?{lengthCm:Gn(Math.hypot((i.x2-i.x1)*Nc,(i.y2-i.y1)*this._decorH),this._cellCm,this._gridPitch),lineStyle:"dashed"===i.line_style?"dashed":"solid"}:{},...s?{sizeWCm:Gn(s.w,this._cellCm,this._gridPitch),sizeHCm:Gn(s.h,this._cellCm,this._gridPitch)}:{},..."furniture"===t.kind?{symbol:t.symbol}:{},..."rect"===t.kind||"ellipse"===t.kind?{fill:e.fill,fillColor:e.fillColor,fillOpacity:e.fillOpacity}:{}}}_decorOpenText(t){if("text"!==t.kind)return;let e=String(t.text??"");const i=[...e.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!xi(t[1])),s=String(t.unit??"").trim(),o="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||s?"":Si(t.entity,o),r=!(!String(t.entity??"").trim()||i||n&&!s);if(n&&!r){const t=e.indexOf("{}");e=t>=0?e.slice(0,t)+n+e.slice(t+2):`${e}${e?" ":""}${n}`}this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:e,color:_t(t.color,this._decorStyle.color),opacity:qn(t.opacity,this._decorStyle.opacity),angle:this._angleField(t.angle),sizeCm:this._decorTextSizeCm(t),pickerEntity:t.entity||"",preserveLegacy:r||void 0},this._decorTextSelection={start:e.length,end:e.length}}_decorRememberTextSelection(t){this._decorTextSelection={start:t.selectionStart??t.value.length,end:t.selectionEnd??t.value.length}}_decorInsertLiveVariable(t){const e=this._decorTextDialog;if(!e)return;const i=Si(e.pickerEntity,t);if(!i)return;const s=e.text,o=Math.max(0,Math.min(s.length,this._decorTextSelection.start)),n=Math.max(o,Math.min(s.length,this._decorTextSelection.end));if(s.length-(n-o)+i.length>200)return;const r=s.slice(0,o)+i+s.slice(n),a=o+i.length;this._decorTextSelection={start:a,end:a},this._decorTextDialog={...e,text:r,preserveLegacy:void 0},this.updateComplete.then(()=>{const t=this.renderRoot.querySelector("textarea.dtarea");t&&(t.focus(),t.setSelectionRange(a,a))})}_decorSaveText(){const t=this._decorTextDialog,e=String(t?.text??"").replace(/\r\n?/g,"\n").trim();if(!t||!e)return void(this._decorTextDialog=null);const i=this._geometrySnapshot(),s=this._curSpaceCfg,o={color:t.color,opacity:qn(t.opacity),size_cm:Number(Math.max(.1,Math.min(fc,t.sizeCm)).toFixed(4)),...Un(t.angle)?{angle:Un(t.angle)}:{}};if(t.id)s.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;if("text"!==i.kind)return i;if(t.preserveLegacy){const{angle:t,size:s,scale:n,...r}=i;return{...r,text:e,...o}}const{entity:s,attr:n,unit:r,...a}=i,{angle:l,size:c,scale:h,...d}=a;return{...d,text:e,...o}});else{const i="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);s.decor=[...this._decorList,{id:i,kind:"text",x:t.x,y:t.y,text:e,...o}],this._decorSel=i}this._decorTextDialog=null,this._recordGeometry(this._t(t.id?"history.decor_edit":"history.decor_add"),i),this._saveConfig(),this.requestUpdate()}_decorSaveShape(){const t=this._decorShapeDialog;if(!t)return;const e=this._geometrySnapshot(),i={color:t.color,opacity:qn(t.opacity),widthCm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),fill:!!t.fill,fillColor:t.fillColor||t.color,fillOpacity:qn(t.fillOpacity,.25)};this._curSpaceCfg.decor=this._decorList.map(e=>{if(e.id!==t.id)return e;const s="rect"===t.kind||"ellipse"===t.kind,o=Vn(i,s);if("line"===e.kind){const i=(e.x1+e.x2)/2*Nc,s=(e.y1+e.y2)/2*this._decorH,n=Bn(Math.max(.1,Number(t.lengthCm)||.1),this._cellCm,this._gridPitch),r=Un(t.angle)*Math.PI/180,a=Math.cos(r)*n/2,l=Math.sin(r)*n/2,c=this._snap([i-a,s-l]),h=this._snap([i+a,s+l]),{width:d,line_style:p,...u}=e;return{...u,...o,x1:$a(c[0]/Nc),y1:$a(c[1]/this._decorH),x2:$a(h[0]/Nc),y2:$a(h[1]/this._decorH),..."dashed"===t.lineStyle?{line_style:"dashed"}:{}}}if("rect"===e.kind||"ellipse"===e.kind||"furniture"===e.kind){const i=e.w*Nc,s=e.h*this._decorH,n=Math.max(this._gridPitch,Ie(Bn(Number(t.sizeWCm),this._cellCm,this._gridPitch),this._gridPitch)),r=Math.max(this._gridPitch,Ie(Bn(Number(t.sizeHCm),this._cellCm,this._gridPitch),this._gridPitch)),a=e.x*Nc+i/2,l=e.y*this._decorH+s/2,c=Un(t.angle),h=jn({x:a-n/2,y:l-r/2},c,t=>this._snap(t)),{width:d,angle:p,...u}=e;return{...u,...o,x:$a(h[0]/Nc),y:$a(h[1]/this._decorH),w:n/Nc,h:r/this._decorH,..."furniture"===e.kind&&t.symbol?{symbol:t.symbol}:{},...c?{angle:c}:{}}}return e}),this._decorStyle={...i,fill:"rect"===t.kind||"ellipse"===t.kind?i.fill:this._decorStyle.fill,fillColor:"rect"===t.kind||"ellipse"===t.kind?i.fillColor:this._decorStyle.fillColor,fillOpacity:"rect"===t.kind||"ellipse"===t.kind?i.fillOpacity:this._decorStyle.fillOpacity},this._decorShapeDialog=null,this._recordGeometry(this._t("history.decor_edit"),e),this._saveConfig(),this.requestUpdate()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(t=>t.id===this._decorSel)||null}_dtPivot(t){return"line"===t.kind?[(t.x1+t.x2)/2*Nc,(t.y1+t.y2)/2*this._decorH]:"furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind?[(t.x+t.w/2)*Nc,(t.y+t.h/2)*this._decorH]:[t.x*Nc,t.y*this._decorH]}_dtApply(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const s={...i};"text"===i.kind&&void 0!==e.textSizeCm&&(delete s.size,delete s.scale);const o={...s};return void 0!==e.textSizeCm&&(o.size_cm=Number(Math.max(.1,Math.min(fc,e.textSizeCm)).toFixed(4))),void 0!==e.angle&&(e.angle?o.angle=Number(e.angle.toFixed(2)):delete o.angle),o}),this._cfgEpoch++,this.requestUpdate())}_dtStart(t,e,i,s){const o=this._dtSel;if(!o)return;t.stopPropagation(),t.preventDefault();const[n,r]=this._dtPivot(o),a=this._svgPoint(t),l=this._decorBoxOf(o);this._dtDrag={id:o.id,kind:e,pid:t.pointerId,ax:n,ay:r,r0:Math.hypot(a[0]-n,a[1]-r),a0:180*Math.atan2(a[1]-r,a[0]-n)/Math.PI,textSizeCm0:"text"===o.kind?this._decorTextSizeCm(o):1,angle0:"line"===o.kind?0:Number(o.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:l||void 0,origShape:JSON.parse(JSON.stringify(o)),before:this._geometrySnapshot(),lineEnd:s,moved:!1},Uc(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if(void 0!==e.lineEnd&&"line"===e.origShape.kind){const s=this._decorSnap(i,t.pointerType,e.id),o=$a(s[0]/Nc),n=$a(s[1]/this._decorH),r=e.origShape,a=0===e.lineEnd?r.x1:r.x2,l=0===e.lineEnd?r.y1:r.y2;return(Math.abs(o-a)>1e-9||Math.abs(n-l)>1e-9)&&(e.moved=!0),void this._replaceDecor(e.id,0===e.lineEnd?{x1:o,y1:n}:{x2:o,y2:n})}if("scale"===e.kind&&e.orig){const s=Jn(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],!t.shiftKey,this._gridPitch,this._gridPitch),o=Math.abs(s.x-e.orig.x)>1e-6||Math.abs(s.y-e.orig.y)>1e-6||Math.abs(s.w-e.orig.w)>1e-6||Math.abs(s.h-e.orig.h)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyBox(e.id,s)}if("scale"===e.kind){const t=Math.hypot(i[0]-e.ax,i[1]-e.ay);if(e.r0<1e-6)return;const s=Math.max(.1,Math.min(fc,e.textSizeCm0*(t/e.r0))),o=Math.abs(s-e.textSizeCm0)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._dtApply(e.id,{textSizeCm:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let o=e.angle0+(s-e.a0);t.shiftKey||(o=5*Math.round(o/5)),o=(o%360+360)%360,o>180&&(o-=360);const n=Math.abs(o-e.angle0)>1e-6;(n||e.moved)&&(e.moved||=n,this._dtApply(e.id,{angle:o}))}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&(this._recordGeometry(this._t("history.decor_transform"),t.before),this._saveConfig()),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("line"===t.kind){const i=t.x1*Nc,s=t.y1*this._decorH,o=t.x2*Nc,n=t.y2*this._decorH;e={id:t.id,x:Math.min(i,o),y:Math.min(s,n),w:Math.abs(o-i),h:Math.abs(n-s)}}else if("furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind)e={id:t.id,x:t.x*Nc,y:t.y*this._decorH,w:t.w*Nc,h:t.h*this._decorH};else{const i=this.renderRoot.querySelector(`text.dtext[data-id="${t.id}"]`);if(!i||"function"!=typeof i.getBBox)return;let s;try{s=i.getBBox()}catch{return}if(!s||!s.width&&!s.height)return;e={id:t.id,x:s.x,y:s.y,w:s.width,h:s.height}}const i=this._dtBox;i&&i.id===e.id&&Math.abs(i.x-e.x)<.01&&Math.abs(i.y-e.y)<.01&&Math.abs(i.w-e.w)<.01&&Math.abs(i.h-e.h)<.01||(this._dtBox=e,this.requestUpdate())}_deleteDecor(t){if(!this._decorList.some(e=>e.id===t))return;const e=this._geometrySnapshot();this._curSpaceCfg.decor=this._decorList.filter(e=>e.id!==t),this._decorSel===t&&(this._decorSel=null),this._recordGeometry(this._t("history.decor_delete"),e),this._saveConfig(),this.requestUpdate()}_decorDeleteSel(){this._decorSel&&this._deleteDecor(this._decorSel)}_confirmDecorErase(){const t=this._decorEraseConfirm;this._decorEraseConfirm=null,t&&this._deleteDecor(t.id)}get _furnWalls(){const t=this._physicalBodiesR().flatMap(t=>t.map((e,i)=>{const s=t[(i+1)%t.length];return[e[0],e[1],s[0],s[1]]}));return[...this._segments,...t]}get _furnWallReach(){return 6*this._gridPitch}_furnFieldValue(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_furnFieldToCm(t){return e=this._imperial?30.48*t:100*t,Number.isFinite(e)?Math.max(1,Math.min(1e4,e)):1;var e}_furnPick(t){const e=function(t){const e=ja(t);return e?{w:e.w,h:e.h}:{w:60,h:60}}(t);this._furnPalette={symbol:t,w:e.w,h:e.h}}_furnPlace(t,e=!1){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const o=Nc,n=this._decorH,r=Ka(Ga(i.w,this._cellCm,this._gridPitch,o)),a=Ka(Ga(i.h,this._cellCm,this._gridPitch,o)),l=this._geometrySnapshot(),c=this._decorSnap(t);let h=c[0],d=c[1],p=0;const u=e?null:Xa(h,d,a*n,this._furnWalls,this._furnWallReach,this._gridPitch);u&&(h=u.cx,d=u.cy,p=u.angle);const _="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),m={id:_,kind:"furniture",symbol:i.symbol,x:$a(h/o-r/2),y:$a(d/n-a/2),w:r,h:a,...Vn(this._decorStyle,!1)};p&&(m.angle=Number(p.toFixed(2))),s.decor=[...this._decorList,m],this._decorSel=_,this._decorTool="select",this._furnPalette=null,this._recordGeometry(this._t("history.decor_add"),l),this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(t){const e=this._decorMove;if("furniture"!==e.orig.kind)return;const i=e.orig,s=this._curSpaceCfg;if(!s)return;const o=Nc,n=this._decorH,r=this._svgPoint(t),a=(i.x+i.w/2)*o+(r[0]-e.start[0]),l=(i.y+i.h/2)*n+(r[1]-e.start[1]);let c,h,d=Number(i.angle)||0;const p=t.shiftKey?null:Xa(a,l,i.h*n,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)c=p.cx/o-i.w/2,h=p.cy/n-i.h/2,d=p.angle;else{const s=this._decorSnap([a-i.w/2*o,l-i.h/2*n],t.pointerType,e.id);c=s[0]/o,h=s[1]/n}c=$a(c),h=$a(h),(Math.abs(c-i.x)>1e-9||Math.abs(h-i.y)>1e-9||Math.abs(d-(Number(i.angle)||0))>1e-9)&&(e.moved=!0),s.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i={...t,x:c,y:h};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_decorApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=Nc,o=this._decorH;i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const n=jn(e,i.angle,t=>this._snap(t));return{...i,x:$a(n[0]/s),y:$a(n[1]/o),w:Math.max(this._gridPitch/s,Math.min(2*ma,e.w/s)),h:Math.max(this._gridPitch/o,Math.min(2*ma,e.h/o))}}),this._cfgEpoch++,this.requestUpdate()}get _furnLive(){const t=this._dtDrag;if(!t||"scale"!==t.kind||!t.orig)return null;const e=this._decorList.find(e=>e.id===t.id);if(!e||"furniture"!==e.kind)return null;const i=Nc,s=this._decorH,o=e.w*i,n=e.h*s,r=function(t,e,i,s,o){const n=t+i/2,r=e+s/2,a=(Number(o)||0)*Math.PI/180,l=Math.cos(a),c=Math.sin(a),h=(t,e)=>{const i=t-n,s=e-r;return[n+i*l-s*c,r+i*c+s*l]};return[h(t,e),h(t+i,e),h(t+i,e+s),h(t,e+s)]}(e.x*i,e.y*s,o,n,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],l=a(r[0],r[1]),c=a(r[0],r[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[o,0])},{x:c[0],y:c[1],text:this._fmtLen([0,0],[0,n])}]}_renderFurnPalette(){const t=this._furnPalette,e=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return j`
t.stopPropagation()}>
${this._t("furn.title")} @@ -3670,16 +3699,16 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f
- ${Ra.map(e=>j` + ${Ha.map(e=>j`
${this._t(`furn.group_${e}`)}
- ${Ea(e).map(e=>j``)}
`)} @@ -3695,96 +3724,96 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f @input=${e=>this._furnPalette={...t,h:this._furnFieldToCm(Number(e.target.value))}} /> ${this._t("furn.place_hint")}
`:j`
${this._t("furn.pick_hint")}
`} -
`}get _bdBase(){const t=this._curSpaceCfg;return t?.plan_url?{...ta(t.plan_aspect,Cc)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?sa(t,Cc):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale),n=Number(t?.plan_scale_x),o=Number(t?.plan_scale_y),r=Number.isFinite(s)&&s>0?s:1;return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(n)&&n>0?n:r,sy:Number.isFinite(o)&&o>0?o:r,angle:Ao(t?.plan_angle)}}_openBackdropDialog(t){if(!this._bdMovable||!this._bdRect)return;t?.preventDefault(),t?.stopPropagation(),this._bdDrag=null;const e=this._bdRect;this._backdropDialog={widthCm:No(e.w,this._cellCm,this._gridPitch),heightCm:No(e.h,this._cellCm,this._gridPitch),angle:this._angleField(e.angle)}}_saveBackdropDialog(){const t=this._backdropDialog,e=this._bdBase,i=this._bdRect;if(!t||!e||!i)return;const s=Ao(t.angle),n=this._geometrySnapshot(),o=Math.min(e.w*ia,Math.max(e.w*ea,Ie(Eo(t.widthCm,this._cellCm,this._gridPitch),this._gridPitch))),r=Math.min(e.h*ia,Math.max(e.h*ea,Ie(Eo(t.heightCm,this._cellCm,this._gridPitch),this._gridPitch))),a=Io({x:i.x+i.w/2-o/2,y:i.y+i.h/2-r/2},s,t=>this._snap(t));this._bdApply((a[0]-e.x)/Cc,(a[1]-e.y)/Cc,o/e.w,r/e.h,s),this._backdropDialog=null,this._recordGeometry(this._t("history.backdrop_transform"),n),this._saveConfig()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(t,e,i,s,n){const o=this._curSpaceCfg;if(!o)return;o.plan_x=Number(_a(t).toFixed(6)),o.plan_y=Number(_a(e).toFixed(6)),delete o.plan_scale,o.plan_scale_x=Number(Math.min(ia,Math.max(ea,i)).toFixed(6)),o.plan_scale_y=Number(Math.min(ia,Math.max(ea,s)).toFixed(6));const r=Ao(n);r?o.plan_angle=Number(r.toFixed(2)):delete o.plan_angle,this._cfgEpoch++,this.requestUpdate()}_bdStart(t,e,i=!1){const s=this._bdBase,n=this._bdRect;if(!s||!n)return!1;const o=this._svgPoint(t),r=e?e[0]:0,a=e?e[1]:0,l=r>0?n.x:n.x+n.w,c=a>0?n.y:n.y+n.h;return this._bdDrag={kind:i?"rotate":e?"scale":"move",pid:t.pointerId,sx:o[0],sy:o[1],base:s,p0:this._bdParams,fx:l,fy:c,sgx:r,sgy:a,rect0:{x:n.x,y:n.y,w:n.w,h:n.h,angle:n.angle},before:this._geometrySnapshot(),moved:!1},Ac(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const t=e.rect0.x,n=e.rect0.y,o=this._snap([t+(i[0]-e.sx),n+(i[1]-e.sy)]),r=Math.abs(o[0]-t)>1e-9||Math.abs(o[1]-n)>1e-9;if(!r&&!e.moved)return;return e.moved||=r,void this._bdApply((o[0]-s.x)/Cc,(o[1]-s.y)/Cc,e.p0.sx,e.p0.sy,e.p0.angle)}if("rotate"===e.kind){const s=e.rect0.x+e.rect0.w/2,n=e.rect0.y+e.rect0.h/2,o=180*Math.atan2(e.sy-n,e.sx-s)/Math.PI;let r=e.p0.angle+(180*Math.atan2(i[1]-n,i[0]-s)/Math.PI-o);t.shiftKey||(r=5*Math.round(r/5)),r=Ao(r);const a=Math.abs(r-e.p0.angle)>1e-9;if(!a&&!e.moved)return;return e.moved||=a,void this._bdApply(e.p0.dx,e.p0.dy,e.p0.sx,e.p0.sy,r)}const n=qo(e.rect0,e.sgx,e.sgy,i[0],i[1],!t.shiftKey,this._gridPitch,Math.min(s.w,s.h)*ea),o=n.w/Math.max(1e-9,s.w),r=n.h/Math.max(1e-9,s.h),a=Math.abs(o-e.p0.sx)>1e-9||Math.abs(r-e.p0.sy)>1e-9||Math.abs(n.x-e.rect0.x)>1e-9||Math.abs(n.y-e.rect0.y)>1e-9;if(!a&&!e.moved)return;e.moved||=a;const l=Io(n,e.p0.angle,t=>this._snap(t));this._bdApply((l[0]-s.x)/Cc,(l[1]-s.y)/Cc,o,r,e.p0.angle)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.sx||1!==t.sy||0!==t.angle}_bdReset(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot();delete t.plan_x,delete t.plan_y,delete t.plan_scale,delete t.plan_scale_x,delete t.plan_scale_y,delete t.plan_angle,this._bdDrag=null,this._recordGeometry(this._t("history.backdrop_transform"),e),this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate()}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&(this._recordGeometry(this._t("history.backdrop_transform"),t.before),this._saveConfig()),this.requestUpdate()}get _bdLive(){if(!this._bdDrag)return null;const t=this._bdRect;return t?{x:t.x+t.w/2,y:t.y+t.h/2,text:`${this._fmtLen([0,0],[t.w,0])} × ${this._fmtLen([0,0],[0,t.h])}`}:null}_renderBackdropFrame(t){const e=this._bdRect;if(!this._bdActive||!e)return G;const i=.02*Math.max(t.w,t.h),s=i/4,n=e.x+e.w/2,o=e.y+e.h/2,r=Ao(e.angle),a=2.2*i;return W` +
`}get _bdBase(){const t=this._curSpaceCfg;return t?.plan_url?{...ca(t.plan_aspect,Nc)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?pa(t,Nc):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale),o=Number(t?.plan_scale_x),n=Number(t?.plan_scale_y),r=Number.isFinite(s)&&s>0?s:1;return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(o)&&o>0?o:r,sy:Number.isFinite(n)&&n>0?n:r,angle:Un(t?.plan_angle)}}_openBackdropDialog(t){if(!this._bdMovable||!this._bdRect)return;t?.preventDefault(),t?.stopPropagation(),this._bdDrag=null;const e=this._bdRect;this._backdropDialog={widthCm:Gn(e.w,this._cellCm,this._gridPitch),heightCm:Gn(e.h,this._cellCm,this._gridPitch),angle:this._angleField(e.angle)}}_saveBackdropDialog(){const t=this._backdropDialog,e=this._bdBase,i=this._bdRect;if(!t||!e||!i)return;const s=Un(t.angle),o=this._geometrySnapshot(),n=Math.min(e.w*da,Math.max(e.w*ha,Ie(Bn(t.widthCm,this._cellCm,this._gridPitch),this._gridPitch))),r=Math.min(e.h*da,Math.max(e.h*ha,Ie(Bn(t.heightCm,this._cellCm,this._gridPitch),this._gridPitch))),a=jn({x:i.x+i.w/2-n/2,y:i.y+i.h/2-r/2},s,t=>this._snap(t));this._bdApply((a[0]-e.x)/Nc,(a[1]-e.y)/Nc,n/e.w,r/e.h,s),this._backdropDialog=null,this._recordGeometry(this._t("history.backdrop_transform"),o),this._saveConfig()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(t,e,i,s,o){const n=this._curSpaceCfg;if(!n)return;n.plan_x=Number($a(t).toFixed(6)),n.plan_y=Number($a(e).toFixed(6)),delete n.plan_scale,n.plan_scale_x=Number(Math.min(da,Math.max(ha,i)).toFixed(6)),n.plan_scale_y=Number(Math.min(da,Math.max(ha,s)).toFixed(6));const r=Un(o);r?n.plan_angle=Number(r.toFixed(2)):delete n.plan_angle,this._cfgEpoch++,this.requestUpdate()}_bdStart(t,e,i=!1){const s=this._bdBase,o=this._bdRect;if(!s||!o)return!1;const n=this._svgPoint(t),r=e?e[0]:0,a=e?e[1]:0,l=r>0?o.x:o.x+o.w,c=a>0?o.y:o.y+o.h;return this._bdDrag={kind:i?"rotate":e?"scale":"move",pid:t.pointerId,sx:n[0],sy:n[1],base:s,p0:this._bdParams,fx:l,fy:c,sgx:r,sgy:a,rect0:{x:o.x,y:o.y,w:o.w,h:o.h,angle:o.angle},before:this._geometrySnapshot(),moved:!1},Uc(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const t=e.rect0.x,o=e.rect0.y,n=this._snap([t+(i[0]-e.sx),o+(i[1]-e.sy)]),r=Math.abs(n[0]-t)>1e-9||Math.abs(n[1]-o)>1e-9;if(!r&&!e.moved)return;return e.moved||=r,void this._bdApply((n[0]-s.x)/Nc,(n[1]-s.y)/Nc,e.p0.sx,e.p0.sy,e.p0.angle)}if("rotate"===e.kind){const s=e.rect0.x+e.rect0.w/2,o=e.rect0.y+e.rect0.h/2,n=180*Math.atan2(e.sy-o,e.sx-s)/Math.PI;let r=e.p0.angle+(180*Math.atan2(i[1]-o,i[0]-s)/Math.PI-n);t.shiftKey||(r=5*Math.round(r/5)),r=Un(r);const a=Math.abs(r-e.p0.angle)>1e-9;if(!a&&!e.moved)return;return e.moved||=a,void this._bdApply(e.p0.dx,e.p0.dy,e.p0.sx,e.p0.sy,r)}const o=Jn(e.rect0,e.sgx,e.sgy,i[0],i[1],!t.shiftKey,this._gridPitch,Math.min(s.w,s.h)*ha),n=o.w/Math.max(1e-9,s.w),r=o.h/Math.max(1e-9,s.h),a=Math.abs(n-e.p0.sx)>1e-9||Math.abs(r-e.p0.sy)>1e-9||Math.abs(o.x-e.rect0.x)>1e-9||Math.abs(o.y-e.rect0.y)>1e-9;if(!a&&!e.moved)return;e.moved||=a;const l=jn(o,e.p0.angle,t=>this._snap(t));this._bdApply((l[0]-s.x)/Nc,(l[1]-s.y)/Nc,n,r,e.p0.angle)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.sx||1!==t.sy||0!==t.angle}_bdReset(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot();delete t.plan_x,delete t.plan_y,delete t.plan_scale,delete t.plan_scale_x,delete t.plan_scale_y,delete t.plan_angle,this._bdDrag=null,this._recordGeometry(this._t("history.backdrop_transform"),e),this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate()}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&(this._recordGeometry(this._t("history.backdrop_transform"),t.before),this._saveConfig()),this.requestUpdate()}get _bdLive(){if(!this._bdDrag)return null;const t=this._bdRect;return t?{x:t.x+t.w/2,y:t.y+t.h/2,text:`${this._fmtLen([0,0],[t.w,0])} × ${this._fmtLen([0,0],[0,t.h])}`}:null}_renderBackdropFrame(t){const e=this._bdRect;if(!this._bdActive||!e)return W;const i=.02*Math.max(t.w,t.h),s=i/4,o=e.x+e.w/2,n=e.y+e.h/2,r=Un(e.angle),a=2.2*i;return B` - - + {t.stopPropagation(),t.preventDefault(),this._bdStart(t,void 0,!0)}}> - - ${[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]].map(([t,n,o])=>{const r=t<0?e.x:e.x+e.w,a=n<0?e.y:e.y+e.h;return W` + ${[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]].map(([t,o,n])=>{const r=t<0?e.x:e.x+e.w,a=o<0?e.y:e.y+e.h;return B`{e.stopPropagation(),e.preventDefault(),this._bdStart(e,[t,n])}}>`})} - `}_renderTextFrame(t){const e=this._dtSel,i=this._dtBox;if(!e||!i||i.id!==e.id)return G;const s=.018*Math.max(t.w,t.h),n=s/4;if("line"===e.kind){const t=[e.x1*Cc,e.y1*this._decorH],i=[e.x2*Cc,e.y2*this._decorH];return W` + @pointerdown=${e=>{e.stopPropagation(),e.preventDefault(),this._bdStart(e,[t,o])}}>`})} + `}_renderTextFrame(t){const e=this._dtSel,i=this._dtBox;if(!e||!i||i.id!==e.id)return W;const s=.018*Math.max(t.w,t.h),o=s/4;if("line"===e.kind){const t=[e.x1*Nc,e.y1*this._decorH],i=[e.x2*Nc,e.y2*this._decorH];return B` - ${[t,i].map((t,e)=>W`B`this._dtStart(t,"scale",void 0,e)}> - `)} - `}const[o,r]=this._dtPivot(e),a=Number(e.angle)||0,l=2.2*s;return W` + `)} + `}const[n,r]=this._dtPivot(e),a=Number(e.angle)||0,l=2.2*s;return B` this._dtStart(t,"rotate")}> - - ${[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]].map(([t,e,o])=>W` + ${[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]].map(([t,e,n])=>B`this._dtStart(i,"scale",[t,e])}>`)} - `}_renderDecorLayer(){const t=Cc,e=this._decorH,i="decor"===this._mode,s=i&&"erase"===this._decorTool,n=this._decorList.map(n=>{const o="dshape"+(i&&this._decorSel===n.id?" dsel":""),r=this._decorResolvedStyle(n),a=this._decorWidthUnits(n),l=t=>this._decorShapeDown(t,n),c=t=>this._decorShapeDbl(t,n);if("line"===n.kind)return W``)} + `}_renderDecorLayer(){const t=Nc,e=this._decorH,i="decor"===this._mode,s=i&&"erase"===this._decorTool,o=this._decorList.map(o=>{const n="dshape"+(i&&this._decorSel===o.id?" dsel":""),r=this._decorResolvedStyle(o),a=this._decorWidthUnits(o),l=t=>this._decorShapeDown(t,o),c=t=>this._decorShapeDbl(t,o);if("line"===o.kind)return B` - ${i&&"select"===this._decorTool?W``:G} - ${s?W``:G}`;if("rect"===n.kind){const i=(n.x+n.w/2)*t,h=(n.y+n.h/2)*e,d=Ao(n.angle);return W``:W} + ${s?B``:W}`;if("rect"===o.kind){const i=(o.x+o.w/2)*t,h=(o.y+o.h/2)*e,d=Un(o.angle);return B` - ${s?W``:G}`}if("ellipse"===n.kind){const i=(n.x+n.w/2)*t,h=(n.y+n.h/2)*e,d=Ao(n.angle);return W``:W}`}if("ellipse"===o.kind){const i=(o.x+o.w/2)*t,h=(o.y+o.h/2)*e,d=Un(o.angle);return B` - ${s?W``:G}`}if("furniture"===n.kind){const i=n.w*t,h=n.h*e,d=qa(n.symbol,i,h);if(!d)return G;const p=Number(n.angle)||0,u=n.x*t+i/2,_=n.y*e+h/2,m=`${p?`rotate(${p} ${u} ${_}) `:""}translate(${n.x*t} ${n.y*e})`;return W``:W}`}if("furniture"===o.kind){const i=o.w*t,h=o.h*e,d=Ja(o.symbol,i,h);if(!d)return W;const p=Number(o.angle)||0,u=o.x*t+i/2,_=o.y*e+h/2,m=`${p?`rotate(${p} ${u} ${_}) `:""}translate(${o.x*t} ${o.y*e})`;return B` - ${s?W``:G}`}if("text"===n.kind){const i=this._decorTextUnits(n),s=function(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}(function(t,e,i,s=()=>!0){const n=t??"";let o=!1;const r=n.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const n=xi(e);return n?(o=!0,s(n.entity||"")?Di(i,n):ki):t});if(o)return r;const a=(e?.entity||"").trim();if(!a)return n;if(!s(a)){const t=n.indexOf($i);return t>=0?n.slice(0,t)+ki+n.slice(t+2):n?`${n} ${ki}`:ki}const l=Di(i,e),c=n.indexOf($i);return c>=0?n.slice(0,c)+l+n.slice(c+2):n?`${n} ${l}`:l}(n.text,n,this.hass,t=>this._planEntityAvailable(t))),a=n.x*t,h=n.y*e,d=Number(n.angle)||0,p=h-(s.length-1)*i*1.2/2;return W``:W}`}if("text"===o.kind){const i=this._decorTextUnits(o),s=function(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}(function(t,e,i,s=()=>!0){const o=t??"";let n=!1;const r=o.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const o=xi(e);return o?(n=!0,s(o.entity||"")?Di(i,o):ki):t});if(n)return r;const a=(e?.entity||"").trim();if(!a)return o;if(!s(a)){const t=o.indexOf($i);return t>=0?o.slice(0,t)+ki+o.slice(t+2):o?`${o} ${ki}`:ki}const l=Di(i,e),c=o.indexOf($i);return c>=0?o.slice(0,c)+l+o.slice(c+2):o?`${o} ${l}`:l}(o.text,o,this.hass,t=>this._planEntityAvailable(t))),a=o.x*t,h=o.y*e,d=Number(o.angle)||0,p=h-(s.length-1)*i*1.2/2;return B`${s.map((t,e)=>W`${t}`)}`}return G});let o=G;const r=this._decorDraft;if(r){const t=this._decorStyle,e=Eo(t.widthCm,this._cellCm,this._gridPitch);if("line"===r.kind)o=W``;else{const i=Math.min(r.a[0],r.b[0]),s=Math.min(r.a[1],r.b[1]),n=Math.abs(r.b[0]-r.a[0]),a=Math.abs(r.b[1]-r.a[1]);o="rect"===r.kind?W``:W``}}return W`${n}${o}`}get _editorToolbarGroups(){return[]}_renderEditorGroupLauncher(t){return this._editorSecondary.renderGroupLauncher(t,this._editorToolbarGroups,this._editorSecondaryCopy)}get _editorSecondaryContextId(){const t=`editor:${this._mode}:${this._space}:${this._cfgEpoch}`,e=this._editorSecondary.activeGroup(this._editorToolbarGroups);if(e)return`${t}:group:${e.id}:${this._editorSecondary.groupGeneration}`;if("plan"===this._mode){const e=this._physicalSel;return e?`${t}:selection:${e.kind}:${e.id}:${e.segment??""}`:`${t}:tool:${this._tool}:${this._path.length}:${this._openWallAnchor?1:0}`}return"decor"===this._mode?"furniture"===this._decorTool?`${t}:palette:furniture:${this._furnPalette?.symbol||"none"}`:"select"===this._decorTool&&this._decorSel?`${t}:selection:decor:${this._decorSel}`:`${t}:tool:${this._decorTool}:${this._bdMoved?1:0}:${this._bdDrag?1:0}`:`${t}:none`}_runEditorContext(t,e){this._editorSecondary.runContext(t,this._editorSecondaryContextId,e)}_renderEditorGroupModel(t){return this._editorSecondary.renderGroupModel(t,this._editorSecondaryContextId,this._editorSecondaryCopy)}_renderDrawWallControl(){return j``}return W});let n=W;const r=this._decorDraft;if(r){const t=this._decorStyle,e=Bn(t.widthCm,this._cellCm,this._gridPitch);if("line"===r.kind)n=B``;else{const i=Math.min(r.a[0],r.b[0]),s=Math.min(r.a[1],r.b[1]),o=Math.abs(r.b[0]-r.a[0]),a=Math.abs(r.b[1]-r.a[1]);n="rect"===r.kind?B``:B``}}return B`${o}${n}`}get _editorToolbarGroups(){return[]}_renderEditorGroupLauncher(t){return this._editorSecondary.renderGroupLauncher(t,this._editorToolbarGroups,this._editorSecondaryCopy)}get _editorSecondaryContextId(){const t=`editor:${this._mode}:${this._space}:${this._cfgEpoch}`,e=this._editorSecondary.activeGroup(this._editorToolbarGroups);if(e)return`${t}:group:${e.id}:${this._editorSecondary.groupGeneration}`;if("plan"===this._mode){const e=this._physicalSel;return e?`${t}:selection:${e.kind}:${e.id}:${e.segment??""}`:`${t}:tool:${this._tool}:${this._path.length}:${this._openWallAnchor?1:0}`}return"decor"===this._mode?"furniture"===this._decorTool?`${t}:palette:furniture:${this._furnPalette?.symbol||"none"}`:"select"===this._decorTool&&this._decorSel?`${t}:selection:decor:${this._decorSel}`:`${t}:tool:${this._decorTool}:${this._bdMoved?1:0}:${this._bdDrag?1:0}`:`${t}:none`}_runEditorContext(t,e){this._editorSecondary.runContext(t,this._editorSecondaryContextId,e)}_renderEditorGroupModel(t){return this._editorSecondary.renderGroupModel(t,this._editorSecondaryContextId,this._editorSecondaryCopy)}_renderDrawWallControl(){return j``}_renderPlanSecondary(){const t=this._editorSecondaryContextId,e=this._physicalSel;if(e){if(!("partition"===e.kind?!!this._curSpaceCfg.partitions?.some(t=>t.id===e.id):"column"===e.kind?!!this._curSpaceCfg.wall_columns?.some(t=>t.id===e.id):!!this._curSpaceCfg.room_drafts?.some(t=>t.id===e.id)))return null;const i="partition"===e.kind?this._t("markup.partition"):"column"===e.kind?this._t("markup.column"):this._t("markup.add");return{contextId:t,kind:"selection",ariaLabel:this._t("editor.context_actions",{object:i}),visibleLabel:i,content:j` `}}const i="draw"===this._tool||"partition"===this._tool||"column"===this._tool,s="partition"===this._tool?"markup.hint_partition":"column"===this._tool?"markup.hint_column":"resize"===this._tool?"markup.hint_resize":"wallthick"===this._tool?"markup.hint_wallthick":"boundary"===this._tool?this._boundaryHintKey:null,n="draw"===this._tool?this._t(this._path.length?"markup.hint_points":"markup.hint_start",this._path.length?{n:this._path.length}:void 0):"";if(!i&&!s&&!n)return null;const o="draw"===this._tool&&this._path.length>0||"boundary"===this._tool&&!!this._openWallAnchor;return{contextId:t,kind:o?"operation":"tool",ariaLabel:this._t("editor.tool_options",{tool:this._t("draw"===this._tool?"markup.add":"partition"===this._tool?"markup.partition":"column"===this._tool?"markup.column":"resize"===this._tool?"markup.resize":"wallthick"===this._tool?"markup.wallthick":"markup.boundary")}),content:j` - ${i?this._renderDrawWallControl():G} - ${n?j`${n}`:G} - ${s?j`${this._t(s)}`:G} + `}}const i="draw"===this._tool||"partition"===this._tool||"column"===this._tool,s="partition"===this._tool?"markup.hint_partition":"column"===this._tool?"markup.hint_column":"resize"===this._tool?"markup.hint_resize":"wallthick"===this._tool?"markup.hint_wallthick":"boundary"===this._tool?this._boundaryHintKey:null,o="draw"===this._tool?this._t(this._path.length?"markup.hint_points":"markup.hint_start",this._path.length?{n:this._path.length}:void 0):"";if(!i&&!s&&!o)return null;const n="draw"===this._tool&&this._path.length>0||"boundary"===this._tool&&!!this._openWallAnchor;return{contextId:t,kind:n?"operation":"tool",ariaLabel:this._t("editor.tool_options",{tool:this._t("draw"===this._tool?"markup.add":"partition"===this._tool?"markup.partition":"column"===this._tool?"markup.column":"resize"===this._tool?"markup.resize":"wallthick"===this._tool?"markup.wallthick":"markup.boundary")}),content:j` + ${i?this._renderDrawWallControl():W} + ${o?j`${o}`:W} + ${s?j`${this._t(s)}`:W} ${"draw"===this._tool&&this._path.length?j``:G}`}}_renderDecorSecondary(){const t=this._editorSecondaryContextId;if("furniture"===this._decorTool)return{contextId:t,kind:"palette",ariaLabel:this._t("editor.palette",{tool:this._t("decor.furniture")}),launcherId:"furniture",dismissPolicy:this._furnPalette?"stay-open-on-canvas":"outside",dismiss:()=>{"furniture"===this._decorTool&&(this._furnPalette=null,this._decorTool="select",this.requestUpdate())},content:this._renderFurnPalette()};const e=this._dtSel;if(e){const i=this._t(`decor.${e.kind}`);return{contextId:t,kind:"selection",ariaLabel:this._t("editor.context_actions",{object:i}),visibleLabel:i,content:j` + `:W}`}}_renderDecorSecondary(){const t=this._editorSecondaryContextId;if("furniture"===this._decorTool)return{contextId:t,kind:"palette",ariaLabel:this._t("editor.palette",{tool:this._t("decor.furniture")}),launcherId:"furniture",dismissPolicy:this._furnPalette?"stay-open-on-canvas":"outside",dismiss:()=>{"furniture"===this._decorTool&&(this._furnPalette=null,this._decorTool="select",this.requestUpdate())},content:this._renderFurnPalette()};const e=this._dtSel;if(e){const i=this._t(`decor.${e.kind}`);return{contextId:t,kind:"selection",ariaLabel:this._t("editor.context_actions",{object:i}),visibleLabel:i,content:j` `}}const i="line"===this._decorTool||"rect"===this._decorTool||"ellipse"===this._decorTool,s="rect"===this._decorTool||"ellipse"===this._decorTool,n="backdrop"===this._decorTool&&!!this._bdRect;return i||n?{contextId:t,kind:"tool",ariaLabel:this._t("editor.tool_options",{tool:this._t(i?`decor.${this._decorTool}`:"decor.backdrop")}),content:j` + `}}const i="line"===this._decorTool||"rect"===this._decorTool||"ellipse"===this._decorTool,s="rect"===this._decorTool||"ellipse"===this._decorTool,o="backdrop"===this._decorTool&&!!this._bdRect;return i||o?{contextId:t,kind:"tool",ariaLabel:this._t("editor.tool_options",{tool:this._t(i?`decor.${this._decorTool}`:"decor.backdrop")}),content:j` ${i?j` this._decorStyle={...this._decorStyle,fillColor:t.detail.color,fillOpacity:t.detail.opacity}}>`:G} - `:G} - ${n?j`${this._t("decor.backdrop_hint")}`:G}`}:null}_withBackdropReset(t){if(!this._bdMoved||this._bdDrag||"palette"===t?.kind)return t;const e=this._editorSecondaryContextId,i=j``;return t?{...t,kind:"selection"===t.kind?"mixed":t.kind,content:j`${t.content}${i}`}:{contextId:e,kind:"tool",ariaLabel:this._t("editor.tool_options",{tool:this._t("decor.backdrop")}),content:i}}get _editorSecondaryDialogBlocked(){return!!(this._tapConfirm||this._vacCalConfirm||this._roomDialog||this._mergeDialog||this._openingDialog||this._physicalDialog||this._openingInfo||this._decorTextDialog||this._decorShapeDialog||this._backdropDialog||this._decorEraseConfirm||this._spaceDialog||this._markerDialog||this._infoCard||this._rulesDialog||this._settingsDialog||this._alignDialog||this._importDialog||this._kioskDialog||this._wallDialog)}_renderEditorSecondary(){if(!this._editing)return G;const t=this._editorSecondary.activeGroup(this._editorToolbarGroups),e=t?this._renderEditorGroupModel(t):"plan"===this._mode?this._renderPlanSecondary():"decor"===this._mode?this._withBackdropReset(this._renderDecorSecondary()):null;return this._editorSecondary.render(e,this._editorSecondaryDialogBlocked)}_renderDecorBar(){const t=[["select","mdi:cursor-default-outline","decor.select"],...this._bdRect?[["backdrop","mdi:image-move","decor.backdrop"]]:[],["line","mdi:vector-line","decor.line"],["rect","mdi:rectangle-outline","decor.rect"],["ellipse","mdi:ellipse-outline","decor.ellipse"],["text","mdi:format-text","decor.text"],["furniture","mdi:sofa-outline","decor.furniture"],["erase","mdi:eraser","decor.erase"]],e=this._geometryHistory.undoName,i=this._geometryHistory.redoName;return j`
+ `;return t?{...t,kind:"selection"===t.kind?"mixed":t.kind,content:j`${t.content}${i}`}:{contextId:e,kind:"tool",ariaLabel:this._t("editor.tool_options",{tool:this._t("decor.backdrop")}),content:i}}get _editorSecondaryDialogBlocked(){return!!(this._tapConfirm||this._vacCalConfirm||this._roomDialog||this._mergeDialog||this._openingDialog||this._physicalDialog||this._openingInfo||this._decorTextDialog||this._decorShapeDialog||this._backdropDialog||this._decorEraseConfirm||this._spaceDialog||this._markerDialog||this._infoCard||this._rulesDialog||this._settingsDialog||this._alignDialog||this._importDialog||this._kioskDialog||this._wallDialog)}_renderEditorSecondary(){if(!this._editing)return W;const t=this._editorSecondary.activeGroup(this._editorToolbarGroups),e=t?this._renderEditorGroupModel(t):"plan"===this._mode?this._renderPlanSecondary():"decor"===this._mode?this._withBackdropReset(this._renderDecorSecondary()):null;return this._editorSecondary.render(e,this._editorSecondaryDialogBlocked)}_renderDecorBar(){const t=[["select","mdi:cursor-default-outline","decor.select"],...this._bdRect?[["backdrop","mdi:image-move","decor.backdrop"]]:[],["line","mdi:vector-line","decor.line"],["rect","mdi:rectangle-outline","decor.rect"],["ellipse","mdi:ellipse-outline","decor.ellipse"],["text","mdi:format-text","decor.text"],["furniture","mdi:sofa-outline","decor.furniture"],["erase","mdi:eraser","decor.erase"]],e=this._geometryHistory.undoName,i=this._geometryHistory.redoName;return j`
${t.map(([t,e,i])=>j`
@@ -3897,13 +3926,13 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f `:G} + `:W} this._decorShapeDialog={...t,...e.detail}}> @@ -3945,7 +3974,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f this._decorShapeDialog={...t,fillColor:e.detail.color,fillOpacity:e.detail.opacity}}>`:G} + @hp-color-opacity-change=${e=>this._decorShapeDialog={...t,fillColor:e.detail.color,fillOpacity:e.detail.opacity}}>`:W}
@@ -3971,54 +4000,54 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f
- `}_cssPxToRender(t){const e=this._stageEl,i=this._viewOr(this._baseVb());return e?.clientWidth&&e.clientHeight?Math.max(i.w/e.clientWidth,i.h/e.clientHeight)*t:this._gridPitch/8*t}get _boundaryCoarse(){return"touch"===this._boundaryPointerType||"pen"===this._boundaryPointerType}_boundaryTolerances(){return{hit:this._cssPxToRender(this._boundaryCoarse?22:12),cap:this._cssPxToRender(this._boundaryCoarse?10:6),ambiguity:this._cssPxToRender(6)}}_boundaryBlocked(t,e){const i=this._spaceModel(),s=i=>Zr(t,i)||i.some((s,n)=>{const o=i[(n+1)%i.length];return hs(t,[s[0],s[1],o[0],o[1]])<=e});for(const t of i.wall_columns||[])if(s(Br(t,this._cellCm,this._gridPitch)))return!0;for(const s of i.partitions||[]){const i=Vo(s.cm,this._cellCm,this._gridPitch)/2;if(hs(t,[s.a[0],s.a[1],s.b[0],s.b[1]])<=Math.max(e,i))return!0}for(const s of i.room_drafts||[])for(let i=0;i+10?Vo(s,this._cellCm,this._gridPitch)/2:0)}_boundaryTargetAt(t){const e=this._viewOr(this._baseVb()),i=this._stageEl,s=[this._space,this._cfgEpoch,t[0],t[1],this._boundaryPointerType,e.x,e.y,e.w,e.h,i?.clientWidth||0,i?.clientHeight||0].join("|");if(this._boundaryTargetMemo?.key===s)return this._boundaryTargetMemo.value;const n=this._boundaryTolerances(),o=this._openCuts();let r=function(t,e,i,s){const{openPull:n,openEndCap:o,solidPull:r,ambiguity:a,eps:l}=s,c=Za(e,l),h=[];for(const e of i||[]){const i=el(t,e,n,o);if(null==i)continue;const s=[(e[0]+e[2])/2,(e[1]+e[3])/2],r=c.find(t=>hs(s,t.seg)<=4*l);h.push({value:e,seg:e,semantic:r?.pair||`open:${e.join(",")}`,distance:i})}const d=sl(h,a,l);if("ambiguous"===d)return{kind:"ambiguous",group:"open"};if(d)return{kind:"open",seg:d.value,distance:d.distance};const p=[];for(const e of c){const i=el(t,e.seg,r(e.seg));null!=i&&p.push({value:e,seg:e.seg,semantic:e.pair,distance:i})}const u=sl(p,a,l);if("ambiguous"===u)return{kind:"ambiguous",group:"shared"};if(u)return{kind:"shared",a:u.value.a,b:u.value.b,edge:u.value.seg,distance:u.distance};const _=[];for(const i of e||[]){if(!i?.id)continue;const e=Fe(i);if(e)for(let s=0;shs(d,t.seg)<=4*l)||_.push({value:{room:i,edge:a},seg:a,semantic:String(i.id),distance:h})}}const m=sl(_,a,l);return"ambiguous"===m?{kind:"ambiguous",group:"outer"}:m?{kind:"outer",...m.value,distance:m.distance}:{kind:"none"}}(t,this._spaceModel().rooms,o,{openPull:n.hit,openEndCap:n.cap,ambiguity:n.ambiguity,eps:.02*this._gridPitch,solidPull:t=>this._solidBoundaryPull(t,o)});return"shared"!==r.kind&&"open"!==r.kind||!this._boundaryBlocked(t,n.hit)||(r={kind:"blocked"}),this._boundaryTargetMemo={key:s,value:r},r}get _boundaryTarget(){return this._markup&&"boundary"===this._tool&&this._cursorPt?this._boundaryTargetAt(this._cursorPt):null}get _boundaryPreview(){const t=this._viewOr(this._baseVb()),e=this._stageEl,i=this._cursorPt,s=this._openWallAnchor,n=[this._markup,this._tool,this._space,this._cfgEpoch,this._boundaryPointerType,i?.[0]??"none",i?.[1]??"none",s?.p?.[0]??"none",s?.p?.[1]??"none",s?.edge?.join(",")??"none",s?.aId??"none",s?.bId??"none",t.x,t.y,t.w,t.h,e?.clientWidth||0,e?.clientHeight||0].join("|");if(this._boundaryPreviewMemo?.key===n)return this._boundaryPreviewMemo.value;const o=t=>(this._boundaryPreviewMemo={key:n,value:t},t);if(!this._markup||"boundary"!==this._tool)return o(null);if(s&&!i)return o({kind:"anchor",point:[...s.p]});if(!i)return o(null);const r=this._openCuts(),a=.02*this._gridPitch;if(s){const{p:t,edge:e}=s,n=Ya(Ja(i,e,ol(e,r,a),this._gridPitch,1.5*this._gridPitch),e),l=this._boundaryBlocked(i,this._boundaryTolerances().hit)||hs(i,e)>this._solidBoundaryPull(e,r);return o({kind:"range",seg:[t[0],t[1],n[0],n[1]],invalid:l})}const l=this._boundaryTargetAt(i);if("shared"===l.kind){return o({kind:"anchor",point:Ja(i,l.edge,ol(l.edge,r,a),this._gridPitch,1.5*this._gridPitch)})}if("open"===l.kind){const t=this._planClosedOpenSpan(l.seg),e=t?Wr([l.seg[0],l.seg[1]],[l.seg[2],l.seg[3]],t.cm,this._cellCm,this._gridPitch):null;return o({kind:"restore",seg:l.seg,body:e})}return"blocked"===l.kind||"ambiguous"===l.kind||"outer"===l.kind?o({kind:"invalid",point:i}):o(null)}get _boundaryHintKey(){if(this._openWallAnchor){const t=this._boundaryPreview;return"range"===t?.kind&&t.invalid?"markup.boundary_hint_retry":"markup.boundary_hint_second"}const t=this._boundaryTarget;return t&&"none"!==t.kind?"open"===t.kind?"markup.boundary_hint_restore":"shared"===t.kind?"markup.boundary_hint_open":"outer"===t.kind?"markup.boundary_hint_outer":"blocked"===t.kind?"markup.boundary_hint_blocked":"markup.boundary_hint_ambiguous":"markup.boundary_hint"}get _boundaryStageClass(){if(!this._markup||"boundary"!==this._tool)return"";if(this._openWallAnchor){const t=this._boundaryPreview;return"range"===t?.kind&&t.invalid?" boundary-invalid":" boundary-solid"}const t=this._boundaryTarget;return"open"===t?.kind?" boundary-open":"shared"===t?.kind?" boundary-solid":t&&"none"!==t.kind?" boundary-invalid":""}_renderOpenWalls(t){if(t&&!t.showBorders&&!this._editing)return W``;const e=this._openCuts(),i=this._boundaryPreview;if(!e.length&&!i)return W``;const s=(t,e=!1)=>e?W` + `}_cssPxToRender(t){const e=this._stageEl,i=this._viewOr(this._baseVb());return e?.clientWidth&&e.clientHeight?Math.max(i.w/e.clientWidth,i.h/e.clientHeight)*t:this._gridPitch/8*t}get _boundaryCoarse(){return"touch"===this._boundaryPointerType||"pen"===this._boundaryPointerType}_boundaryTolerances(){return{hit:this._cssPxToRender(this._boundaryCoarse?22:12),cap:this._cssPxToRender(this._boundaryCoarse?10:6),ambiguity:this._cssPxToRender(6)}}_boundaryBlocked(t,e){const i=this._spaceModel(),s=i=>ra(t,i)||i.some((s,o)=>{const n=i[(o+1)%i.length];return gs(t,[s[0],s[1],n[0],n[1]])<=e});for(const t of i.wall_columns||[])if(s(ta(t,this._cellCm,this._gridPitch)))return!0;for(const s of i.partitions||[]){const i=ir(s.cm,this._cellCm,this._gridPitch)/2;if(gs(t,[s.a[0],s.a[1],s.b[0],s.b[1]])<=Math.max(e,i))return!0}for(const s of i.room_drafts||[])for(let i=0;i+10?ir(s,this._cellCm,this._gridPitch)/2:0)}_boundaryTargetAt(t){const e=this._viewOr(this._baseVb()),i=this._stageEl,s=[this._space,this._cfgEpoch,t[0],t[1],this._boundaryPointerType,e.x,e.y,e.w,e.h,i?.clientWidth||0,i?.clientHeight||0].join("|");if(this._boundaryTargetMemo?.key===s)return this._boundaryTargetMemo.value;const o=this._boundaryTolerances(),n=this._openCuts();let r=function(t,e,i,s){const{openPull:o,openEndCap:n,solidPull:r,ambiguity:a,eps:l}=s,c=rl(e,l),h=[];for(const e of i||[]){const i=hl(t,e,o,n);if(null==i)continue;const s=[(e[0]+e[2])/2,(e[1]+e[3])/2],r=c.find(t=>gs(s,t.seg)<=4*l);h.push({value:e,seg:e,semantic:r?.pair||`open:${e.join(",")}`,distance:i})}const d=pl(h,a,l);if("ambiguous"===d)return{kind:"ambiguous",group:"open"};if(d)return{kind:"open",seg:d.value,distance:d.distance};const p=[];for(const e of c){const i=hl(t,e.seg,r(e.seg));null!=i&&p.push({value:e,seg:e.seg,semantic:e.pair,distance:i})}const u=pl(p,a,l);if("ambiguous"===u)return{kind:"ambiguous",group:"shared"};if(u)return{kind:"shared",a:u.value.a,b:u.value.b,edge:u.value.seg,distance:u.distance};const _=[];for(const i of e||[]){if(!i?.id)continue;const e=Fe(i);if(e)for(let s=0;sgs(d,t.seg)<=4*l)||_.push({value:{room:i,edge:a},seg:a,semantic:String(i.id),distance:h})}}const m=pl(_,a,l);return"ambiguous"===m?{kind:"ambiguous",group:"outer"}:m?{kind:"outer",...m.value,distance:m.distance}:{kind:"none"}}(t,this._spaceModel().rooms,n,{openPull:o.hit,openEndCap:o.cap,ambiguity:o.ambiguity,eps:.02*this._gridPitch,solidPull:t=>this._solidBoundaryPull(t,n)});return"shared"!==r.kind&&"open"!==r.kind||!this._boundaryBlocked(t,o.hit)||(r={kind:"blocked"}),this._boundaryTargetMemo={key:s,value:r},r}get _boundaryTarget(){return this._markup&&"boundary"===this._tool&&this._cursorPt?this._boundaryTargetAt(this._cursorPt):null}get _boundaryPreview(){const t=this._viewOr(this._baseVb()),e=this._stageEl,i=this._cursorPt,s=this._openWallAnchor,o=[this._markup,this._tool,this._space,this._cfgEpoch,this._boundaryPointerType,i?.[0]??"none",i?.[1]??"none",s?.p?.[0]??"none",s?.p?.[1]??"none",s?.edge?.join(",")??"none",s?.aId??"none",s?.bId??"none",t.x,t.y,t.w,t.h,e?.clientWidth||0,e?.clientHeight||0].join("|");if(this._boundaryPreviewMemo?.key===o)return this._boundaryPreviewMemo.value;const n=t=>(this._boundaryPreviewMemo={key:o,value:t},t);if(!this._markup||"boundary"!==this._tool)return n(null);if(s&&!i)return n({kind:"anchor",point:[...s.p]});if(!i)return n(null);const r=this._openCuts(),a=.02*this._gridPitch;if(s){const{p:t,edge:e}=s,o=ol(nl(i,e,_l(e,r,a),this._gridPitch,1.5*this._gridPitch),e),l=this._boundaryBlocked(i,this._boundaryTolerances().hit)||gs(i,e)>this._solidBoundaryPull(e,r);return n({kind:"range",seg:[t[0],t[1],o[0],o[1]],invalid:l})}const l=this._boundaryTargetAt(i);if("shared"===l.kind){return n({kind:"anchor",point:nl(i,l.edge,_l(l.edge,r,a),this._gridPitch,1.5*this._gridPitch)})}if("open"===l.kind){const t=this._planClosedOpenSpan(l.seg),e=t?Qr([l.seg[0],l.seg[1]],[l.seg[2],l.seg[3]],t.cm,this._cellCm,this._gridPitch):null;return n({kind:"restore",seg:l.seg,body:e})}return"blocked"===l.kind||"ambiguous"===l.kind||"outer"===l.kind?n({kind:"invalid",point:i}):n(null)}get _boundaryHintKey(){if(this._openWallAnchor){const t=this._boundaryPreview;return"range"===t?.kind&&t.invalid?"markup.boundary_hint_retry":"markup.boundary_hint_second"}const t=this._boundaryTarget;return t&&"none"!==t.kind?"open"===t.kind?"markup.boundary_hint_restore":"shared"===t.kind?"markup.boundary_hint_open":"outer"===t.kind?"markup.boundary_hint_outer":"blocked"===t.kind?"markup.boundary_hint_blocked":"markup.boundary_hint_ambiguous":"markup.boundary_hint"}get _boundaryStageClass(){if(!this._markup||"boundary"!==this._tool)return"";if(this._openWallAnchor){const t=this._boundaryPreview;return"range"===t?.kind&&t.invalid?" boundary-invalid":" boundary-solid"}const t=this._boundaryTarget;return"open"===t?.kind?" boundary-open":"shared"===t?.kind?" boundary-solid":t&&"none"!==t.kind?" boundary-invalid":""}_renderOpenWalls(t){if(t&&!t.showBorders&&!this._editing)return B``;const e=this._openCuts(),i=this._boundaryPreview;if(!e.length&&!i)return B``;const s=(t,e=!1)=>e?B` - `:W``;return W` - ${e.map(t=>W``:B``;return B` + ${e.map(t=>B``)} - ${"range"===i?.kind?W` ${s([i.seg[0],i.seg[1]])} - ${s([i.seg[2],i.seg[3]],i.invalid)}`:G} - ${"restore"===i?.kind&&i.body?W``${t[0]} ${t[1]}`).join(" L ")} Z`}>`:G} - ${"anchor"===i?.kind?s(i.point):"invalid"===i?.kind?s(i.point,!0):G} - `;var n}_openCuts(){const t=this._curSpaceCfg;return Xa(this._spaceModel().rooms,t?.open_spans,Cc,.02*this._gridPitch)}_openPairs(){const t=this._openCuts();if(!t.length)return[];const e=this._spaceModel().rooms.filter(t=>t.id),i=.02*this._gridPitch,s=[];for(let n=0;n{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return l.some(t=>hs(e,t)<4*i)});c.length&&s.push({a:e[n],b:e[o],segs:c})}return s}_commitOpenSpans(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch;this._cfgEpoch++;const s=this._spaceModel().rooms;let n=Va(e.open_spans);n.length&&t?.old.length?n=al(n,t.old,t.next,Cc):n.length||(n=Qa(Xa(s,null,Cc,i),Cc)),n=rl(n,s,Cc,i),this._persistOpenCuts(n.map(t=>Ba(t,Cc)))}_persistOpenCuts(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch,s=rl(Qa(t,Cc),this._spaceModel().rooms,Cc,i),n=s.map(t=>Ba(t,Cc));s.length?e.open_spans=s:delete e.open_spans,tl(e.rooms||[],this._spaceModel().rooms,n,i)}_planClosedOpenSpan(t){const e=this._curSpaceCfg;if(!e)return null;const i=.02*this._gridPitch,s=this._openCuts();let n=Array.isArray(e.walls)?e.walls.slice():[];for(const t of kr(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc))!t.open&&t.cm>0&&(n=lr(n,t.a,t.b,t.cm,this._wallKeyPitch,Cc));const o=function(t,e,i){const s=[(e[0]+e[2])/2,(e[1]+e[3])/2];return t.filter(t=>{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return Math.hypot(e[0]-s[0],e[1]-s[1])>4*i})}(s,t,i);let r=this._normalizeWalls(n,o),a=xr(this._spaceModel().rooms,r,o,t,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc);if(!(a>0)){const e=[];for(const t of kr(this._spaceModel().rooms,r,o,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc))t.open||e.push([t.a[0],t.a[1],t.b[0],t.b[1]]);r=nl(r,t,e,this._wallKeyPitch,Cc,jo),r=this._normalizeWalls(r,o),a=xr(this._spaceModel().rooms,r,o,t,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc)}return{cuts:o,walls:r,cm:a>0?a:jo}}_closeOpenSpan(t){const e=this._curSpaceCfg;if(!e)return;const i=this._geometrySnapshot(),s=this._planClosedOpenSpan(t);s&&(s.walls.length?e.walls=s.walls:delete e.walls,this._persistOpenCuts(s.cuts),this._showToast(this._t("toast.boundary_restored")),this._recordGeometry(this._t("history.close_boundary"),i),this._saveConfig(),this.requestUpdate())}_boundaryClick(t){const e=this._boundaryRestoreGuard;if(e&&Date.now()>e.until&&(this._boundaryRestoreGuard=null),e&&Date.now()<=e.until&&Math.hypot(t[0]-e.point[0],t[1]-e.point[1])<=this._boundaryTolerances().hit)return;const i=.02*this._gridPitch,s=this._openCuts();if(this._openWallAnchor){const{p:e,edge:l}=this._openWallAnchor;if(hs(t,l)>this._solidBoundaryPull(l,s))return void this._showToast(this._t("toast.boundary_same_edge"));if(this._boundaryBlocked(t,this._boundaryTolerances().hit))return void this._showToast(this._t("toast.boundary_blocked"));const c=Ya(Ja(t,l,ol(l,s,i),this._gridPitch,1.5*this._gridPitch),l),h=Math.hypot(c[0]-e[0],c[1]-e[1]);if(this._openWallAnchor=null,this._cursorPt=null,h<.5*this._gridPitch)return void this._showToast(this._t("toast.openwall_short"));const d=[e[0],e[1],c[0],c[1]],p=[...s,d],u=this._curSpaceCfg;if(!u)return;const _=this._geometrySnapshot(),m=this._normalizeWalls(u.walls,p);m.length?u.walls=m:delete u.walls;const g=(u.openings||[]).length;return u.openings=(n=u.openings,o=d,r=Cc,a=6*this._gridPitch,n?.length?n.filter(t=>hs([Number(t.x)*r,Number(t.y)*r],o)>a||!rr([o[0],o[1]],[o[2],o[3]],Number(t.angle)||0)):n?n.slice():[]),(u.openings||[]).lengththis._pointInRoom(t,e));if(!e)return void this._showToast(this._t("toast.delete_room_pick"));if(!confirm(this._t("confirm.delete_room",{name:e.name})))return;const i=this._curSpaceCfg;if(!i)return;const s=this._geometrySnapshot();i.rooms=i.rooms.filter(t=>t.id!==e.id),this._commitOpenSpans(),this._recordGeometry(this._t("history.delete_room"),s),this._saveConfig(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}get _wallKeyPitch(){return ha}get _spaceWalls(){const t=this._curSpaceCfg?.walls;return Array.isArray(t)?t:[]}_cfgOpenCuts(){return Va(this._curSpaceCfg?.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_intervalCm(t){return xr(this._spaceModel().rooms,this._spaceWalls,this._openCuts(),t,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc)}_normalizeWalls(t,e){return or($r(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc),this._curSpaceCfg?.rooms||[],ha,1,e.map(t=>[t[0]/Cc,t[1]/Cc,t[2]/Cc,t[3]/Cc]))}_paperShapes(t){const e=this._spaceWalls;if(!e.length)return Oe(t);const i=this._openPairs().flatMap(t=>t.segs);return Pr(t,e,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc)}_thickWallCuts(){const t=this._spaceWalls;if(!t.length)return[];const e=this._openPairs().flatMap(t=>t.segs);return function(t,e,i,s,n,o,r=1){if(!e?.length)return[];const a=new Set,l=[];for(const c of t||[]){if(!c?.id)continue;const h=wr(t,c.id,e,i,s,n,o,r);if(!h)continue;const d=h.poly;for(let t=0;t0))continue;const u=Zo(e,i,s,r);if(a.has(u))continue;a.add(u);const _=Vo(p,n,o),[m,g]=ur(d,t),f=-m,v=-g,y=_/2,b=[[e[0]+f*y,e[1]+v*y],[i[0]+f*y,i[1]+v*y],[i[0]+m*y,i[1]+g*y],[e[0]+m*y,e[1]+g*y]];l.push({key:u,kind:c,cm:p,quad:b,a:[e[0],e[1]],b:[i[0],i[1]],depthUnits:_})}}return l}(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_wallThickHit(t){const e=6*this._gridPitch,i=this._openCuts();let s=null;for(const n of kr(this._spaceModel().rooms,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc)){const i=hs(t,[n.a[0],n.a[1],n.b[0],n.b[1]]);i<=e&&(!s||i0?Vo(e,this._cellCm,this._gridPitch):3*this._gridPitch,s=Math.max(i/2,1.25*this._gridPitch);let n="";for(const e of t.segs)n+=(n?" ":"")+hr([[e[0],e[1]],[e[2],e[3]]],s,!1);return{segs:t.segs,open:t.open,d:n}}_wallThickClick(t){const e=this._wallThickHit(t);if(!e)return void this._showToast(this._t("toast.wallthick_pick"));if(e.open)return void this._showToast(this._t("toast.wallthick_open"));const i=e.cm,s=this._viewOr(this._baseVb()),n=(e.a[0]+e.b[0])/2,o=(e.a[1]+e.b[1])/2;this._wallDialog={a:e.a,b:e.b,value:Go(i,this._imperial),roomId:e.roomId,sx:(n-s.x)/s.w*100,sy:(o-s.y)/s.h*100}}_wallThickApply(t){const e=this._wallDialog;if(!e)return;const i=this._curSpaceCfg;if(!i)return;const s=e.value.trim(),n=s?pc(s):0;if(null==n)return void this._showPhysicalRange(100);const o=this._imperial?2.54*n:n;if(s&&(!Number.isFinite(o)||o<0||o>0&&o<1||o>100))return void this._showPhysicalRange(100);const r=this._geometrySnapshot(),a=s&&o>0?o:null,l=this._openCuts();let c;c=t&&e.roomId?cr(i.walls,this._spaceModel().rooms,e.roomId,a,this._wallKeyPitch,l,Cc):lr(i.walls,e.a,e.b,a,this._wallKeyPitch,Cc),c=this._normalizeWalls(c,l),c.length?i.walls=c:delete i.walls,this._wallDialog=null,this._showToast(this._t(null==a?"toast.wallthick_cleared":"toast.wallthick_set")),this._recordGeometry(this._t("history.wall_thickness"),r),this._saveConfig(),this.requestUpdate()}_wallHatchDefs(t){if(!this._spaceWalls.length&&!this._physicalBodiesR().length&&!this._markup)return W``;const e=Math.max(.4,1/Math.max(this._zoom,.4)),i=t||"#607d8b";return W` + ${s([i.seg[2],i.seg[3]],i.invalid)}`:W} + ${"restore"===i?.kind&&i.body?B``${t[0]} ${t[1]}`).join(" L ")} Z`}>`:W} + ${"anchor"===i?.kind?s(i.point):"invalid"===i?.kind?s(i.point,!0):W} + `;var o}_openCuts(){const t=this._curSpaceCfg;return al(this._spaceModel().rooms,t?.open_spans,Nc,.02*this._gridPitch)}_openPairs(){const t=this._openCuts();if(!t.length)return[];const e=this._spaceModel().rooms.filter(t=>t.id),i=.02*this._gridPitch,s=[];for(let o=0;o{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return l.some(t=>gs(e,t)<4*i)});c.length&&s.push({a:e[o],b:e[n],segs:c})}return s}_commitOpenSpans(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch;this._cfgEpoch++;const s=this._spaceModel().rooms;let o=il(e.open_spans);o.length&&t?.old.length?o=gl(o,t.old,t.next,Nc):o.length||(o=ll(al(s,null,Nc,i),Nc)),o=ml(o,s,Nc,i),this._persistOpenCuts(o.map(t=>tl(t,Nc)))}_persistOpenCuts(t){const e=this._curSpaceCfg;if(!e)return;const i=.02*this._gridPitch,s=ml(ll(t,Nc),this._spaceModel().rooms,Nc,i),o=s.map(t=>tl(t,Nc));s.length?e.open_spans=s:delete e.open_spans,cl(e.rooms||[],this._spaceModel().rooms,o,i)}_planClosedOpenSpan(t){const e=this._curSpaceCfg;if(!e)return null;const i=.02*this._gridPitch,s=this._openCuts();let o=Array.isArray(e.walls)?e.walls.slice():[];for(const t of Rr(this._spaceModel().rooms,o,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc))!t.open&&t.cm>0&&(o=fr(o,t.a,t.b,t.cm,this._wallKeyPitch,Nc));const n=function(t,e,i){const s=[(e[0]+e[2])/2,(e[1]+e[3])/2];return t.filter(t=>{const e=[(t[0]+t[2])/2,(t[1]+t[3])/2];return Math.hypot(e[0]-s[0],e[1]-s[1])>4*i})}(s,t,i);let r=this._normalizeWalls(o,n),a=Ar(this._spaceModel().rooms,r,n,t,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc);if(!(a>0)){const e=[];for(const t of Rr(this._spaceModel().rooms,r,n,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc))t.open||e.push([t.a[0],t.a[1],t.b[0],t.b[1]]);r=ul(r,t,e,this._wallKeyPitch,Nc,Xn),r=this._normalizeWalls(r,n),a=Ar(this._spaceModel().rooms,r,n,t,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc)}return{cuts:n,walls:r,cm:a>0?a:Xn}}_closeOpenSpan(t){const e=this._curSpaceCfg;if(!e)return;const i=this._geometrySnapshot(),s=this._planClosedOpenSpan(t);s&&(s.walls.length?e.walls=s.walls:delete e.walls,this._persistOpenCuts(s.cuts),this._showToast(this._t("toast.boundary_restored")),this._recordGeometry(this._t("history.close_boundary"),i),this._saveConfig(),this.requestUpdate())}_boundaryClick(t){const e=this._boundaryRestoreGuard;if(e&&Date.now()>e.until&&(this._boundaryRestoreGuard=null),e&&Date.now()<=e.until&&Math.hypot(t[0]-e.point[0],t[1]-e.point[1])<=this._boundaryTolerances().hit)return;const i=.02*this._gridPitch,s=this._openCuts();if(this._openWallAnchor){const{p:e,edge:l}=this._openWallAnchor;if(gs(t,l)>this._solidBoundaryPull(l,s))return void this._showToast(this._t("toast.boundary_same_edge"));if(this._boundaryBlocked(t,this._boundaryTolerances().hit))return void this._showToast(this._t("toast.boundary_blocked"));const c=ol(nl(t,l,_l(l,s,i),this._gridPitch,1.5*this._gridPitch),l),h=Math.hypot(c[0]-e[0],c[1]-e[1]);if(this._openWallAnchor=null,this._cursorPt=null,h<.5*this._gridPitch)return void this._showToast(this._t("toast.openwall_short"));const d=[e[0],e[1],c[0],c[1]],p=[...s,d],u=this._curSpaceCfg;if(!u)return;const _=this._geometrySnapshot(),m=this._normalizeWalls(u.walls,p);m.length?u.walls=m:delete u.walls;const g=(u.openings||[]).length;return u.openings=(o=u.openings,n=d,r=Nc,a=6*this._gridPitch,o?.length?o.filter(t=>gs([Number(t.x)*r,Number(t.y)*r],n)>a||!mr([n[0],n[1]],[n[2],n[3]],Number(t.angle)||0)):o?o.slice():[]),(u.openings||[]).lengththis._pointInRoom(t,e));if(!e)return void this._showToast(this._t("toast.delete_room_pick"));if(!confirm(this._t("confirm.delete_room",{name:e.name})))return;const i=this._curSpaceCfg;if(!i)return;const s=this._geometrySnapshot();i.rooms=i.rooms.filter(t=>t.id!==e.id),this._commitOpenSpans(),this._recordGeometry(this._t("history.delete_room"),s),this._saveConfig(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}get _wallKeyPitch(){return ya}get _spaceWalls(){const t=this._curSpaceCfg?.walls;return Array.isArray(t)?t:[]}_cfgOpenCuts(){return il(this._curSpaceCfg?.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_intervalCm(t){return Ar(this._spaceModel().rooms,this._spaceWalls,this._openCuts(),t,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc)}_normalizeWalls(t,e){return _r(zr(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc),this._curSpaceCfg?.rooms||[],ya,1,e.map(t=>[t[0]/Nc,t[1]/Nc,t[2]/Nc,t[3]/Nc]))}_paperShapes(t){const e=this._spaceWalls;if(!e.length)return Oe(t);const i=this._openPairs().flatMap(t=>t.segs);return Lr(t,e,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc)}_thickWallCuts(){const t=this._spaceWalls;if(!t.length)return[];const e=this._openPairs().flatMap(t=>t.segs);return function(t,e,i,s,o,n,r=1){if(!e?.length)return[];const a=new Set,l=[];for(const c of t||[]){if(!c?.id)continue;const h=Pr(t,c.id,e,i,s,o,n,r);if(!h)continue;const d=h.poly;for(let t=0;t0))continue;const u=rr(e,i,s,r);if(a.has(u))continue;a.add(u);const _=ir(p,o,n),[m,g]=kr(d,t),f=-m,v=-g,y=_/2,b=[[e[0]+f*y,e[1]+v*y],[i[0]+f*y,i[1]+v*y],[i[0]+m*y,i[1]+g*y],[e[0]+m*y,e[1]+g*y]];l.push({key:u,kind:c,cm:p,quad:b,a:[e[0],e[1]],b:[i[0],i[1]],depthUnits:_})}}return l}(this._spaceModel().rooms,t,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]])}_wallThickHit(t){const e=6*this._gridPitch,i=this._openCuts();let s=null;for(const o of Rr(this._spaceModel().rooms,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc)){const i=gs(t,[o.a[0],o.a[1],o.b[0],o.b[1]]);i<=e&&(!s||i0?ir(e,this._cellCm,this._gridPitch):3*this._gridPitch,s=Math.max(i/2,1.25*this._gridPitch);let o="";for(const e of t.segs)o+=(o?" ":"")+yr([[e[0],e[1]],[e[2],e[3]]],s,!1);return{segs:t.segs,open:t.open,d:o}}_wallThickClick(t){const e=this._wallThickHit(t);if(!e)return void this._showToast(this._t("toast.wallthick_pick"));if(e.open)return void this._showToast(this._t("toast.wallthick_open"));const i=e.cm,s=this._viewOr(this._baseVb()),o=(e.a[0]+e.b[0])/2,n=(e.a[1]+e.b[1])/2;this._wallDialog={a:e.a,b:e.b,value:er(i,this._imperial),roomId:e.roomId,sx:(o-s.x)/s.w*100,sy:(n-s.y)/s.h*100}}_wallThickApply(t){const e=this._wallDialog;if(!e)return;const i=this._curSpaceCfg;if(!i)return;const s=e.value.trim(),o=s?wc(s):0;if(null==o)return void this._showPhysicalRange(100);const n=this._imperial?2.54*o:o;if(s&&(!Number.isFinite(n)||n<0||n>0&&n<1||n>100))return void this._showPhysicalRange(100);const r=this._geometrySnapshot(),a=s&&n>0?n:null,l=this._openCuts();let c;c=t&&e.roomId?vr(i.walls,this._spaceModel().rooms,e.roomId,a,this._wallKeyPitch,l,Nc):fr(i.walls,e.a,e.b,a,this._wallKeyPitch,Nc),c=this._normalizeWalls(c,l),c.length?i.walls=c:delete i.walls,this._wallDialog=null,this._showToast(this._t(null==a?"toast.wallthick_cleared":"toast.wallthick_set")),this._recordGeometry(this._t("history.wall_thickness"),r),this._saveConfig(),this.requestUpdate()}_wallHatchDefs(t){if(!this._spaceWalls.length&&!this._physicalBodiesR().length&&!this._markup)return B``;const e=Math.max(.4,1/Math.max(this._zoom,.4)),i=t||"#607d8b";return B` - `}_resolvedRoomFills(t,e){const i=new Map,s=new Map;for(const n of t.rooms){const t=this._roomDialog&&n.id===this._roomEditId?this._roomFill||e.fill:os(e.fill,n),o=this._roomDialog&&n.id===this._roomEditId?this._roomCustomFill||e.customFill:Ni(e.customFill,n),r=Ui(t,"lqi"===t&&n.area?this._roomLqi(n.area):null,"light"===t?ln(an(this._planHass,this._devices,n)):"none","temp"===t?this._roomTemp(n):null,e.tempMin,e.tempMax,this._fillColors,o);i.set(n,r),n.id&&s.set(n.id,r)}return{byRoom:i,byId:s}}_spaceDisplayForRender(){const t=Fi(this._curSpaceCfg),e=this._spaceDialog;return e&&"edit"===e.mode&&e.spaceId===this._space?{...t,showBorders:e.showBorders,showNames:e.showNames,hideDecor:e.hideDecor,hideOpenings:e.hideOpenings,color:e.roomColor,opacity:e.roomOpacity,fill:e.fillMode,customFill:e.customFill?Ei(e.customFill):Ii,glow:e.glowEnabled,tempMin:e.tempMin,tempMax:e.tempMax,showLqi:e.showLqi,cardFontScale:e.cardFontScale,labelTemp:e.labelTemp,labelHum:e.labelHum,labelLqi:e.labelLqi,labelLight:e.labelLight}:t}_openingWallIndexFor(t,e){const i=t.rooms.map(t=>`${t.id}:${t.poly?.map(t=>t.join(",")).join("/")||`${t.x},${t.y},${t.w},${t.h}`}`).join(";"),s=this._spaceWalls.map(t=>`${t.key}:${t.a?.join(",")||""}:${t.b?.join(",")||""}:${t.cm}`).join(";"),n=e.map(t=>t.join(",")).join(";"),o=[t.id,this._cfgEpoch,this._wallKeyPitch,this._cellCm,this._gridPitch,i,s,n].join("|");return this._openingWallIndexCache&&this._openingWallIndexCache.key===o||(this._openingWallIndexCache={key:o,value:Rr(t.rooms,this._spaceWalls,e,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc)}),this._openingWallIndexCache}_renderOpeningTunnelFills(t,e,i="data"){if(this._markup||!this._spaceWalls.length||!this._openingsR.length)return W``;if("glow-base"===i&&![...e.byRoom.values()].some(Boolean))return W``;const s=this._openPairs().flatMap(t=>t.segs),n=this._openingsR.map(t=>({x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),o=n.map(t=>`${t.x},${t.y},${t.angle},${t.length}`).join(";"),r=this._openingWallIndexFor(t,s),a=`${r.key}|${o}`;return this._openingTunnelCache&&this._openingTunnelCache.key===a||(this._openingTunnelCache={key:a,value:Or(r.value,n)}),function({openings:t,geometries:e,fillsByRoomId:i,idPrefix:s="data",groupClass:n="opening-tunnels",dataLayer:o="data"}){const r=t.map((t,n)=>{const o=e[n];if(!o)return G;const r=o.faces.find(t=>-1===t.side),a=o.faces.find(t=>1===t.side);if(!r||!a)return G;const l=i.get(r.roomId)||null,c=i.get(a.roomId)||null;if(!l&&!c)return G;const h=`${r.d} ${a.d}`,d=!!l&&!!c&&l.color===c.color&&l.opacity===c.opacity,p=`translate(${t.rx} ${t.ry}) rotate(${t.angle})`;if(d)return W``;const u=n.maxY-n.minY;if(!(u>0))return W;const _=`${(100*Math.max(0,Math.min(1,-n.minY/u))).toFixed(6)}%`,m=l||{color:"#000000",opacity:0},g=c||{color:"#000000",opacity:0},f=`hp-opening-tunnel-${s.replace(/[^a-zA-Z0-9_-]/g,"-")}-${o}`;return B``});return W``}({openings:this._openingsR,geometries:this._openingTunnelCache.value,fillsByRoomId:e.byId,idPrefix:`${t.id}-${i}`,groupClass:"data"===i?"opening-tunnels":"opening-tunnels glow-base-tunnels",dataLayer:i})}_resolvedGlowBase(t,e,i){const s=new Map,n=new Map,o=this._fillColors.glow_base;for(const r of t.rooms){const t=i.byRoom.get(r),a=rs(e.glow,r)&&(!t||t.opacity<=0)?{color:o.c,opacity:o.a,mode:"glow"}:null;s.set(r,a),r.id&&n.set(r.id,a)}return{byRoom:s,byId:n}}_renderGlowBaseRooms(t,e){if(this._markup||![...e.byRoom.values()].some(Boolean))return W``;const i=new Map(t.rooms.map(t=>[t,Fe(t)])),s=this._openPairs().flatMap(t=>t.segs),n=t=>"M "+t.map(t=>t[0]+" "+t[1]).join(" L ")+" Z",o=t.rooms.map(o=>{const r=e.byRoom.get(o)||null,a=i.get(o)||null;if(!r||!a)return G;const l=this._spaceWalls.length&&o.id&&Mr(t.rooms,o.id,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc)||a,c=ii(l,t.rooms.filter(t=>t!==o).map(t=>i.get(t)).filter(t=>!!t)),h=this._cleanFloor(o,l,t).path;return h||c.length?W``});return B``}({openings:this._openingsR,geometries:this._openingTunnelCache.value,fillsByRoomId:e.byId,idPrefix:`${t.id}-${i}`,groupClass:"data"===i?"opening-tunnels":"opening-tunnels glow-base-tunnels",dataLayer:i})}_resolvedGlowBase(t,e,i){const s=new Map,o=new Map,n=this._fillColors.glow_base;for(const r of t.rooms){const t=i.byRoom.get(r),a=ps(e.glow,r)&&(!t||t.opacity<=0)?{color:n.c,opacity:n.a,mode:"glow"}:null;s.set(r,a),r.id&&o.set(r.id,a)}return{byRoom:s,byId:o}}_renderGlowBaseRooms(t,e){if(this._markup||![...e.byRoom.values()].some(Boolean))return B``;const i=new Map(t.rooms.map(t=>[t,Fe(t)])),s=this._openPairs().flatMap(t=>t.segs),o=t=>"M "+t.map(t=>t[0]+" "+t[1]).join(" L ")+" Z",n=t.rooms.map(n=>{const r=e.byRoom.get(n)||null,a=i.get(n)||null;if(!r||!a)return W;const l=this._spaceWalls.length&&n.id&&Er(t.rooms,n.id,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc)||a,c=ii(l,t.rooms.filter(t=>t!==n).map(t=>i.get(t)).filter(t=>!!t)),h=this._cleanFloor(n,l,t).path;return h||c.length?B``:o.poly||l!==a?W``:n.poly||l!==a?B``:W``});return W``}_renderSvgRoomLabels(t,e){return t.bg||e.showNames||this._markup?W``:W`${t.rooms.map(t=>{const e=this._roomCenter(t);return W`${t.name}`})}`}_renderWallBodies(t){if(t&&!t.showBorders&&("view"===this._mode||"devices"===this._mode))return W``;const e=this._spaceWalls,i=this._physicalBodiesR();if(!e.length&&!i.length)return W``;const s=this._openPairs().flatMap(t=>t.segs),n=(this._curSpaceCfg?.openings||[]).map(t=>({x:Number(t.x)*Cc,y:Number(t.y)*Cc,angle:Number(t.angle)||0,length:(Number(t.length)>0?Number(t.length):.9)*Cc})),o=`${this._space}|${this._cfgEpoch}|${this._spaceModel().rooms.length}`;this._wallUnionCache&&this._wallUnionCache.key===o||(this._wallUnionCache={key:o,value:Dr(this._spaceModel().rooms,e,s,n,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc,i)});const r=this._wallUnionCache.value;if(!r)return W``;const a=this._stageEl,l=this._viewOr(this._baseVb()),c=a&&a.clientWidth&&l.w?a.clientWidth/l.w:1,h=t?.color||"#607d8b",d=Wo(r.depthUnits,c),p=this._fillColors.wall_fill;return W` + fill=${r.color} fill-opacity=${r.opacity} pointer-events="none">`:B``});return B``}_renderSvgRoomLabels(t,e){return t.bg||e.showNames||this._markup?B``:B`${t.rooms.map(t=>{const e=this._roomCenter(t);return B`${t.name}`})}`}_renderWallBodies(t){if(t&&!t.showBorders&&("view"===this._mode||"devices"===this._mode))return B``;const e=this._spaceWalls,i=this._physicalBodiesR();if(!e.length&&!i.length)return B``;const s=this._openPairs().flatMap(t=>t.segs),o=(this._curSpaceCfg?.openings||[]).map(t=>({x:Number(t.x)*Nc,y:Number(t.y)*Nc,angle:Number(t.angle)||0,length:(Number(t.length)>0?Number(t.length):.9)*Nc})),n=`${this._space}|${this._cfgEpoch}|${this._spaceModel().rooms.length}`;this._wallUnionCache&&this._wallUnionCache.key===n||(this._wallUnionCache={key:n,value:Fr(this._spaceModel().rooms,e,s,o,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc,i)});const r=this._wallUnionCache.value;if(!r)return B``;const a=this._stageEl,l=this._viewOr(this._baseVb()),c=a&&a.clientWidth&&l.w?a.clientWidth/l.w:1,h=t?.color||"#607d8b",d=Qn(r.depthUnits,c),p=this._fillColors.wall_fill;return B` @@ -4026,8 +4055,8 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f data-hp="wall" data-id="union" data-kind="union" d="${r.d}" fill="${d?"none":"url(#hp-wall-hatch)"}" fill-rule=${r.fillRule} stroke="${h}" stroke-width="0.6" pointer-events="none"> - `}_renderRoomHover(t){const e=this._hoverRoom;if("view"!==this._mode||!e||e.space!==t.id)return W``;const i=t.rooms.find(t=>t===e.room||!!t.id&&t.id===e.room.id);if(!i)return W``;const s=Fe(i);if(!s)return W``;const n=t.rooms.filter(t=>t!==i).map(t=>({room:t,poly:Fe(t)})).filter(t=>!!t.poly),o=ii(s,n.map(t=>t.poly)),r=this._openPairs(),a=r.flatMap(t=>t.segs),l=i.id?r.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):r.flatMap(t=>t.segs),c=this._spaceWalls,h=[{axis:s,face:c.length&&i.id&&Mr(t.rooms,i.id,c,a,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc)||s}];for(const e of o){const i=n.find(t=>t.poly===e)?.room;let s=e;if(c.length&&i?.id){const n=wr(t.rooms,i.id,c,a,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc);n&&(s=Tr(n.poly,n.offsets)||e)}h.push({axis:e,face:s})}const d=this._openingsR.map(t=>{const e=t.angle*Math.PI/180,i=Math.cos(e)*t.rlen/2,s=Math.sin(e)*t.rlen/2;return[t.rx-i,t.ry-s,t.rx+i,t.ry+s]}),p=.02*this._gridPitch,u=l.concat(d),_=h.map(({axis:t,face:e})=>{const i=u.map(i=>((t,e,i)=>{const s=t[2]-t[0],n=t[3]-t[1],o=Math.hypot(s,n);if(o.05)&&hs([l,c],[i[0],i[1],s[0],s[1]])<=4*p){h=!0;break}}if(!h)return null;let d=null;for(let t=0;t.05)continue;const u=hs([l,c],[e[0],e[1],s[0],s[1]]);(!d||u!!t);return i.length?ts(e,i,p).map(t=>`M ${t[0]} ${t[1]} L ${t[2]} ${t[3]}`).join(" "):`M ${e.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`}).filter(Boolean).join(" ");return _?W``:W``}_renderWallThickUi(){const t=this._wallThickHover;return t&&t.d?W``:W``}_renderWallThickDialog(){const t=this._wallDialog;return t?j`
`}_renderRoomHover(t){const e=this._hoverRoom;if("view"!==this._mode||!e||e.space!==t.id)return B``;const i=t.rooms.find(t=>t===e.room||!!t.id&&t.id===e.room.id);if(!i)return B``;const s=Fe(i);if(!s)return B``;const o=t.rooms.filter(t=>t!==i).map(t=>({room:t,poly:Fe(t)})).filter(t=>!!t.poly),n=ii(s,o.map(t=>t.poly)),r=this._openPairs(),a=r.flatMap(t=>t.segs),l=i.id?r.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):r.flatMap(t=>t.segs),c=this._spaceWalls,h=[{axis:s,face:c.length&&i.id&&Er(t.rooms,i.id,c,a,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc)||s}];for(const e of n){const i=o.find(t=>t.poly===e)?.room;let s=e;if(c.length&&i?.id){const o=Pr(t.rooms,i.id,c,a,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc);o&&(s=Or(o.poly,o.offsets)||e)}h.push({axis:e,face:s})}const d=this._openingsR.map(t=>{const e=t.angle*Math.PI/180,i=Math.cos(e)*t.rlen/2,s=Math.sin(e)*t.rlen/2;return[t.rx-i,t.ry-s,t.rx+i,t.ry+s]}),p=.02*this._gridPitch,u=l.concat(d),_=h.map(({axis:t,face:e})=>{const i=u.map(i=>((t,e,i)=>{const s=t[2]-t[0],o=t[3]-t[1],n=Math.hypot(s,o);if(n.05)&&gs([l,c],[i[0],i[1],s[0],s[1]])<=4*p){h=!0;break}}if(!h)return null;let d=null;for(let t=0;t.05)continue;const u=gs([l,c],[e[0],e[1],s[0],s[1]]);(!d||u!!t);return i.length?rs(e,i,p).map(t=>`M ${t[0]} ${t[1]} L ${t[2]} ${t[3]}`).join(" "):`M ${e.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`}).filter(Boolean).join(" ");return _?B``:B``}_renderWallThickUi(){const t=this._wallThickHover;return t&&t.d?B``:B``}_renderWallThickDialog(){const t=this._wallDialog;return t?j`
t.stopPropagation()}>
@@ -4045,7 +4074,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f
-
`:j``}_openingClick(t){const e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return void this._editOpening(i);const s=He(t,this._spaceModel().rooms,e);if(!s)return void this._showToast(this._t("toast.opening_no_wall"));if(function(t,e,i,s,n){for(const o of s)if(!(hs([t,e],o)>n)&&rr([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(s.x,s.y,s.angle,this._openCuts(),e))return void this._showToast(this._t("toast.opening_on_virtual"));const n=this._opRuler(s,this._cmToUnits(90));this._openingDialog={type:"door",lengthCm:90,contact:"",lock:"",invert:!1,flipH:!1,flipV:!1,x:n.x,y:n.y,angle:n.angle},this._cursorPt=null}_editOpening(t){this._openingDialog={id:t.id,type:t.type,lengthCm:Math.round(t.rlen/this._gridPitch*this._cellCm),contact:t.contact||"",lock:t.lock||"",invert:!!t.invert,flipH:!!t.flip_h,flipV:!!t.flip_v,x:t.rx,y:t.ry,angle:t.angle}}_opPointerDown(t,e){if("plan"===this._mode&&"resize"!==this._tool){t.preventDefault(),t.stopPropagation();try{Ac(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;if(Math.abs(t.clientX-this._opDrag.sx)+Math.abs(t.clientY-this._opDrag.sy)<=3)return;const i=He(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,n=s?.openings?.find(t=>t.id===e.id);if(!n)return;const o=this._opRuler(i,n.length*Cc);this._opMeasure=o.measure;const r=o.x/Cc,a=o.y/this._spaceH,l=n.x!==r||n.y!==a||n.angle!==i.angle;l&&(this._opDrag.dirty=!0),n.x=r,n.y=a,n.angle=i.angle,l&&this._cfgEpoch++,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel().rooms,s=this._gridPitch/2;let n=t.x,o=t.y,r=qe([n,o],t.angle,e,i,s);if(r&&r.centered&&(n!==r.wallCenter[0]||o!==r.wallCenter[1]))[n,o]=r.wallCenter,r=qe([n,o],t.angle,e,i,s);else if(r){const[a,l]=r.wallA,[c,h]=r.wallB,d=c-a,p=h-l,u=Math.hypot(d,p);if(u>0){const c=this._gridPitch,h=Math.min(e/2,u/2);let _=Math.round(((n-a)*d+(o-l)*p)/u/c)*c;_=Math.max(h,Math.min(u-h,_)),n=a+_/u*d,o=l+_/u*p,r=qe([n,o],t.angle,e,i,s)||r}}if(!r)return{x:n,y:o,angle:t.angle,measure:null};const a="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Ee(t/this._gridPitch*this._cellCm,a)});return{x:n,y:o,angle:t.angle,measure:{labels:[l(r.sideA,r.midA),l(r.sideB,r.midB)],guide:r.centered?{x:r.wallCenter[0],y:r.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;const i=this._opDrag,s=i.moved;this._opMeasure=null,s&&i.dirty&&(this._recordGeometry(this._t("history.move_opening"),i.before),this._saveConfig()),s?window.setTimeout(()=>this._opDrag=null,0):this._opDrag=null}_opClick(t,e){"plan"===this._mode&&"resize"===this._tool||(t.stopPropagation(),this._opDrag?.moved||"plan"===this._mode&&this._editOpening(e))}_saveOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Cc,y:t.y/s,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/Cc,contact:t.contact||null,lock:"window"!==t.type&&t.lock||null,invert:t.invert||void 0,flip_h:"gate"!==t.type&&t.flipH||void 0,flip_v:t.flipV||void 0};e.openings=e.openings||[];const o=e.openings.findIndex(t=>t.id===n.id);o>=0?e.openings[o]=n:e.openings.push(n),this._recordGeometry(this._t(t.id?"history.edit_opening":"history.add_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t?.id||!e?.openings)return;const i=this._geometrySnapshot();e.openings=e.openings.filter(e=>e.id!==t.id),this._recordGeometry(this._t("history.delete_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){if(!this._planEntityAvailable(e))continue;const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&t.push([e,s?.attributes?.friendly_name||e,n?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")&&this._planEntityAvailable(t)).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_mergeClick(t){const e=this._spaceModel().rooms,i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const s=i.id;if(!this._mergeSel||this._mergeSel===s)return void(this._mergeSel=this._mergeSel===s?null:s);const n=e.find(t=>t.id===this._mergeSel),o=n?Fe(n):null,r=Fe(i),a=o&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=Pe(ni(t),ni(e));if(1!==i.length)return null;if(1!==i[0].length)return null;const s=i[0][0].slice(0,-1).map(t=>[t[0],t[1]]);return s.length>=3?s:null}(o,r):null;if(!a)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:s,poly:a,pick:"a"},this._mergeSel=null}_commitMerge(){const t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n="a"===t.pick?t.aId:t.bId,o="a"===t.pick?t.bId:t.aId,r=e.rooms.find(t=>t.id===n);r?(r.poly=t.poly.map(t=>[t[0]/Cc,t[1]/s]),delete r.x,delete r.y,delete r.w,delete r.h,e.rooms=e.rooms.filter(t=>t.id!==o),this._commitOpenSpans(),this._recordGeometry(this._t("history.merge_rooms"),i),this._saveConfig(),this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.rooms_merged",{name:r.name||""}))):this._mergeDialog=null}_splitClick(t){const e=this._spaceModel().rooms;if(!this._splitSel){const i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;return void(this._splitSel={roomId:i.id,pts:[]})}const i=e.find(t=>t.id===this._splitSel.roomId),s=i?Fe(i):null;if(!i||!s)return void(this._splitSel=null);const n=.02*this._gridPitch,o=6*this._gridPitch,r=Ge(t,s),a=r&&function(t,e,i){let s=null,n=1/0;for(let o=0;o=n)continue;n=_;const m=Math.sqrt(p),g=(i>0?Math.max(0,Math.min(m,Math.round(u*m/i)*i)):u*m)/m;s=[r+g*h,a+g*d]}return s}(r,s,this._gridPitch)||r,l=r&&a&&Math.hypot(r[0]-t[0],r[1]-t[1])<=o?a:null,c=!!l&&Ve(l,s,n),h=this._splitSel.pts;if(!h.length)return c?void(this._splitSel={...this._splitSel,pts:[l]}):void this._showToast(this._t("toast.split_pick_wall"));if(!c){const e=this._snap(t);return Ke(e,s,n)?void(this._splitSel={...this._splitSel,pts:[...h,e]}):void this._showToast(this._t("toast.split_pick_inside"))}const d=function(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(je(s,n,i))return null;const o=oi(t,s,i),r=oi(t,n,i);if(o<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!Ke(e,t,i))return null;for(let i=0;i{const r=[e];let a=(s+1)%t.length;for(let e=0;e<=t.length&&(r.push(t[a]),a!==o);e++)a=(a+1)%t.length;return r.push(n),ri(r,i)};let c,h;if(o===r){const r=ri([...e],i);if(r.length<3||si(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}c=ri(a,i),h=r}else c=ri([...l(s,o,n,r),...[...a].reverse()],i),h=ri([...l(n,r,s,o),...a],i);return c.length<3||h.length<3||si(c)<=i||si(h)<=i||Math.abs(si(c)+si(h)-si(t))>Math.max(i,1e-6*si(t))?null:[c,h]}(s,[...h,l],n);if(!d)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[p,u]=d,_=si(p)>=si(u)?p:u,m=_===p?u:p;this._pendingSplit={roomId:i.id,mainPoly:_,newPoly:m},this._cursorPt=null,this._nameSel="",this._areaSel="",this._roomDialog=!0}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])}_markupMove(t){if(!this._markup)return;const e=t.pointerType;if(e&&(this._boundaryPointerType=e),"column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"boundary"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const i=("draw"===this._tool||"partition"===this._tool)&&this._path.length&&!this._contourClosed,s="split"===this._tool&&!!this._splitSel?.pts?.length;if(!i&&!s)return;const n=this._svgPoint(t);this._cursorPt=i?this._snapDrawPoint(n,t.shiftKey):this._snap(n)}get _openingPreview(){if("opening"!==this._tool||!this._cursorPt)return null;const t=this._cursorPt,e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return null;const s=this._cmToUnits(90),n=He(t,this._spaceModel().rooms,e);if(!n)return null;const o=this._opRuler(n,s);return{x:o.x,y:o.y,angle:o.angle,rlen:s,measure:o.measure}}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){(this._areaSel||this._nameSel.trim())&&this._commitRoom()}_commitRoom(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot(),i=this._spaceH,s=!!this._pendingSplit;let n;if(this._pendingSplit){const e=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!e)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);e.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Cc,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,n=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;n=this._path.slice(0,-1)}const o=this._areaSel?this.hass.areas[this._areaSel]?.name:"",r={id:"r"+Date.now().toString(36),name:this._nameSel||o||this._t("room.default_name"),area:this._areaSel||null,poly:n.map(t=>[t[0]/Cc,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(r),!s&&this._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),s&&this._commitOpenSpans(),!s){const e=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||jo],i=e[0]||this._drawWallCm;if(null!=i){this._cfgEpoch++;const s=this._openCuts();let o=function(t,e,i,s,n,o=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=yr(e,i,o,n,r,t);if(!a)return t?t.slice():[];const l=br(t,a,n,r);let c=t?t.slice():[];for(let t=0;t0||(c=lr(c,e,i,s,n,r))}return c}(t.walls,this._spaceModel().rooms,r.id,i,this._wallKeyPitch,s,Cc);for(const t of kr(this._spaceModel().rooms,o,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc)){if(t.roomId!==r.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],a=n.findIndex((t,e)=>{const i=n[(e+1)%n.length];return hs(s,[t[0],t[1],i[0],i[1]])<=.02*this._gridPitch});a>=0&&(o=lr(o,t.a,t.b,e[a]||i,this._wallKeyPitch,Cc))}o=this._normalizeWalls(o,s),o.length?t.walls=o:delete t.walls}}this._recordGeometry(this._t(s?"history.split_room":"history.add_room"),e),this._saveConfig(),this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._pendingSplit=null,this._splitSel=null;const a=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let l=0;if(a){const t=Cc,e={...this._layout};for(const i of this._devices){if(i.area!==a||i.space!==this._space)continue;if(l++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/Cc,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const c=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(a?this._t("toast.room_saved",{n:c,added:l}):this._t("toast.room_saved_no_area",{n:c}))}_cancelPath(){this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._cursorPt=null,this._roomDialog=!1,this._pendingSplit=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._openWallAnchor=null,this._boundaryRestoreGuard=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null}_resumeLastDraft(){const t=this._resumeDraftBySpace[this._space];if(!t)return;const e=this._spaceModel().room_drafts.find(e=>e.id===t);e?(this._activeDraftId=t,this._path=e.points.map(t=>[...t]),this._draftSegmentCms=e.segments.map(t=>t.cm),this._cursorPt=null):delete this._resumeDraftBySpace[this._space]}_roomDialogCancel(){return this._roomDialog=!1,this._roomEditId?(this._roomEditId=null,this._nameSel="",void(this._areaSel="")):this._pendingSplit?(this._pendingSplit=null,void(this._splitSel=null)):void this._undoPoint()}get _freeAreas(){const t=new Set;for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area&&t.add(i.area);return Object.values(this.hass?.areas||{}).filter(e=>!t.has(e.area_id)).sort((t,e)=>(t.name||"").localeCompare(e.name||""))}_openMarkerDialog(t){this._vacAllCamerasFor=null,this._vacAllCameraCache=null,t&&this._ackNewDevice(t.id),this._norm?this._markerDialog=t?{devId:t.id,name:t.name,binding:"virtual"===t.bindingKind?"virtual":t.bindingKind+":"+t.bindingRef,bindingMode:"virtual"===t.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===t.bindingKind&&!!this._fullRegistryHass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:ui(t.marker?.display),rippleColor:_t(t.marker?.ripple_color,""),rippleSize:Number(t.marker?.ripple_size)>0?Number(t.marker.ripple_size):3,size:Number(t.marker?.size)>0?Number(t.marker.size):1,angle:Number(t.marker?.angle)||0,tapAction:t.marker?.tap_action||"",tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",defaultTap:"light"===t.primary?.split(".")[0]?"toggle":"info",controls:nn(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",isLight:!0===t.marker?.is_light,useClimateTemp:!0===t.marker?.use_climate_temp,glowRadius:Number(t.marker?.glow_radius_cm)>0?String(this._imperial?Math.round(Number(t.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(t.marker.glow_radius_cm))/100):"",model:t.model||"",link:t.link||"",description:t.description||"",pdfs:[...t.pdfs||[]],room:t.marker?.room_id?t.space+"#@"+t.marker.room_id:t.space&&t.area?t.space+"#"+t.area:"",hideFromPlan:!0===t.marker?.hidden,busy:!1}:{name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:3,size:1,angle:0,tapAction:"",tapTarget:"",tapConfirm:!1,runFilter:"",defaultTap:"info",controls:[],controlsFilter:"",isLight:!1,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)}:this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of wi)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&this._planEntityAvailable(i)&&t.push({value:i,label:s?.attributes?.friendly_name||i,sub:this._t("run."+e)});return t.sort((t,e)=>t.sub.localeCompare(e.sub)||t.label.localeCompare(e.label))}_bindingCandidates(){const t=this._planHass,e=gn(this._markers),i=new Set(this._markers.filter(t=>t.removed).map(t=>t.binding)),s=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&s.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&s.add("entity:"+t.bindingRef));const n=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&n.add(t.name.trim()+"|"+(t.area||""));const o=[];for(const e of Object.values(t.devices)){if("service"===e.entry_type)continue;const t="device:"+e.id;if(s.has(t))continue;const r=(e.name_by_user||e.name||e.id).trim();t!==this._markerDialog?.binding&&!i.has(t)&&n.has(r+"|"+(e.area_id||""))||o.push({value:t,label:r,sub:(e.model||this._t("marker.sub_device"))+("Group"===e.model?this._t("marker.sub_z2m_group"):"")})}const r=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[n,a]of Object.entries(t.entities)){const l="entity:"+n;if(s.has(l))continue;if(fn(t,n,e)&&!i.has(l))continue;const c=r.has(a.platform),h="group"===a.platform;if(!c&&!h)continue;if(a.hidden&&!i.has(l))continue;const d=t.states[n];o.push({value:l,label:a.name||d?.attributes?.friendly_name||n,sub:n.split(".")[0]+" · "+("group"===a.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const n=new Set(o.map(t=>t.value));for(const[r,a]of Object.entries(t.entities)){const l="entity:"+r;if(s.has(l)||n.has(l)||a.hidden&&!i.has(l))continue;if(fn(t,r,e)&&!i.has(l))continue;const c=t.states[r],h=a.name||c?.attributes?.friendly_name||r,d=a.device_id?t.devices[a.device_id]:null,p=d&&(d.name_by_user||d.name)||"";o.push({value:l,label:h,sub:r.split(".")[0]+" · "+this._t("marker.sub_entity")+(p?" · "+p:"")})}}const a=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=a?o.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(a)):o;return l.sort((t,e)=>t.label.localeCompare(e.label)),l.slice(0,200)}_physicalBodiesR(t=this._spaceModel()){const e=`${t.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===e)return this._physicalBodiesCache.all;const i=(t.room_drafts||[]).flatMap(t=>Gr(t,this._cellCm,this._gridPitch)),s=(t.partitions||[]).flatMap(t=>{const e=Wr(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?[e]:[]}),n=(t.wall_columns||[]).map(t=>Br(t,this._cellCm,this._gridPitch)),o=[...i,...s,...n];return this._physicalBodiesCache={key:e,drafts:i,partitions:s,columns:n,all:o},o}_cleanFloor(t,e,i=this._spaceModel()){const s=t.id||`#${i.rooms.indexOf(t)}`,n=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=uc(this._cleanFloorCache,n);if(t.hit)return t.value}const o=e.map(t=>t[0]),r=e.map(t=>t[1]),a=[Math.min(...o),Math.min(...r),Math.max(...o),Math.max(...r)],l=this._physicalBodiesR(i).filter(t=>{const e=t.map(t=>t[0]),i=t.map(t=>t[1]);return Math.max(...e)>=a[0]&&Math.min(...e)<=a[2]&&Math.max(...i)>=a[1]&&Math.min(...i)<=a[3]}),c=l.length?Vr(e,l):null,h={floor:e,geom:c,path:c?jr(c):"",area:Kr(c||[[[...e,e[0]]]])};return this._rszPreview||_c(this._cleanFloorCache,n,h,600),h}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";const s=this._fullRegistryHass,n=this._bindingStatus(t),o="active"===n.kind?n.enabledEntityIds:n.allEntityIds;if("device"===e){const t=s.devices?.[i];if(!t)return"mdi:help-circle";const e=o;return e.some(t=>t.startsWith("lock."))?"mdi:lock":mn(s,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=s.entities?.[i],e=this.hass.states?.[i],n=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":mn(s,n,"",[i],this._iconRules)}return""}_allRoomsFlat(){const t=[];for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area?t.push({value:e.id+"#"+i.area,label:(e.title||e.id)+" · "+i.name}):i.id&&t.push({value:e.id+"#@"+i.id,label:(e.title||e.id)+" · "+i.name+" · "+this._t("marker.subarea")});return t}_errText(t){if(!t)return this._t("err.unknown");if("string"==typeof t)return t;if(t.message)return t.message;if(t.error)return t.error;if(null!=t.code)return this._t("err.code",{code:t.code});try{return JSON.stringify(t)}catch{return String(t)}}async _pickMarkerFiles(t){const e=t.target,i=e.files?[...e.files]:[];if(e.value="",!i.length||!this._markerDialog)return;const s=this._markerDialog.uploadId||this._markerDialog.devId||"new",n=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const t={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||t.name,url:o.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}async _saveMarker(){const t=this._markerDialog;if(t&&!t.busy&&("ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding))if("virtual"!==t.binding||t.name.trim())if("run"!==t.tapAction||t.tapTarget){if("ha"===t.bindingMode){const e=this._bindingStatus(t.binding),i=t.devId?this._markers.find(e=>e.id===t.devId):null,s=!i||i.binding!==t.binding;if("active"!==e.kind&&s)return void this._showToast(this._t("ha_disabled"===e.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===e.kind&&!0===i?.hidden&&!t.hideFromPlan)return this._markerDialog={...t,hideFromPlan:!0},void this._showToast(this._t("entity"===e.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}this._markerDialog={...t,busy:!0};try{const e=this._serverCfg;let i;e.markers=e.markers||[];const s=us(t.room);let n=s?.space||null,o=s?.area||null;const r=s?.roomId||null;"virtual"!==t.binding||n||(n=this._space),i=ai(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const a=t.devId,l="virtual"===t.binding?[]:e.markers.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),c=l.length>0,h=nn(t.binding,t.controls,this._bindingEntities(t.binding)),d=e.markers.find(t=>t.id===i||t.id===a)?.vacuum||null,p={id:i,vacuum:d,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&3!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle?t.angle:null,tap_action:t.tapAction||null,tap_target:"run"===t.tapAction&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:h.length?h:null,is_light:!!t.isLight||null,use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=pc(t.glowRadius);return null==e||e<=0?null:Math.round(this._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:!!t.hideFromPlan};("virtual"===t.binding||t.room)&&(p.space=n,p.area=o,p.room_id=r);const u=a?this._devices.find(t=>t.id===a):null,_=u?.marker?.room_id??null,m=!!t.room&&null!=u&&(u.space!==n||u.area!==o||_!==r);let g=!1;const f=t.uploadId||a;if(f&&f!==i&&p.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:f,to_id:i}),e=t?.mapping||{};p.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const n="/files/"+e+"/",o="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(n))return t;const e=t.url.split(n)[1]||"",[i,r]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(n+i)[0]+o+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(n).join(o)}})}(p.pdfs,f,i,e),g=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}e.markers=e.markers.filter(t=>t.id!==i&&t.id!==a&&("virtual"===p.binding||t.binding!==p.binding)),e.markers.push(p);let v=null;const y=n||u?.space||this._space,b=a?this._layout[a]:null,w=b?{s:b.s||u?.space||this._space,x:b.x,y:b.y}:a&&u&&this._defPos[a]?this._normPos(u.space,this._defPos[a].x,this._defPos[a].y):null;if(!c&&w&&w.s===y)i===a&&this._layout[i]&&!m||(v={s:w.s,x:w.x,y:w.y},this._layout={...this._layout,[i]:v});else if(c||!this._layout[i]||m){const t=this._spaceModel(n||void 0);let e=t.vb[0]+t.vb[2]/2,s=t.vb[1]+t.vb[3]/2;const a=r?t.rooms.find(t=>t.id===r):o?t.rooms.find(t=>t.area===o):void 0;a&&([e,s]=this._roomCenter(a)),v=this._normPos(n||this._space,e,s),this._layout={...this._layout,[i]:v}}await this._saveConfigNow(),v&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:i,pos:v}));const k=new Set(l);a&&a!==i&&k.add(a),k.delete(i);for(const t of k)delete this._layout[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{});g&&f&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:f}).catch(()=>{}),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_saved"))}catch(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}else this._showToast(this._t("toast.run_target_required"));else this._showToast(this._t("toast.virtual_name_required"))}async _deleteMarker(){const t=this._markerDialog;if(!t||t.busy||!t.devId)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null;if(!e)return;const i=t.name||this._t("device.fallback");if(!confirm(this._t("confirm.remove_marker",{name:i})))return;const s=this._serverCfg;s.markers=s.markers||[];const n=s.markers,o="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!o)return;const r=function(t,e,i,s){const n=new Set([e]),o=t.filter(t=>{const o=t.id===e||!s&&t.binding===i;return o&&n.add(t.id),!o});return{markers:s?o:[...o,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:n}}(s.markers,e.id,o,"virtual"===e.bindingKind);s.markers=r.markers;const a=r.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of a){delete this._layout[t],delete this._defPos[t],this._dirtyPos.delete(t),this._sentPos.delete(t);const e=this._activityRt.get(t);e&&clearTimeout(e.timer),this._activityRt.delete(t),this._vacRt.delete(t),delete this._vacSrvTrails[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{}),await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:t}).catch(()=>{}),await this.hass.callWS({type:"houseplan/trail/delete",marker_id:t}).catch(()=>{})}this._markerDialog=null,this._infoCard?.id===e.id&&this._closeInfoCard(),this._selId===e.id&&(this._selId=null),this._drag?.id===e.id&&(this._drag=null),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._serverCfg===s&&(s.markers=n),this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Cc,y:i/Cc}}_openSpaceDialog(t,e){if(this._serverStorage&&this._serverCfg)if("edit"===t){const i=this._serverCfg.spaces.find(t=>t.id===e);if(!i)return;const s=Fi(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?Ei(i.settings.custom_fill):null,o="none"===s.fill?{...n||Ii,a:0}:n;this._spaceDialog={mode:t,spaceId:e,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:s.showBorders,showNames:s.showNames,hideDecor:s.hideDecor,hideOpenings:s.hideOpenings,roomColor:s.color,roomOpacity:s.opacity,fillMode:"none"===s.fill?"custom":s.fill,customFill:o,glowEnabled:s.glow,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:Co({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:zi,roomOpacity:Ai,fillMode:"custom",customFill:{...Ii,a:0},glowEnabled:!0,bgColor:null,bgMode:null,northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:5,busy:!1};else this._showToast(this._t("toast.integration_missing"))}async _pickPlanFile(t){const e=t.target,i=e.files?.[0];if(!i||!this._spaceDialog)return;const s={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[i.type]||(i.name.toLowerCase().endsWith(".svg")?"svg":"");if(!s)return void this._showToast(this._t("toast.plan_formats"));const n=new Uint8Array(await i.arrayBuffer());let o="";for(let t=0;t{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=a});URL.revokeObjectURL(a),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:r,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return j`
${this._t("space.loading")}
`;const e=t.saved||[];if(!e.length)return j`
${this._t("space.no_saved")}
`;return j`
+
`:j``}_openingClick(t){const e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return void this._editOpening(i);const s=He(t,this._spaceModel().rooms,e);if(!s)return void this._showToast(this._t("toast.opening_no_wall"));if(function(t,e,i,s,o){for(const n of s)if(!(gs([t,e],n)>o)&&mr([n[0],n[1]],[n[2],n[3]],i))return!0;return!1}(s.x,s.y,s.angle,this._openCuts(),e))return void this._showToast(this._t("toast.opening_on_virtual"));const o=this._opRuler(s,this._cmToUnits(90));this._openingDialog={type:"door",lengthCm:90,contact:"",lock:"",invert:!1,flipH:!1,flipV:!1,x:o.x,y:o.y,angle:o.angle},this._cursorPt=null}_editOpening(t){this._openingDialog={id:t.id,type:t.type,lengthCm:Math.round(t.rlen/this._gridPitch*this._cellCm),contact:t.contact||"",lock:t.lock||"",invert:!!t.invert,flipH:!!t.flip_h,flipV:!!t.flip_v,x:t.rx,y:t.ry,angle:t.angle}}_opPointerDown(t,e){if("plan"===this._mode&&"resize"!==this._tool){t.preventDefault(),t.stopPropagation();try{Uc(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;if(Math.abs(t.clientX-this._opDrag.sx)+Math.abs(t.clientY-this._opDrag.sy)<=3)return;const i=He(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,o=s?.openings?.find(t=>t.id===e.id);if(!o)return;const n=this._opRuler(i,o.length*Nc);this._opMeasure=n.measure;const r=n.x/Nc,a=n.y/this._spaceH,l=o.x!==r||o.y!==a||o.angle!==i.angle;l&&(this._opDrag.dirty=!0),o.x=r,o.y=a,o.angle=i.angle,l&&this._cfgEpoch++,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel().rooms,s=this._gridPitch/2;let o=t.x,n=t.y,r=qe([o,n],t.angle,e,i,s);if(r&&r.centered&&(o!==r.wallCenter[0]||n!==r.wallCenter[1]))[o,n]=r.wallCenter,r=qe([o,n],t.angle,e,i,s);else if(r){const[a,l]=r.wallA,[c,h]=r.wallB,d=c-a,p=h-l,u=Math.hypot(d,p);if(u>0){const c=this._gridPitch,h=Math.min(e/2,u/2);let _=Math.round(((o-a)*d+(n-l)*p)/u/c)*c;_=Math.max(h,Math.min(u-h,_)),o=a+_/u*d,n=l+_/u*p,r=qe([o,n],t.angle,e,i,s)||r}}if(!r)return{x:o,y:n,angle:t.angle,measure:null};const a="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Ee(t/this._gridPitch*this._cellCm,a)});return{x:o,y:n,angle:t.angle,measure:{labels:[l(r.sideA,r.midA),l(r.sideB,r.midB)],guide:r.centered?{x:r.wallCenter[0],y:r.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;const i=this._opDrag,s=i.moved;this._opMeasure=null,s&&i.dirty&&(this._recordGeometry(this._t("history.move_opening"),i.before),this._saveConfig()),s?window.setTimeout(()=>this._opDrag=null,0):this._opDrag=null}_opClick(t,e){"plan"===this._mode&&"resize"===this._tool||(t.stopPropagation(),this._opDrag?.moved||"plan"===this._mode&&this._editOpening(e))}_saveOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,o={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Nc,y:t.y/s,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/Nc,contact:t.contact||null,lock:"window"!==t.type&&t.lock||null,invert:t.invert||void 0,flip_h:"gate"!==t.type&&t.flipH||void 0,flip_v:t.flipV||void 0};e.openings=e.openings||[];const n=e.openings.findIndex(t=>t.id===o.id);n>=0?e.openings[n]=o:e.openings.push(o),this._recordGeometry(this._t(t.id?"history.edit_opening":"history.add_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t?.id||!e?.openings)return;const i=this._geometrySnapshot();e.openings=e.openings.filter(e=>e.id!==t.id),this._recordGeometry(this._t("history.delete_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){if(!this._planEntityAvailable(e))continue;const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],o=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||o)&&t.push([e,s?.attributes?.friendly_name||e,o?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")&&this._planEntityAvailable(t)).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_mergeClick(t){const e=this._spaceModel().rooms,i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const s=i.id;if(!this._mergeSel||this._mergeSel===s)return void(this._mergeSel=this._mergeSel===s?null:s);const o=e.find(t=>t.id===this._mergeSel),n=o?Fe(o):null,r=Fe(i),a=n&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=Pe(oi(t),oi(e));if(1!==i.length)return null;if(1!==i[0].length)return null;const s=i[0][0].slice(0,-1).map(t=>[t[0],t[1]]);return s.length>=3?s:null}(n,r):null;if(!a)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:s,poly:a,pick:"a"},this._mergeSel=null}_commitMerge(){const t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,o="a"===t.pick?t.aId:t.bId,n="a"===t.pick?t.bId:t.aId,r=e.rooms.find(t=>t.id===o);r?(r.poly=t.poly.map(t=>[t[0]/Nc,t[1]/s]),delete r.x,delete r.y,delete r.w,delete r.h,e.rooms=e.rooms.filter(t=>t.id!==n),this._commitOpenSpans(),this._recordGeometry(this._t("history.merge_rooms"),i),this._saveConfig(),this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.rooms_merged",{name:r.name||""}))):this._mergeDialog=null}_splitClick(t){const e=this._spaceModel().rooms;if(!this._splitSel){const i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;return void(this._splitSel={roomId:i.id,pts:[]})}const i=e.find(t=>t.id===this._splitSel.roomId),s=i?Fe(i):null;if(!i||!s)return void(this._splitSel=null);const o=.02*this._gridPitch,n=6*this._gridPitch,r=We(t,s),a=r&&function(t,e,i){let s=null,o=1/0;for(let n=0;n=o)continue;o=_;const m=Math.sqrt(p),g=(i>0?Math.max(0,Math.min(m,Math.round(u*m/i)*i)):u*m)/m;s=[r+g*h,a+g*d]}return s}(r,s,this._gridPitch)||r,l=r&&a&&Math.hypot(r[0]-t[0],r[1]-t[1])<=n?a:null,c=!!l&&Ve(l,s,o),h=this._splitSel.pts;if(!h.length)return c?void(this._splitSel={...this._splitSel,pts:[l]}):void this._showToast(this._t("toast.split_pick_wall"));if(!c){const e=this._snap(t);return Ke(e,s,o)?void(this._splitSel={...this._splitSel,pts:[...h,e]}):void this._showToast(this._t("toast.split_pick_inside"))}const d=function(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],o=e[e.length-1];if(je(s,o,i))return null;const n=ni(t,s,i),r=ni(t,o,i);if(n<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!Ke(e,t,i))return null;for(let i=0;i{const r=[e];let a=(s+1)%t.length;for(let e=0;e<=t.length&&(r.push(t[a]),a!==n);e++)a=(a+1)%t.length;return r.push(o),ri(r,i)};let c,h;if(n===r){const r=ri([...e],i);if(r.length<3||si(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}c=ri(a,i),h=r}else c=ri([...l(s,n,o,r),...[...a].reverse()],i),h=ri([...l(o,r,s,n),...a],i);return c.length<3||h.length<3||si(c)<=i||si(h)<=i||Math.abs(si(c)+si(h)-si(t))>Math.max(i,1e-6*si(t))?null:[c,h]}(s,[...h,l],o);if(!d)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[p,u]=d,_=si(p)>=si(u)?p:u,m=_===p?u:p;this._pendingSplit={roomId:i.id,mainPoly:_,newPoly:m},this._cursorPt=null,this._nameSel="",this._areaSel="",this._roomDialog=!0}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])}_markupMove(t){if(!this._markup)return;const e=t.pointerType;if(e&&(this._boundaryPointerType=e),"column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"boundary"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const i=("draw"===this._tool||"partition"===this._tool)&&this._path.length&&!this._contourClosed,s="split"===this._tool&&!!this._splitSel?.pts?.length;if(!i&&!s)return;const o=this._svgPoint(t);this._cursorPt=i?this._snapDrawPoint(o,t.shiftKey):this._snap(o)}get _openingPreview(){if("opening"!==this._tool||!this._cursorPt)return null;const t=this._cursorPt,e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return null;const s=this._cmToUnits(90),o=He(t,this._spaceModel().rooms,e);if(!o)return null;const n=this._opRuler(o,s);return{x:n.x,y:n.y,angle:n.angle,rlen:s,measure:n.measure}}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){(this._areaSel||this._nameSel.trim())&&this._commitRoom()}_commitRoom(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot(),i=this._spaceH,s=!!this._pendingSplit;let o;if(this._pendingSplit){const e=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!e)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);e.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Nc,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,o=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;o=this._path.slice(0,-1)}const n=this._areaSel?this.hass.areas[this._areaSel]?.name:"",r={id:"r"+Date.now().toString(36),name:this._nameSel||n||this._t("room.default_name"),area:this._areaSel||null,poly:o.map(t=>[t[0]/Nc,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(r),!s&&this._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),s&&this._commitOpenSpans(),!s){const e=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||Xn],i=e[0]||this._drawWallCm;if(null!=i){this._cfgEpoch++;const s=this._openCuts();let n=function(t,e,i,s,o,n=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=Dr(e,i,n,o,r,t);if(!a)return t?t.slice():[];const l=Tr(t,a,o,r);let c=t?t.slice():[];for(let t=0;t0||(c=fr(c,e,i,s,o,r))}return c}(t.walls,this._spaceModel().rooms,r.id,i,this._wallKeyPitch,s,Nc);for(const t of Rr(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc)){if(t.roomId!==r.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],a=o.findIndex((t,e)=>{const i=o[(e+1)%o.length];return gs(s,[t[0],t[1],i[0],i[1]])<=.02*this._gridPitch});a>=0&&(n=fr(n,t.a,t.b,e[a]||i,this._wallKeyPitch,Nc))}n=this._normalizeWalls(n,s),n.length?t.walls=n:delete t.walls}}this._recordGeometry(this._t(s?"history.split_room":"history.add_room"),e),this._saveConfig(),this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._pendingSplit=null,this._splitSel=null;const a=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let l=0;if(a){const t=Nc,e={...this._layout};for(const i of this._devices){if(i.area!==a||i.space!==this._space)continue;if(l++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/Nc,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const c=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(a?this._t("toast.room_saved",{n:c,added:l}):this._t("toast.room_saved_no_area",{n:c}))}_cancelPath(){this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._cursorPt=null,this._roomDialog=!1,this._pendingSplit=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._openWallAnchor=null,this._boundaryRestoreGuard=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null}_resumeLastDraft(){const t=this._resumeDraftBySpace[this._space];if(!t)return;const e=this._spaceModel().room_drafts.find(e=>e.id===t);e?(this._activeDraftId=t,this._path=e.points.map(t=>[...t]),this._draftSegmentCms=e.segments.map(t=>t.cm),this._cursorPt=null):delete this._resumeDraftBySpace[this._space]}_roomDialogCancel(){return this._roomDialog=!1,this._roomEditId?(this._roomEditId=null,this._nameSel="",void(this._areaSel="")):this._pendingSplit?(this._pendingSplit=null,void(this._splitSel=null)):void this._undoPoint()}get _freeAreas(){const t=new Set;for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area&&t.add(i.area);return Object.values(this.hass?.areas||{}).filter(e=>!t.has(e.area_id)).sort((t,e)=>(t.name||"").localeCompare(e.name||""))}_openMarkerDialog(t){if(this._vacAllCamerasFor=null,this._vacAllCameraCache=null,t&&this._ackNewDevice(t.id),this._norm)if(t){const e=t.marker,i=Object.prototype.hasOwnProperty.call(e||{},"is_light"),s=Object.prototype.hasOwnProperty.call(e||{},"glow_color"),o=Ji(e?.glow_color);this._markerDialog={devId:t.id,name:t.name,binding:"virtual"===t.bindingKind?"virtual":t.bindingKind+":"+t.bindingRef,bindingMode:"virtual"===t.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===t.bindingKind&&!!this._fullRegistryHass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:ui(t.marker?.display),rippleColor:_t(t.marker?.ripple_color,""),rippleSize:Number(t.marker?.ripple_size)>0?Number(t.marker.ripple_size):3,size:Number(t.marker?.size)>0?Number(t.marker.size):1,angle:Number(t.marker?.angle)||0,tapAction:t.marker?.tap_action||"",tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",defaultTap:"light"===t.primary?.split(".")[0]?"toggle":"info",controls:co(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",lightRole:!0===e?.is_light?"always":!1===e?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:i,originalIsLight:e?.is_light,glowMode:null!=o?.bri?"fixed":o?"color":"auto",glowColor:o?.c||this._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(o?.bri??1))),glowColorDrafted:!!o,glowBrightnessDrafted:null!=o?.bri,glowTouched:!1,originalHasGlowColor:s&&(!!o||null===e?.glow_color),originalGlowColor:o||(null===e?.glow_color?null:void 0),useClimateTemp:!0===t.marker?.use_climate_temp,glowRadius:Number(t.marker?.glow_radius_cm)>0?String(this._imperial?Math.round(Number(t.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(t.marker.glow_radius_cm))/100):"",model:t.model||"",link:t.link||"",description:t.description||"",pdfs:[...t.pdfs||[]],room:t.marker?.room_id?t.space+"#@"+t.marker.room_id:t.space&&t.area?t.space+"#"+t.area:"",hideFromPlan:!0===t.marker?.hidden,busy:!1}}else this._markerDialog={name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:3,size:1,angle:0,tapAction:"",tapTarget:"",tapConfirm:!1,runFilter:"",defaultTap:"info",controls:[],controlsFilter:"",lightRole:"auto",lightRoleTouched:!1,originalHasIsLight:!1,originalIsLight:void 0,glowMode:"auto",glowColor:this._fillColors.glow_light.c,glowBrightness:100,glowColorDrafted:!1,glowBrightnessDrafted:!1,glowTouched:!1,originalHasGlowColor:!1,originalGlowColor:void 0,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of wi)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&this._planEntityAvailable(i)&&t.push({value:i,label:s?.attributes?.friendly_name||i,sub:this._t("run."+e)});return t.sort((t,e)=>t.sub.localeCompare(e.sub)||t.label.localeCompare(e.label))}_bindingCandidates(){const t=this._planHass,e=So(this._markers),i=new Set(this._markers.filter(t=>t.removed).map(t=>t.binding)),s=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&s.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&s.add("entity:"+t.bindingRef));const o=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&o.add(t.name.trim()+"|"+(t.area||""));const n=[];for(const e of Object.values(t.devices)){if("service"===e.entry_type)continue;const t="device:"+e.id;if(s.has(t))continue;const r=(e.name_by_user||e.name||e.id).trim();t!==this._markerDialog?.binding&&!i.has(t)&&o.has(r+"|"+(e.area_id||""))||n.push({value:t,label:r,sub:(e.model||this._t("marker.sub_device"))+("Group"===e.model?this._t("marker.sub_z2m_group"):"")})}const r=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[o,a]of Object.entries(t.entities)){const l="entity:"+o;if(s.has(l))continue;if(Mo(t,o,e)&&!i.has(l))continue;const c=r.has(a.platform),h="group"===a.platform;if(!c&&!h)continue;if(a.hidden&&!i.has(l))continue;const d=t.states[o];n.push({value:l,label:a.name||d?.attributes?.friendly_name||o,sub:o.split(".")[0]+" · "+("group"===a.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const o=new Set(n.map(t=>t.value));for(const[r,a]of Object.entries(t.entities)){const l="entity:"+r;if(s.has(l)||o.has(l)||a.hidden&&!i.has(l))continue;if(Mo(t,r,e)&&!i.has(l))continue;const c=t.states[r],h=a.name||c?.attributes?.friendly_name||r,d=a.device_id?t.devices[a.device_id]:null,p=d&&(d.name_by_user||d.name)||"";n.push({value:l,label:h,sub:r.split(".")[0]+" · "+this._t("marker.sub_entity")+(p?" · "+p:"")})}}const a=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=a?n.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(a)):n;return l.sort((t,e)=>t.label.localeCompare(e.label)),l.slice(0,200)}_physicalBodiesR(t=this._spaceModel()){const e=`${t.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===e)return this._physicalBodiesCache.all;const i=(t.room_drafts||[]).flatMap(t=>ea(t,this._cellCm,this._gridPitch)),s=(t.partitions||[]).flatMap(t=>{const e=Qr(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?[e]:[]}),o=(t.wall_columns||[]).map(t=>ta(t,this._cellCm,this._gridPitch)),n=[...i,...s,...o];return this._physicalBodiesCache={key:e,drafts:i,partitions:s,columns:o,all:n},n}_cleanFloor(t,e,i=this._spaceModel()){const s=t.id||`#${i.rooms.indexOf(t)}`,o=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=kc(this._cleanFloorCache,o);if(t.hit)return t.value}const n=e.map(t=>t[0]),r=e.map(t=>t[1]),a=[Math.min(...n),Math.min(...r),Math.max(...n),Math.max(...r)],l=this._physicalBodiesR(i).filter(t=>{const e=t.map(t=>t[0]),i=t.map(t=>t[1]);return Math.max(...e)>=a[0]&&Math.min(...e)<=a[2]&&Math.max(...i)>=a[1]&&Math.min(...i)<=a[3]}),c=l.length?ia(e,l):null,h={floor:e,geom:c,path:c?Xr(c):"",area:sa(c||[[[...e,e[0]]]])};return this._rszPreview||$c(this._cleanFloorCache,o,h,600),h}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";const s=this._fullRegistryHass,o=this._bindingStatus(t),n="active"===o.kind?o.enabledEntityIds:o.allEntityIds;if("device"===e){const t=s.devices?.[i];if(!t)return"mdi:help-circle";const e=n;return e.some(t=>t.startsWith("lock."))?"mdi:lock":xo(s,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=s.entities?.[i],e=this.hass.states?.[i],o=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":xo(s,o,"",[i],this._iconRules)}return""}_allRoomsFlat(){const t=[];for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area?t.push({value:e.id+"#"+i.area,label:(e.title||e.id)+" · "+i.name}):i.id&&t.push({value:e.id+"#@"+i.id,label:(e.title||e.id)+" · "+i.name+" · "+this._t("marker.subarea")});return t}_errText(t){if(!t)return this._t("err.unknown");if("string"==typeof t)return t;if(t.message)return t.message;if(t.error)return t.error;if(null!=t.code)return this._t("err.code",{code:t.code});try{return JSON.stringify(t)}catch{return String(t)}}async _pickMarkerFiles(t){const e=t.target,i=e.files?[...e.files]:[];if(e.value="",!i.length||!this._markerDialog)return;const s=this._markerDialog.uploadId||this._markerDialog.devId||"new",o=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),n=await i.json().catch(()=>({}));if(!i.ok||n.error){const t={too_large:this._t("err.too_large",{mb:n.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[n.error]||n.error||"HTTP "+i.status)}o.push({name:n.name||t.name,url:n.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}o.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...o]},this._showToast(this._t("toast.files_attached",{n:o.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}_markerLightFields(t){const e={};if(t.lightRoleTouched?"always"===t.lightRole?e.is_light=!0:"never"===t.lightRole&&(e.is_light=!1):t.originalHasIsLight&&(e.is_light=t.originalIsLight??null),t.glowTouched){if("auto"!==t.glowMode){const i={c:_t(t.glowColor,this._fillColors.glow_light.c)};"fixed"===t.glowMode&&(i.bri=Math.max(.01,Math.min(1,Math.round(t.glowBrightness)/100))),e.glow_color=i}}else t.originalHasGlowColor&&(e.glow_color=t.originalGlowColor??null);return e}async _saveMarker(){const t=this._markerDialog;if(t&&!t.busy&&("ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding))if("virtual"!==t.binding||t.name.trim())if("run"!==t.tapAction||t.tapTarget){if("ha"===t.bindingMode){const e=this._bindingStatus(t.binding),i=t.devId?this._markers.find(e=>e.id===t.devId):null,s=!i||i.binding!==t.binding;if("active"!==e.kind&&s)return void this._showToast(this._t("ha_disabled"===e.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===e.kind&&!0===i?.hidden&&!t.hideFromPlan)return this._markerDialog={...t,hideFromPlan:!0},void this._showToast(this._t("entity"===e.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}this._markerDialog={...t,busy:!0};try{const e=this._serverCfg;let i;e.markers=e.markers||[];const s=ys(t.room);let o=s?.space||null,n=s?.area||null;const r=s?.roomId||null;"virtual"!==t.binding||o||(o=this._space),i=ai(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const a=t.devId,l="virtual"===t.binding?[]:e.markers.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),c=l.length>0,h=co(t.binding,t.controls,this._bindingEntities(t.binding)),d=e.markers.find(t=>t.id===i||t.id===a)?.vacuum||null,p={id:i,vacuum:d,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&3!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle?t.angle:null,tap_action:t.tapAction||null,tap_target:"run"===t.tapAction&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:h.length?h:null,...this._markerLightFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=wc(t.glowRadius);return null==e||e<=0?null:Math.round(this._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:!!t.hideFromPlan};("virtual"===t.binding||t.room)&&(p.space=o,p.area=n,p.room_id=r);const u=a?this._devices.find(t=>t.id===a):null,_=u?.marker?.room_id??null,m=!!t.room&&null!=u&&(u.space!==o||u.area!==n||_!==r);let g=!1;const f=t.uploadId||a;if(f&&f!==i&&p.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:f,to_id:i}),e=t?.mapping||{};p.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const o="/files/"+e+"/",n="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(o))return t;const e=t.url.split(o)[1]||"",[i,r]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(o+i)[0]+n+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(o).join(n)}})}(p.pdfs,f,i,e),g=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}e.markers=e.markers.filter(t=>t.id!==i&&t.id!==a&&("virtual"===p.binding||t.binding!==p.binding)),e.markers.push(p);let v=null;const y=o||u?.space||this._space,b=a?this._layout[a]:null,w=b?{s:b.s||u?.space||this._space,x:b.x,y:b.y}:a&&u&&this._defPos[a]?this._normPos(u.space,this._defPos[a].x,this._defPos[a].y):null;if(!c&&w&&w.s===y)i===a&&this._layout[i]&&!m||(v={s:w.s,x:w.x,y:w.y},this._layout={...this._layout,[i]:v});else if(c||!this._layout[i]||m){const t=this._spaceModel(o||void 0);let e=t.vb[0]+t.vb[2]/2,s=t.vb[1]+t.vb[3]/2;const a=r?t.rooms.find(t=>t.id===r):n?t.rooms.find(t=>t.area===n):void 0;a&&([e,s]=this._roomCenter(a)),v=this._normPos(o||this._space,e,s),this._layout={...this._layout,[i]:v}}await this._saveConfigNow(),v&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:i,pos:v}));const k=new Set(l);a&&a!==i&&k.add(a),k.delete(i);for(const t of k)delete this._layout[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{});g&&f&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:f}).catch(()=>{}),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_saved"))}catch(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}else this._showToast(this._t("toast.run_target_required"));else this._showToast(this._t("toast.virtual_name_required"))}async _deleteMarker(){const t=this._markerDialog;if(!t||t.busy||!t.devId)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null;if(!e)return;const i=t.name||this._t("device.fallback");if(!confirm(this._t("confirm.remove_marker",{name:i})))return;const s=this._serverCfg;s.markers=s.markers||[];const o=s.markers,n="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!n)return;const r=function(t,e,i,s){const o=new Set([e]),n=t.filter(t=>{const n=t.id===e||!s&&t.binding===i;return n&&o.add(t.id),!n});return{markers:s?n:[...n,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:o}}(s.markers,e.id,n,"virtual"===e.bindingKind);s.markers=r.markers;const a=r.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of a){delete this._layout[t],delete this._defPos[t],this._dirtyPos.delete(t),this._sentPos.delete(t);const e=this._activityRt.get(t);e&&clearTimeout(e.timer),this._activityRt.delete(t),this._vacRt.delete(t),delete this._vacSrvTrails[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{}),await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:t}).catch(()=>{}),await this.hass.callWS({type:"houseplan/trail/delete",marker_id:t}).catch(()=>{})}this._markerDialog=null,this._infoCard?.id===e.id&&this._closeInfoCard(),this._selId===e.id&&(this._selId=null),this._drag?.id===e.id&&(this._drag=null),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._serverCfg===s&&(s.markers=o),this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Nc,y:i/Nc}}_openSpaceDialog(t,e){if(this._serverStorage&&this._serverCfg)if("edit"===t){const i=this._serverCfg.spaces.find(t=>t.id===e);if(!i)return;const s=Fi(i),o=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?Ei(i.settings.custom_fill):null,n="none"===s.fill?{...o||Ii,a:0}:o;this._spaceDialog={mode:t,spaceId:e,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:s.showBorders,showNames:s.showNames,hideDecor:s.hideDecor,hideOpenings:s.hideOpenings,roomColor:s.color,roomOpacity:s.opacity,fillMode:"none"===s.fill?"custom":s.fill,customFill:n,glowEnabled:s.glow,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:Nn({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:zi,roomOpacity:Ai,fillMode:"custom",customFill:{...Ii,a:0},glowEnabled:!0,bgColor:null,bgMode:null,northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:5,busy:!1};else this._showToast(this._t("toast.integration_missing"))}async _pickPlanFile(t){const e=t.target,i=e.files?.[0];if(!i||!this._spaceDialog)return;const s={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[i.type]||(i.name.toLowerCase().endsWith(".svg")?"svg":"");if(!s)return void this._showToast(this._t("toast.plan_formats"));const o=new Uint8Array(await i.arrayBuffer());let n="";for(let t=0;t{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=a});URL.revokeObjectURL(a),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:r,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return j`
${this._t("space.loading")}
`;const e=t.saved||[];if(!e.length)return j`
${this._t("space.no_saved")}
`;return j`
${e.map(e=>j`
@@ -4062,15 +4091,15 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f
`)} -
`}async _saveSpaceDialog(){const t=this._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this._showToast(this._t("toast.plan_required"));const e="create"===t.mode&&0===(this._serverCfg?.spaces.length||0);this._spaceDialog={...t,busy:!0};try{const i="create"===t.mode?"s"+Date.now().toString(36):t.spaceId;let s=null;if("file"===t.source&&t.planFile){s={url:(await this.hass.callWS({type:"houseplan/plan/set",space_id:i,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let n=t.savedAspect||null;!s&&"file"===t.source&&t.planUrl&&!n&&this._aspectJob&&(n=await this._aspectJob||null);const o=this._serverCfg;let r;if("create"===t.mode)r={id:i,title:t.title.trim(),plan_url:null,view_box:[0,0,1,1],rooms:[]},o.spaces.push(r);else{if(r=o.spaces.find(t=>t.id===i),!r)throw new Error("space "+i+" is gone from the config");r.title=t.title.trim()}s?(r.plan_url=s.url,r.plan_aspect=s.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==r.plan_url&&(r.plan_url=t.planUrl,r.plan_aspect=n),"draw"===t.source&&(r.plan_url=null,r.plan_aspect=null,delete r.plan_x,delete r.plan_y,delete r.plan_scale,delete r.plan_scale_x,delete r.plan_scale_y,delete r.plan_angle);const a="draw"===t.source;r.settings={...r.settings||{},show_borders:!(!a||"create"!==t.mode)||t.showBorders,show_names:!(!a||"create"!==t.mode)||t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,custom_fill:t.customFill||void 0,glow_enabled:t.glowEnabled,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):20,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):25,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},r.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(cc,t.cellCm)):5,await this._saveConfigNow(),this._spaceDialog=null,"create"===t.mode&&(this._space=r.id),this._regSignature="",this._maybeRebuildDevices(),this._importQueue.length?this._openNextImport():e||this._importTotal>0?(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._showToast(this._t(e&&!this._importTotal?"toast.space_added_onboard":"import.done"))):(this._showToast("create"===t.mode?this._t("toast.space_added"):this._t("toast.space_saved")),"create"===t.mode&&("plan"!==this._mode?this._setMode("plan"):(this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._saveNav())))}catch(t){this._spaceDialog&&(this._spaceDialog={...this._spaceDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}async _deleteSpace(){const t=this._spaceDialog;if(!t||"edit"!==t.mode)return;const e=this._serverCfg.spaces.find(e=>e.id===t.spaceId);if(confirm(this._t("confirm.delete_space",{title:e.title}))){this._serverCfg.spaces=this._serverCfg.spaces.filter(e=>e.id!==t.spaceId);try{await this._saveConfigNow(),this._spaceDialog=null,this._space===t.spaceId&&(this._space=this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.space_deleted"))}catch(t){this._showToast(this._t("toast.delete_failed",{err:this._errText(t)}))}}}async _saveConfigNow(){this._cfgEpoch++;try{await this._writeConfig()}catch(t){throw"conflict"===t?.code&&await this._reloadConfigOnly(),t}}_startImport(){const t=this._importDialog;if(!t)return;const e=t.floors.filter(t=>t.checked).map(t=>t.name);this._importDialog=null,e.length?(this._importQueue=e,this._importTotal=e.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this._importQueue.shift();void 0!==t&&(this._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:zi,roomOpacity:Ai,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:null,northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:5,busy:!1})}_skipImport(){this._spaceDialog=null,this._importQueue.length?this._openNextImport():this._importTotal>0&&this._model.length&&(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._showToast(this._t("import.done")))}_renderImportDialog(){const t=this._importDialog,e=t.floors.filter(t=>t.checked).length;return j``}async _saveSpaceDialog(){const t=this._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this._showToast(this._t("toast.plan_required"));const e="create"===t.mode&&0===(this._serverCfg?.spaces.length||0);this._spaceDialog={...t,busy:!0};try{const i="create"===t.mode?"s"+Date.now().toString(36):t.spaceId;let s=null;if("file"===t.source&&t.planFile){s={url:(await this.hass.callWS({type:"houseplan/plan/set",space_id:i,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let o=t.savedAspect||null;!s&&"file"===t.source&&t.planUrl&&!o&&this._aspectJob&&(o=await this._aspectJob||null);const n=this._serverCfg;let r;if("create"===t.mode)r={id:i,title:t.title.trim(),plan_url:null,view_box:[0,0,1,1],rooms:[]},n.spaces.push(r);else{if(r=n.spaces.find(t=>t.id===i),!r)throw new Error("space "+i+" is gone from the config");r.title=t.title.trim()}s?(r.plan_url=s.url,r.plan_aspect=s.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==r.plan_url&&(r.plan_url=t.planUrl,r.plan_aspect=o),"draw"===t.source&&(r.plan_url=null,r.plan_aspect=null,delete r.plan_x,delete r.plan_y,delete r.plan_scale,delete r.plan_scale_x,delete r.plan_scale_y,delete r.plan_angle);const a="draw"===t.source;r.settings={...r.settings||{},show_borders:!(!a||"create"!==t.mode)||t.showBorders,show_names:!(!a||"create"!==t.mode)||t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,custom_fill:t.customFill||void 0,glow_enabled:t.glowEnabled,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):20,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):25,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},r.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(vc,t.cellCm)):5,await this._saveConfigNow(),this._spaceDialog=null,"create"===t.mode&&(this._space=r.id),this._regSignature="",this._maybeRebuildDevices(),this._importQueue.length?this._openNextImport():e||this._importTotal>0?(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._showToast(this._t(e&&!this._importTotal?"toast.space_added_onboard":"import.done"))):(this._showToast("create"===t.mode?this._t("toast.space_added"):this._t("toast.space_saved")),"create"===t.mode&&("plan"!==this._mode?this._setMode("plan"):(this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._saveNav())))}catch(t){this._spaceDialog&&(this._spaceDialog={...this._spaceDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}async _deleteSpace(){const t=this._spaceDialog;if(!t||"edit"!==t.mode)return;const e=this._serverCfg.spaces.find(e=>e.id===t.spaceId);if(confirm(this._t("confirm.delete_space",{title:e.title}))){this._serverCfg.spaces=this._serverCfg.spaces.filter(e=>e.id!==t.spaceId);try{await this._saveConfigNow(),this._spaceDialog=null,this._space===t.spaceId&&(this._space=this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.space_deleted"))}catch(t){this._showToast(this._t("toast.delete_failed",{err:this._errText(t)}))}}}async _saveConfigNow(){this._cfgEpoch++;try{await this._writeConfig()}catch(t){throw"conflict"===t?.code&&await this._reloadConfigOnly(),t}}_startImport(){const t=this._importDialog;if(!t)return;const e=t.floors.filter(t=>t.checked).map(t=>t.name);this._importDialog=null,e.length?(this._importQueue=e,this._importTotal=e.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this._importQueue.shift();void 0!==t&&(this._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:zi,roomOpacity:Ai,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:null,northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:5,busy:!1})}_skipImport(){this._spaceDialog=null,this._importQueue.length?this._openNextImport():this._importTotal>0&&this._model.length&&(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._showToast(this._t("import.done")))}_renderImportDialog(){const t=this._importDialog,e=t.floors.filter(t=>t.checked).length;return j`this._importDialog=null}>
${this._t("import.hint")}
${t.floors.map((e,i)=>j``)}
@@ -4082,32 +4111,32 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f ${this._t("import.start",{n:e})}
-
`}_sunGlobal(){const t=this._settingsDialog;return t?{...this._settings,north_deg:t.northDeg??void 0,bg_mode:t.bgMode,sun_rays:t.sunRays}:this._settings}_sunSpace(){const t=this._spaceDialog,e=this._curSpaceCfg?.settings||{};return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,north_deg:t.northDeg??void 0,bg_mode:t.bgMode??void 0,sun_rays:t.sunRays??void 0}:e}_effNorth(){return Co(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Do(this._sunGlobal(),this._sunSpace())}_effSunRays(){return To(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Po(this.hass):null}_renderSunRays(t){const e=W``;if(this._editing||!this._effSunRays())return this._sunFadeReset(),e;const i=this._effNorth(),s=null!==i?Po(this.hass):null;if(!s||s.elevation<=0)return this._sunFadeReset(),e;if(n=s.elevation,Number(n)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return e;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var n;const o=`${t.id}|${s.azimuth}|${s.elevation}|${i}|${this._cfgEpoch}`;if(!this._sunRaysCache||this._sunRaysCache.key!==o){const e=t.rooms.map(t=>({id:t.id||"",poly:Fe(t)})).filter(t=>!!t.id&&!!t.poly),n=this._openingsR.filter(t=>"window"===t.type).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),r=this._spaceWalls,a=this._openPairs().flatMap(t=>t.segs),l=this._openingWallIndexFor(t,a).value,c={},h={};if(r.length){for(const i of e){const e=Mr(t.rooms,i.id,r,a,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc);e&&(c[i.id]=e)}for(const t of n){const e=Er(l,{x:t.x,y:t.y,angle:t.angle,length:t.length});e.cm>0&&(h[t.id]=Vo(e.cm,this._cellCm,this._gridPitch))}}let d=xo(e,n,s.azimuth,s.elevation,i,r.length?c:void 0,r.length?h:void 0);const p=this._physicalBodiesR(t);p.length&&(d=d.map(t=>{const e=function(t,e,i){return i>0?t.map(t=>function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<=2)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const 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.slice(0,-1).concat(n.slice(0,-1))}([...t,...t.map(t=>[t[0]+e[0]*i,t[1]+e[1]*i])])).filter(t=>t.length>=3):t}(p,t.dir,t.len),i=t.polys.map(t=>Vr(t,e));return{...t,paths:i.map(jr).filter(Boolean),polys:i.flatMap(Yr)}}).filter(t=>t.paths?.length||t.polys.length)),this._sunRaysCache={key:o,rays:d,rims:d.map(t=>function(t,e=1e-4){const[i,s]=t.dir,n=-s,o=i,r=[];for(const a of[t.a,t.b]){const l=[];for(const r of t.polys)for(let t=0;te)continue;if(Math.abs((h[0]-a[0])*n+(h[1]-a[1])*o)>e)continue;const d=(c[0]-a[0])*i+(c[1]-a[1])*s,p=(h[0]-a[0])*i+(h[1]-a[1])*s;Math.abs(p-d)<=e||l.push(dt[0]-e[0]);const c=[];for(const t of l){const i=c[c.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):c.push([t[0],t[1]])}for(const[t,e]of c)r.push([[a[0]+i*t,a[1]+s*t],[a[0]+i*e,a[1]+s*e]])}return r}(t))}}const r=this._sunRaysCache.rays,a=this._sunRaysCache.rims;if(!r.length)return e;const l=(c=yo(s.elevation).warmth,qi("#ffe9c2","#ff9a45",vo(c)));var c;const h=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],d=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]];return W` - ${r.map((t,e)=>{const i=(t.a[0]+t.b[0])/2,s=(t.a[1]+t.b[1])/2,n=i+t.normal[0]*t.depth,o=s+t.normal[1]*t.depth;return W` - ${h.map(([t,e])=>W``}_sunGlobal(){const t=this._settingsDialog;return t?{...this._settings,north_deg:t.northDeg??void 0,bg_mode:t.bgMode,sun_rays:t.sunRays}:this._settings}_sunSpace(){const t=this._spaceDialog,e=this._curSpaceCfg?.settings||{};return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,north_deg:t.northDeg??void 0,bg_mode:t.bgMode??void 0,sun_rays:t.sunRays??void 0}:e}_effNorth(){return Nn(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Fn(this._sunGlobal(),this._sunSpace())}_effSunRays(){return On(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Ln(this.hass):null}_renderSunRays(t){const e=B``;if(this._editing||!this._effSunRays())return this._sunFadeReset(),e;const i=this._effNorth(),s=null!==i?Ln(this.hass):null;if(!s||s.elevation<=0)return this._sunFadeReset(),e;if(o=s.elevation,Number(o)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return e;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var o;const n=`${t.id}|${s.azimuth}|${s.elevation}|${i}|${this._cfgEpoch}`;if(!this._sunRaysCache||this._sunRaysCache.key!==n){const e=t.rooms.map(t=>({id:t.id||"",poly:Fe(t)})).filter(t=>!!t.id&&!!t.poly),o=this._openingsR.filter(t=>"window"===t.type).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),r=this._spaceWalls,a=this._openPairs().flatMap(t=>t.segs),l=this._openingWallIndexFor(t,a).value,c={},h={};if(r.length){for(const i of e){const e=Er(t.rooms,i.id,r,a,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc);e&&(c[i.id]=e)}for(const t of o){const e=Br(l,{x:t.x,y:t.y,angle:t.angle,length:t.length});e.cm>0&&(h[t.id]=ir(e.cm,this._cellCm,this._gridPitch))}}let d=An(e,o,s.azimuth,s.elevation,i,r.length?c:void 0,r.length?h:void 0);const p=this._physicalBodiesR(t);p.length&&(d=d.map(t=>{const e=function(t,e,i){return i>0?t.map(t=>function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<=2)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.slice(0,-1).concat(o.slice(0,-1))}([...t,...t.map(t=>[t[0]+e[0]*i,t[1]+e[1]*i])])).filter(t=>t.length>=3):t}(p,t.dir,t.len),i=t.polys.map(t=>ia(t,e));return{...t,paths:i.map(Xr).filter(Boolean),polys:i.flatMap(oa)}}).filter(t=>t.paths?.length||t.polys.length)),this._sunRaysCache={key:n,rays:d,rims:d.map(t=>function(t,e=1e-4){const[i,s]=t.dir,o=-s,n=i,r=[];for(const a of[t.a,t.b]){const l=[];for(const r of t.polys)for(let t=0;te)continue;if(Math.abs((h[0]-a[0])*o+(h[1]-a[1])*n)>e)continue;const d=(c[0]-a[0])*i+(c[1]-a[1])*s,p=(h[0]-a[0])*i+(h[1]-a[1])*s;Math.abs(p-d)<=e||l.push(dt[0]-e[0]);const c=[];for(const t of l){const i=c[c.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):c.push([t[0],t[1]])}for(const[t,e]of c)r.push([[a[0]+i*t,a[1]+s*t],[a[0]+i*e,a[1]+s*e]])}return r}(t))}}const r=this._sunRaysCache.rays,a=this._sunRaysCache.rims;if(!r.length)return e;const l=(c=Dn(s.elevation).warmth,qi("#ffe9c2","#ff9a45",Cn(c)));var c;const h=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],d=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]];return B` + ${r.map((t,e)=>{const i=(t.a[0]+t.b[0])/2,s=(t.a[1]+t.b[1])/2,o=i+t.normal[0]*t.depth,n=s+t.normal[1]*t.depth;return B` + ${h.map(([t,e])=>B``)} - ${d.map(([t,e])=>W` + ${d.map(([t,e])=>B``)} `})} - ${r.map((t,e)=>t.paths?.length?t.paths.map(t=>W``):t.polys.map(t=>W`t.paths?.length?t.paths.map(t=>B``):t.polys.map(t=>B``))} - ${r.map((t,e)=>(a[e]||[]).map(t=>W`(a[e]||[]).map(t=>B``))} - `}_skyPlan(){const t=this._editing||"daynight"!==this._effBgMode()?null:this._sunNow();if(!t)return this._skyElev=null,void(this._skySnap=!1);const e=So(t.elevation);var i,s;i=this._skyElev,s=e,(null===i||!Number.isFinite(i)||Math.abs(s-i)>=3)&&(this._skySnap=!0),this._skyElev=e}_skyRelease(){this._skySnap&&!this._skySnapRaf&&(this._skySnapRaf=requestAnimationFrame(()=>{this._skySnapRaf=requestAnimationFrame(()=>{this._skySnapRaf=0,this._skySnap=!1,this.requestUpdate()})}))}_sunFadeReset(){this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunShown=!1,this._sunOut=!1}_compassPoint(t){const e=t.currentTarget.getBoundingClientRect(),i=t.clientX-(e.left+e.width/2),s=t.clientY-(e.top+e.height/2);if(Math.hypot(i,s)<5)return;let n=Math.round(180*Math.atan2(i,-s)/Math.PI);t.shiftKey&&(n=15*Math.round(n/15)),n=(n%360+360)%360,this._settingsDialog={...this._settingsDialog,northDeg:n}}_renderCompass(){const t=this._settingsDialog.northDeg;return j``}_skyPlan(){const t=this._editing||"daynight"!==this._effBgMode()?null:this._sunNow();if(!t)return this._skyElev=null,void(this._skySnap=!1);const e=In(t.elevation);var i,s;i=this._skyElev,s=e,(null===i||!Number.isFinite(i)||Math.abs(s-i)>=3)&&(this._skySnap=!0),this._skyElev=e}_skyRelease(){this._skySnap&&!this._skySnapRaf&&(this._skySnapRaf=requestAnimationFrame(()=>{this._skySnapRaf=requestAnimationFrame(()=>{this._skySnapRaf=0,this._skySnap=!1,this.requestUpdate()})}))}_sunFadeReset(){this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunShown=!1,this._sunOut=!1}_compassPoint(t){const e=t.currentTarget.getBoundingClientRect(),i=t.clientX-(e.left+e.width/2),s=t.clientY-(e.top+e.height/2);if(Math.hypot(i,s)<5)return;let o=Math.round(180*Math.atan2(i,-s)/Math.PI);t.shiftKey&&(o=15*Math.round(o/15)),o=(o%360+360)%360,this._settingsDialog={...this._settingsDialog,northDeg:o}}_renderCompass(){const t=this._settingsDialog.northDeg;return j`{t.currentTarget.setPointerCapture(t.pointerId),this._compassDrag=!0,this._compassPoint(t)}} @pointermove=${t=>{this._compassDrag&&this._compassPoint(t)}} @pointerup=${()=>this._compassDrag=!1} @pointercancel=${()=>this._compassDrag=!1}> - ${[0,45,90,135,180,225,270,315].map(t=>W`B``)} @@ -4115,42 +4144,44 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f ${this._t("gs.north_letter")} ${null===t?"—":t+"°"} - `}_stageBg(t){if("daynight"===this._effBgMode()){const t=this._sunNow();if(t)return yo(So(t.elevation)).bg}const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":Oi(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":t.bgColor||"")||s}_stageBgHex(){const t=this._stageEl;if(t){const e=getComputedStyle(t).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(e)return"#"+e.slice(1,4).map(t=>(+t).toString(16).padStart(2,"0")).join("")}return"#111111"}async _runAlignToGrid(){const t=this._alignDialog;if(t&&!t.busy&&this._serverCfg){this._clearGeometryGesture(),this._alignDialog={...t,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain;const e=await this.hass.callWS({type:"houseplan/plan/optimize",config:t.config,layout:t.layout,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});this._serverCfg=t.config,this._layout=t.layout,this._geometryHistory.clear(),this._cfgRev=e?.config_rev??this._cfgRev+1,this._layoutRev=e?.layout_rev??this._layoutRev+1,this._canOptimizeUndo=!!e?.can_undo,this._dirtyPos.clear(),this._sentPos.clear(),this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this._alignDialog=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved),m:String(t.report.migrated+t.report.canonicalized+t.report.wallsMerged+t.report.spansMerged)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),"conflict"===t?.code&&await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}async _undoPlanOptimization(){if(this._canOptimizeUndo&&!this._optimizeUndoBusy){this._clearGeometryGesture(),this._optimizeUndoBusy=!0,this.requestUpdate();try{await this.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});const[t,e]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._serverCfg=t?.config||this._serverCfg,this._cfgRev=t?.rev??this._cfgRev,this._layout=e?.layout||this._layout,this._geometryHistory.clear(),this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!1,this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this.requestUpdate(),this._showToast(this._t("gs.optimize_undone"))}catch(t){this._canOptimizeUndo=!1,this._showToast(this._t("toast.error",{err:this._errText(t)}))}finally{this._optimizeUndoBusy=!1,this.requestUpdate()}}}_setFillColor(t,e){const i=this._settingsDialog;this._settingsDialog={...i,colors:{...i.colors,[t]:{...i.colors[t],...e}}}}async _saveSettingsDialog(){const t=this._settingsDialog;if(t&&!t.busy){this._settingsDialog={...t,busy:!0};try{const e=this._serverCfg,i=JSON.stringify(t.colors)===JSON.stringify(Li),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const n=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(n)&&n>0&&300!==Math.round(n)?s.glow_radius_cm=Math.round(n):delete s.glow_radius_cm,t.bgColor?s.bg_color=t.bgColor:delete s.bg_color,null!==t.northDeg&&Number.isInteger(t.northDeg)&&t.northDeg>=0&&t.northDeg<=359?s.north_deg=t.northDeg:delete s.north_deg,"daynight"===t.bgMode?s.bg_mode="daynight":delete s.bg_mode,t.sunRays?s.sun_rays=!0:delete s.sun_rays,delete s.weather_entity,this._serverCfg={...e,settings:s},await this._saveConfigNow(),this._settingsDialog=null,this.requestUpdate(),this._showToast(this._t("gs.saved"))}catch(t){this._settingsDialog&&(this._settingsDialog={...this._settingsDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}_boolInput(t,e,i=!1){const s=t=>e(!!t.target.checked);return customElements.get("ha-switch")?j``:j``}_rangeInput(t,e,i,s,n){const o=t=>{const e=Number(t.target.value);Number.isFinite(e)&&n(e)};return customElements.get("ha-slider")?j``:j``}_renderColorRow(t,e){const i=this._settingsDialog.colors[t];return j`
+ `}_stageBg(t){if("daynight"===this._effBgMode()){const t=this._sunNow();if(t)return Dn(In(t.elevation)).bg}const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":Oi(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":t.bgColor||"")||s}_stageBgHex(){const t=this._stageEl;if(t){const e=getComputedStyle(t).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(e)return"#"+e.slice(1,4).map(t=>(+t).toString(16).padStart(2,"0")).join("")}return"#111111"}async _runAlignToGrid(){const t=this._alignDialog;if(t&&!t.busy&&this._serverCfg){this._clearGeometryGesture(),this._alignDialog={...t,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain;const e=await this.hass.callWS({type:"houseplan/plan/optimize",config:t.config,layout:t.layout,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});this._serverCfg=t.config,this._layout=t.layout,this._geometryHistory.clear(),this._cfgRev=e?.config_rev??this._cfgRev+1,this._layoutRev=e?.layout_rev??this._layoutRev+1,this._canOptimizeUndo=!!e?.can_undo,this._dirtyPos.clear(),this._sentPos.clear(),this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this._alignDialog=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved),m:String(t.report.migrated+t.report.canonicalized+t.report.wallsMerged+t.report.spansMerged)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),"conflict"===t?.code&&await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}async _undoPlanOptimization(){if(this._canOptimizeUndo&&!this._optimizeUndoBusy){this._clearGeometryGesture(),this._optimizeUndoBusy=!0,this.requestUpdate();try{await this.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});const[t,e]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._serverCfg=t?.config||this._serverCfg,this._cfgRev=t?.rev??this._cfgRev,this._layout=e?.layout||this._layout,this._geometryHistory.clear(),this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!1,this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this.requestUpdate(),this._showToast(this._t("gs.optimize_undone"))}catch(t){this._canOptimizeUndo=!1,this._showToast(this._t("toast.error",{err:this._errText(t)}))}finally{this._optimizeUndoBusy=!1,this.requestUpdate()}}}_setFillColor(t,e){const i=this._settingsDialog;this._settingsDialog={...i,colors:{...i.colors,[t]:{...i.colors[t],...e}}}}async _saveSettingsDialog(){const t=this._settingsDialog;if(t&&!t.busy){this._settingsDialog={...t,busy:!0};try{const e=this._serverCfg,i=JSON.stringify(t.colors)===JSON.stringify(Li),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const o=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(o)&&o>0&&300!==Math.round(o)?s.glow_radius_cm=Math.round(o):delete s.glow_radius_cm,t.bgColor?s.bg_color=t.bgColor:delete s.bg_color,null!==t.northDeg&&Number.isInteger(t.northDeg)&&t.northDeg>=0&&t.northDeg<=359?s.north_deg=t.northDeg:delete s.north_deg,"daynight"===t.bgMode?s.bg_mode="daynight":delete s.bg_mode,t.sunRays?s.sun_rays=!0:delete s.sun_rays,delete s.weather_entity,this._serverCfg={...e,settings:s},await this._saveConfigNow(),this._settingsDialog=null,this.requestUpdate(),this._showToast(this._t("gs.saved"))}catch(t){this._settingsDialog&&(this._settingsDialog={...this._settingsDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}_boolInput(t,e,i=!1){const s=t=>e(!!t.target.checked);return customElements.get("ha-switch")?j``:j``}_rangeInput(t,e,i,s,o,n=!1,r){const a=t=>{const e=Number(t.target.value);Number.isFinite(e)&&o(e)};return customElements.get("ha-slider")?j``:j``}_renderColorRow(t,e){const i=this._settingsDialog.colors[t];return j`
${this._t(e)} this._setFillColor(t,{c:e.target.value})} /> ${this._rangeInput(0,100,1,Math.round(100*i.a),e=>this._setFillColor(t,{a:e/100}))} ${Math.round(100*i.a)}% -
`}get _glowRadiusCm(){const t=Number(this._settings.glow_radius_cm);return Number.isFinite(t)&&t>0?t:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const t=this._glowRadiusCm;return this._imperial?String(Math.round(t/30.48*10)/10):String(t/100)}_renderGlowLayer(t,e){const i=this._fillColors,s=this._glowRadiusCm/this._cellCm*this._gridPitch,n=this._gridPitch,o=t.rooms.map(t=>({r:t,poly:Fe(t)})).filter(t=>!!t.poly),r=o.filter(({r:t})=>rs(e.glow,t));if(!r.length)return W``;const a=this._openingsR.filter(t=>"window"!==t.type),l=this._spaceWalls,c=this._openPairs().flatMap(t=>t.segs),h=this._physicalBodiesR(t),d=new Map;if(l.length)for(const e of a){const i=e.angle*Math.PI/180,s=Math.cos(i)*e.rlen/2,n=Math.sin(i)*e.rlen/2,o=xr(t.rooms,l,c,[e.rx-s,e.ry-n,e.rx+s,e.ry+n],this._wallKeyPitch,this._cellCm,this._gridPitch,Cc);o>0&&d.set(e.id,Vo(o,this._cellCm,this._gridPitch))}const p=new Map;for(const e of an(this._planHass,this._devices.filter(e=>e.space===t.id)))e.on&&e.castsGlow&&e.device.id&&!p.has(e.device.id)&&p.set(e.device.id,e.eid);const u=[];for(const e of this._devices){if(e.space!==t.id)continue;const r=p.get(e.id);if(!r)continue;const _=Vi(this.hass.states[r],i.glow_light.c);if(!_)continue;const m=Number(e.marker?.glow_radius_cm),g=Number.isFinite(m)&&m>0?m/this._cellCm*this._gridPitch:s,f=this._pos(e),v=[...o].reverse().find(t=>this._pointInRoom([f.x,f.y],t.r));let y=null;const b=v?`${t.id}|${this._cfgEpoch}|${f.x.toFixed(4)},${f.y.toFixed(4)}|${g.toFixed(4)}`:"",w=v?uc(this._glowClipCache,b):{hit:!1};if(w.hit)y=w.value;else if(v){const e=h.length?Jr(h,[f.x,f.y],g):[],i=v.r.id?Xi(v.r.id,t.rooms):new Set([v.r.id]),s=o.filter(t=>t.r.id&&i.has(t.r.id)),r=s.length?s:[v],p=r.map(i=>{const s=l.length&&i.r.id&&Mr(t.rooms,i.r.id,l,c,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc)||i.poly;return e.length?jr(Vr(s,e)):"M "+s.map(t=>t[0]+" "+t[1]).join(" L ")+" Z"}),u=o.filter(t=>!r.includes(t)).map(t=>t.poly);for(const t of a){const i=r.some(e=>{const i=Ge([t.rx,t.ry],e.poly);return i&&Math.hypot(i[0]-t.rx,i[1]-t.ry)<=.75*n});if(!i)continue;const s=t.angle*Math.PI/180,o=Math.cos(s)*t.rlen/2,a=Math.sin(s)*t.rlen/2;if(!Yi([t.rx,t.ry],t.angle,[f.x,f.y],u,.6*n))continue;const l=Ki([f.x,f.y],[t.rx-o,t.ry-a],[t.rx+o,t.ry+a],g,170,d.get(t.id)||0);l&&p.push(e.length?jr(Vr(l,e)):"M "+l.map(t=>t[0]+" "+t[1]).join(" L ")+" Z")}y=p,_c(this._glowClipCache,b,y,256)}u.push({pos:f,c:_.c,alpha:i.glow_light.a*_.bri,clip:y,r:g})}if(!u.length)return W``;const _=r.length===o.length?null:r.map(({r:e,poly:i})=>{const s=l.length&&e.id&&Mr(t.rooms,e.id,l,c,this._wallKeyPitch,this._cellCm,this._gridPitch,Cc)||i,n=this._cleanFloor(e,s,t).path,r=ii(s,o.filter(t=>t.r!==e).map(t=>t.poly)),a=t=>"M "+t.map(t=>t[0]+" "+t[1]).join(" L ")+" Z";return[n||a(s),...r.map(a)].join(" ")});return W` - ${u.map((t,e)=>W` +
`}get _glowRadiusCm(){const t=Number(this._settings.glow_radius_cm);return Number.isFinite(t)&&t>0?t:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const t=this._glowRadiusCm;return this._imperial?String(Math.round(t/30.48*10)/10):String(t/100)}_renderGlowLayer(t,e){const i=this._fillColors,s=this._glowRadiusCm/this._cellCm*this._gridPitch,o=this._gridPitch,n=t.rooms.map(t=>({r:t,poly:Fe(t)})).filter(t=>!!t.poly),r=n.filter(({r:t})=>ps(e.glow,t));if(!r.length)return B``;const a=this._openingsR.filter(t=>"window"!==t.type),l=this._spaceWalls,c=this._openPairs().flatMap(t=>t.segs),h=this._physicalBodiesR(t),d=new Map;if(l.length)for(const e of a){const i=e.angle*Math.PI/180,s=Math.cos(i)*e.rlen/2,o=Math.sin(i)*e.rlen/2,n=Ar(t.rooms,l,c,[e.rx-s,e.ry-o,e.rx+s,e.ry+o],this._wallKeyPitch,this._cellCm,this._gridPitch,Nc);n>0&&d.set(e.id,ir(n,this._cellCm,this._gridPitch))}const p=mo(this._planHass,this._devices.filter(e=>e.space===t.id)),u=new Map;for(const t of p){if(!t.device.id)continue;const e=u.get(t.device.id)||[];e.push(t),u.set(t.device.id,e)}const _=[];for(const e of this._devices){if(e.space!==t.id)continue;const r=go(u.get(e.id)||[]);if(!r)continue;const p=Xi(this._planHass.states[r.eid],e.marker?.glow_color,i.glow_light.c);if(!p)continue;const m=Number(e.marker?.glow_radius_cm),g=Number.isFinite(m)&&m>0?m/this._cellCm*this._gridPitch:s,f=this._pos(e),v=[...n].reverse().find(t=>this._pointInRoom([f.x,f.y],t.r));let y=null;const b=v?`${t.id}|${this._cfgEpoch}|${f.x.toFixed(4)},${f.y.toFixed(4)}|${g.toFixed(4)}`:"",w=v?kc(this._glowClipCache,b):{hit:!1};if(w.hit)y=w.value;else if(v){const e=h.length?na(h,[f.x,f.y],g):[],i=v.r.id?os(v.r.id,t.rooms):new Set([v.r.id]),s=n.filter(t=>t.r.id&&i.has(t.r.id)),r=s.length?s:[v],p=r.map(i=>{const s=l.length&&i.r.id&&Er(t.rooms,i.r.id,l,c,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc)||i.poly;return e.length?Xr(ia(s,e)):"M "+s.map(t=>t[0]+" "+t[1]).join(" L ")+" Z"}),u=n.filter(t=>!r.includes(t)).map(t=>t.poly);for(const t of a){const i=r.some(e=>{const i=We([t.rx,t.ry],e.poly);return i&&Math.hypot(i[0]-t.rx,i[1]-t.ry)<=.75*o});if(!i)continue;const s=t.angle*Math.PI/180,n=Math.cos(s)*t.rlen/2,a=Math.sin(s)*t.rlen/2;if(!es([t.rx,t.ry],t.angle,[f.x,f.y],u,.6*o))continue;const l=ts([f.x,f.y],[t.rx-n,t.ry-a],[t.rx+n,t.ry+a],g,170,d.get(t.id)||0);l&&p.push(e.length?Xr(ia(l,e)):"M "+l.map(t=>t[0]+" "+t[1]).join(" L ")+" Z")}y=p,$c(this._glowClipCache,b,y,256)}_.push({pos:f,c:p.c,alpha:Qi(p.bri,i.glow_light.a),clip:y,r:g})}if(!_.length)return B``;const m=r.length===n.length?null:r.map(({r:e,poly:i})=>{const s=l.length&&e.id&&Er(t.rooms,e.id,l,c,this._wallKeyPitch,this._cellCm,this._gridPitch,Nc)||i,o=this._cleanFloor(e,s,t).path,r=ii(s,n.filter(t=>t.r!==e).map(t=>t.poly)),a=t=>"M "+t.map(t=>t[0]+" "+t[1]).join(" L ")+" Z";return[o||a(s),...r.map(a)].join(" ")});return B` + ${_.map((t,e)=>B` - ${t.clip?W`${t.clip.map(t=>W``)}`:G}`)} - ${_?W`${_.map(t=>W``)}`:G} + ${t.clip?B`${t.clip.map(t=>B``)}`:W}`)} + ${m?B`${m.map(t=>B``)}`:W} ${""} - + - ${u.map((t,e)=>W` + ${_.map((t,e)=>B``)} + clip-path=${t.clip?`url(#hp-glowclip-${e})`:W}>`)} `}_renderAlignDialog(){const t=this._alignDialog,e=t.report;return j`this._alignDialog=null}>
${t.changed?j` - ${e.moved?j`

${this._t("gs.align_count",{n:String(e.moved),total:String(e.total),cm:String(t.cm)})}

`:G} - ${t.where?j`

${this._t("gs.align_where",{s:t.where})}

`:G} - ${e.rotated?j`

${this._t("gs.align_turned",{n:String(e.rotated)})}

`:G} - ${e.removedDrafts?j`

${this._t("gs.align_removed_drafts",{n:String(e.removedDrafts)})}

`:G} + ${e.moved?j`

${this._t("gs.align_count",{n:String(e.moved),total:String(e.total),cm:String(t.cm)})}

`:W} + ${t.where?j`

${this._t("gs.align_where",{s:t.where})}

`:W} + ${e.rotated?j`

${this._t("gs.align_turned",{n:String(e.rotated)})}

`:W} + ${e.removedDrafts?j`

${this._t("gs.align_removed_drafts",{n:String(e.removedDrafts)})}

`:W}

${this._t("gs.optimize_changes",{m:String(e.migrated),c:String(e.canonicalized),w:String(e.wallsMerged),s:String(e.spansMerged)})}

- ${e.glowSpacesMigrated||e.glowRoomsMigrated?j`

${this._t("gs.optimize_glow_migration",{spaces:String(e.glowSpacesMigrated),rooms:String(e.glowRoomsMigrated)})}

`:G} + ${e.glowSpacesMigrated||e.glowRoomsMigrated?j`

${this._t("gs.optimize_glow_migration",{spaces:String(e.glowSpacesMigrated),rooms:String(e.glowRoomsMigrated)})}

`:W}
${this._t("gs.align_warn")}
`:j`

${this._t("gs.align_none")}

`}
@@ -4159,7 +4190,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f ${t.changed?j` `:G} + `:W}
`}_renderSettingsDialog(){return j`this._settingsDialog=null}> @@ -4183,7 +4214,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f ${this._t("gs.glow_radius")} {const e=pc(t.target.value);null!=e&&e>0&&(this._settingsDialog={...this._settingsDialog,glowRadius:e})}} /> + @input=${t=>{const e=wc(t.target.value);null!=e&&e>0&&(this._settingsDialog={...this._settingsDialog,glowRadius:e})}} /> ${this._imperial?this._t("gs.unit_ft"):this._t("gs.unit_m")}
@@ -4204,7 +4235,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f ${this._settingsDialog.bgColor?j``:j`${this._t("gs.bg_theme")}`}
`:j`
${this._t("gs.bg_daynight_hint")}
`} - ${Po(this.hass)?G:j`
${this._t("gs.sun_missing")}
`} + ${Ln(this.hass)?W:j`
${this._t("gs.sun_missing")}
`}
${this._renderCompass()}
@@ -4214,9 +4245,9 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f placeholder=${this._t("gs.north_ph")} .value=${null===this._settingsDialog.northDeg?"":String(this._settingsDialog.northDeg)} @input=${t=>{const e=t.target.value.trim(),i=""===e?null:Math.round(Number(e));this._settingsDialog={...this._settingsDialog,northDeg:null!==i&&Number.isFinite(i)?Math.min(359,Math.max(0,i)):null}}} /> - ${null!==this._settingsDialog.northDeg?j``:G} + ${null!==this._settingsDialog.northDeg?j``:W}
- ${null===this._settingsDialog.northDeg?j`
${this._t("gs.north_hint")}
`:G} + ${null===this._settingsDialog.northDeg?j`
${this._t("gs.north_hint")}
`:W}
`:W}
${this._t("marker.controls_hint")}
@@ -5152,36 +5183,81 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f ${this.hass.states[e]?.attributes?.friendly_name||e} this._markerDialog={...t,controls:t.controls.filter(t=>t!==e)}}> `)} - `:G} + `:W} this._markerDialog={...t,controlsFilter:e.target.value}} /> ${t.controlsFilter.trim()?j`
- ${Object.keys(this.hass.states).filter(e=>sn(t.binding,[e],s).length>0&&!t.controls.includes(e)&&this._planEntityAvailable(e)).filter(e=>{const i=t.controlsFilter.trim().toLowerCase(),s=String(this.hass.states[e]?.attributes?.friendly_name||"");return e.toLowerCase().includes(i)||s.toLowerCase().includes(i)}).slice(0,8).map(e=>j``)} -
`:G} + `:W} ${this._bindingHasClimate(t.binding)?j``:G} - - -
- this._markerDialog={...t,glowRadius:e.target.value}} /> - ${this._imperial?this._t("gs.unit_ft"):this._t("gs.unit_m")} - ${this._t("marker.glow_radius_hint")} -
+ `:W} +
+ ${this._t("marker.light_role_label")} +

${this._t("marker.light_role_tip")}

+
+ + + +
+
+ +
+ ${this._t("marker.glow_color_label")} +

${this._t("marker.glow_color_tip")}

+
+ + + +
+ ${"auto"!==t.glowMode?j`
+ {this._markerDialog={...t,glowColor:e.detail.color,glowColorDrafted:!0,glowTouched:!0}}}> + ${"fixed"===t.glowMode?j` + ${this._t("marker.glow_brightness")} + ${this._rangeInput(1,100,1,t.glowBrightness,e=>{this._markerDialog={...t,glowBrightness:e,glowBrightnessDrafted:!0,glowTouched:!0}},d,this._t("marker.glow_brightness"))} + ${Math.round(t.glowBrightness)}%`:W} +
`:W} + ${"fixed"===t.glowMode?j`

${this._t("marker.glow_brightness_hint")}

`:W} + +
+ this._markerDialog={...t,glowRadius:e.target.value}} /> + ${this._imperial?this._t("gs.unit_ft"):this._t("gs.unit_m")} + ${this._t("marker.glow_radius_hint")} +
+
+ ${d?j`

+ ${p} +

`:W} ${customElements.get("ha-icon-picker")?j`this._markerDialog={...t,icon:t.autoIcon}}> ${this._t("marker.icon_pin_auto")} -

`:G} +

`:W} this._spaceDialog={...t,source:"draw"}} /> @@ -5323,9 +5399,9 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f
- {const i=pc(e.target.value);this._spaceDialog={...t,cellCm:null!=i&&i>0?Math.max(.1,Math.min(cc,i)):t.cellCm}}} /> + @input=${e=>{const i=wc(e.target.value);this._spaceDialog={...t,cellCm:null!=i&&i>0?Math.max(.1,Math.min(vc,i)):t.cellCm}}} /> ${this._t("space.scale_unit")}
@@ -5379,20 +5455,20 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f - ${"static"===(t.bgMode??Do(this._settings,{}))?j` + ${"static"===(t.bgMode??Fn(this._settings,{}))?j`
this._spaceDialog={...t,bgColor:e.target.value}} /> ${t.bgColor?j``:j`${this._t("space.bg_inherited")}`} -
`:G} + `:W}
{const i=e.target.value.trim(),s=""===i?null:Math.round(Number(i));this._spaceDialog={...t,northDeg:null!==s&&Number.isFinite(s)?Math.min(359,Math.max(0,s)):null}}} /> - ${null===t.northDeg?this._t("space.north_inherited",{v:null===Co(this._settings,{})?"—":String(Co(this._settings,{}))+"°"}):"°"} + ${null===t.northDeg?this._t("space.north_inherited",{v:null===Nn(this._settings,{})?"—":String(Nn(this._settings,{}))+"°"}):"°"}
{const i=pc(e.target.value);null!=i&&(this._spaceDialog={...t,tempMin:i})}} /> + @input=${e=>{const i=wc(e.target.value);null!=i&&(this._spaceDialog={...t,tempMin:i})}} /> – {const i=pc(e.target.value);null!=i&&(this._spaceDialog={...t,tempMax:i})}} /> + @input=${e=>{const i=wc(e.target.value);null!=i&&(this._spaceDialog={...t,tempMax:i})}} /> °C - `:G} + `:W} ${"custom"===e&&"custom"===t.fillMode?j`
${this._t("space.custom_fill")} @@ -5425,8 +5501,8 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f @hp-color-opacity-change=${e=>{this._spaceDialog={...t,customFill:{c:e.detail.color,a:e.detail.opacity}}}}> ${t.customFill?j``:G} -
`:G}`)} + ${this._t("btn.reset")}`:W} + `:W}`)} `:W} +
+ ${this._t("marker.light_role_label")} +

${this._t("marker.light_role_tip")}

+
+ + + +
+
+ +
+ ${this._t("marker.glow_color_label")} +

${this._t("marker.glow_color_tip")}

+
+ + + +
+ ${"auto"!==t.glowMode?j`
+ {this._markerDialog={...t,glowColor:e.detail.color,glowColorDrafted:!0,glowTouched:!0}}}> + ${"fixed"===t.glowMode?j` + ${this._t("marker.glow_brightness")} + ${this._rangeInput(1,100,1,t.glowBrightness,e=>{this._markerDialog={...t,glowBrightness:e,glowBrightnessDrafted:!0,glowTouched:!0}},d,this._t("marker.glow_brightness"))} + ${Math.round(t.glowBrightness)}%`:W} +
`:W} + ${"fixed"===t.glowMode?j`

${this._t("marker.glow_brightness_hint")}

`:W} + +
+ this._markerDialog={...t,glowRadius:e.target.value}} /> + ${this._imperial?this._t("gs.unit_ft"):this._t("gs.unit_m")} + ${this._t("marker.glow_radius_hint")} +
+
+ ${d?j`

+ ${p} +

`:W} ${customElements.get("ha-icon-picker")?j`this._markerDialog={...t,icon:t.autoIcon}}> ${this._t("marker.icon_pin_auto")} -

`:G} +

`:W} this._spaceDialog={...t,source:"draw"}} /> @@ -5323,9 +5399,9 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f
- {const i=pc(e.target.value);this._spaceDialog={...t,cellCm:null!=i&&i>0?Math.max(.1,Math.min(cc,i)):t.cellCm}}} /> + @input=${e=>{const i=wc(e.target.value);this._spaceDialog={...t,cellCm:null!=i&&i>0?Math.max(.1,Math.min(vc,i)):t.cellCm}}} /> ${this._t("space.scale_unit")}
@@ -5379,20 +5455,20 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f - ${"static"===(t.bgMode??Do(this._settings,{}))?j` + ${"static"===(t.bgMode??Fn(this._settings,{}))?j`
this._spaceDialog={...t,bgColor:e.target.value}} /> ${t.bgColor?j``:j`${this._t("space.bg_inherited")}`} -
`:G} + `:W}
{const i=e.target.value.trim(),s=""===i?null:Math.round(Number(i));this._spaceDialog={...t,northDeg:null!==s&&Number.isFinite(s)?Math.min(359,Math.max(0,s)):null}}} /> - ${null===t.northDeg?this._t("space.north_inherited",{v:null===Co(this._settings,{})?"—":String(Co(this._settings,{}))+"°"}):"°"} + ${null===t.northDeg?this._t("space.north_inherited",{v:null===Nn(this._settings,{})?"—":String(Nn(this._settings,{}))+"°"}):"°"}
{const i=pc(e.target.value);null!=i&&(this._spaceDialog={...t,tempMin:i})}} /> + @input=${e=>{const i=wc(e.target.value);null!=i&&(this._spaceDialog={...t,tempMin:i})}} /> – {const i=pc(e.target.value);null!=i&&(this._spaceDialog={...t,tempMax:i})}} /> + @input=${e=>{const i=wc(e.target.value);null!=i&&(this._spaceDialog={...t,tempMax:i})}} /> °C - `:G} + `:W} ${"custom"===e&&"custom"===t.fillMode?j`
${this._t("space.custom_fill")} @@ -5425,8 +5501,8 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a0c5f2ef813072841bf1731b08bc547af14f @hp-color-opacity-change=${e=>{this._spaceDialog={...t,customFill:{c:e.detail.color,a:e.detail.opacity}}}}> ${t.customFill?j``:G} -
`:G}`)} + ${this._t("btn.reset")}`:W} + `:W}`)}