diff --git a/custom_components/houseplan/const.py b/custom_components/houseplan/const.py index 77a1ac6..2d131cf 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.53.1" +VERSION = "1.54.0" DEFAULT_CONFIG: dict = { "spaces": [], diff --git a/custom_components/houseplan/frontend/houseplan-card.js b/custom_components/houseplan/frontend/houseplan-card.js index 3dba2ce..bdf6e76 100755 --- a/custom_components/houseplan/frontend/houseplan-card.js +++ b/custom_components/houseplan/frontend/houseplan-card.js @@ -2884,4 +2884,4 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow `} - `}}As.properties={_hdrH:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorTextDialog:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},As.ZOOM_MAX=8,As.ZOOM_MIN=.4,As._touchSeen=!1,As._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,As.styles=as,customElements.get("houseplan-card")||customElements.define("houseplan-card",As),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 v1.53.1 ","background:#3ea6ff;color:#04121f;font-weight:700",""); + `}}As.properties={_hdrH:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorTextDialog:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},As.ZOOM_MAX=8,As.ZOOM_MIN=.4,As._touchSeen=!1,As._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,As.styles=as,customElements.get("houseplan-card")||customElements.define("houseplan-card",As),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 v1.54.0 ","background:#3ea6ff;color:#04121f;font-weight:700",""); diff --git a/custom_components/houseplan/manifest.json b/custom_components/houseplan/manifest.json index 1c61537..bce0bdc 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.53.1" + "version": "1.54.0" } diff --git a/demo/srv/assets/houseplan-card.js b/demo/srv/assets/houseplan-card.js index 3dba2ce..bdf6e76 100755 --- a/demo/srv/assets/houseplan-card.js +++ b/demo/srv/assets/houseplan-card.js @@ -2884,4 +2884,4 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow `} - `}}As.properties={_hdrH:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorTextDialog:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},As.ZOOM_MAX=8,As.ZOOM_MIN=.4,As._touchSeen=!1,As._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,As.styles=as,customElements.get("houseplan-card")||customElements.define("houseplan-card",As),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 v1.53.1 ","background:#3ea6ff;color:#04121f;font-weight:700",""); + `}}As.properties={_hdrH:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorTextDialog:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},As.ZOOM_MAX=8,As.ZOOM_MIN=.4,As._touchSeen=!1,As._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,As.styles=as,customElements.get("houseplan-card")||customElements.define("houseplan-card",As),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 v1.54.0 ","background:#3ea6ff;color:#04121f;font-weight:700",""); diff --git a/dist/houseplan-card.js b/dist/houseplan-card.js index 3dba2ce..bdf6e76 100755 --- a/dist/houseplan-card.js +++ b/dist/houseplan-card.js @@ -2884,4 +2884,4 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow `} - `}}As.properties={_hdrH:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorTextDialog:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},As.ZOOM_MAX=8,As.ZOOM_MIN=.4,As._touchSeen=!1,As._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,As.styles=as,customElements.get("houseplan-card")||customElements.define("houseplan-card",As),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 v1.53.1 ","background:#3ea6ff;color:#04121f;font-weight:700",""); + `}}As.properties={_hdrH:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorTextDialog:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},As.ZOOM_MAX=8,As.ZOOM_MIN=.4,As._touchSeen=!1,As._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,As.styles=as,customElements.get("houseplan-card")||customElements.define("houseplan-card",As),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 v1.54.0 ","background:#3ea6ff;color:#04121f;font-weight:700",""); diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 565cf8a..a1e1e77 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,44 @@ # Changelog +## v1.54.0 — 2026-07-31 + +### Live robot vacuums + +The plan now shows the robot while it works. The device marker stays where +you put it — that is the dock — and a round puck drives the plan in real +time, pouring its path out from under itself. Everything is display only: +the card never commands the robot. + +- **Calibration without arithmetic.** «Настроить автоматически» matches the + robot's room list against your rooms by name and solves the transform in + one click. When names do not match, the fit panel lays the robot's rooms + over the plan as a dashed ghost: drag it into place, stretch it by the + corner handles, quarter-turn and mirror with two buttons. Mirror is on by + default — every robot map we have measured flips Y versus the screen. + One calibration per robot map, so two floors stay independent. +- **The path is recorded server-side.** The integration watches the robot + itself, so the trail records with no card open, survives page reloads, and + every screen sees the same line. The current run and one previous run are + kept — cleaned versus not-yet-cleaned at a glance. +- **«Показывать путь робота»**: never / while cleaning (default) / always. + Only the last mode also draws the previous run, faded. +- The trail never runs ahead of the icon: drawn segments lag one point and + the growing tip is glued to the puck's animated centre every frame. It is + drawn as a dark halo under a light core, so it stays readable over any + room fill. The puck teleports instead of gliding when the view changes — + zoom, floor switch or a return to the browser tab. +- Adapters: Xiaomi Cloud Map Extractor, dreame-vacuum (Tasshack), Valetudo. + Verified against a live Dreame X50 Master. + +### Also + +- **«Свет по источникам» is the default fill for new spaces** and leads the + options list. Existing plans are untouched: a space whose owner never + chose a fill still renders as before. +- **Fixed: the "what to run" search showed no results.** The list is a + scrollable box and, as a flex item, collapsed into a 1px sliver — the + matches were there, rendered into nothing. + ## v1.53.1 — 2026-07-30 - **Fix: the "what to run" search showed no results.** The results were diff --git a/docs/CHANGELOG.ru.md b/docs/CHANGELOG.ru.md index 8eca756..3d90d44 100755 --- a/docs/CHANGELOG.ru.md +++ b/docs/CHANGELOG.ru.md @@ -6,6 +6,45 @@ > **Правило проекта:** оба файла пополняются в одном коммите с самим > изменением — как и остальная документация (см. docs/STATUS.md). +## v1.54.0 — 2026-07-31 + +### Роботы-пылесосы вживую + +План теперь показывает робота за работой. Маркер устройства остаётся там, +куда вы его поставили — это база, — а по плану в реальном времени едет +круглая шайба, «выливая» путь из-под себя. Только отображение: карточка +ничего роботу не командует. + +- **Калибровка без арифметики.** «Настроить автоматически» сопоставляет + комнаты робота с вашими по именам и решает привязку в один клик. Если + имена не совпали, панель подгонки кладёт комнаты робота на план + пунктирным призраком: перетащите на место, растяните за уголки, поворот + на 90° и зеркало — двумя кнопками. Зеркало включено по умолчанию: у всех + измеренных нами роботов ось Y перевёрнута относительно экрана. Своя + калибровка на каждую карту робота, поэтому два этажа не мешают друг другу. +- **Путь пишется на сервере.** Интеграция следит за роботом сама, поэтому + след записывается даже без открытой карточки, переживает перезагрузку + страницы, и все экраны видят одну и ту же линию. Хранятся текущая уборка + и одна предыдущая — сразу видно, где робот уже прошёл, а где ещё нет. +- **«Показывать путь робота»**: никогда / во время уборки (по умолчанию) / + всегда. Прошлая уборка бледной линией рисуется только в последнем режиме. +- След никогда не обгоняет иконку: отрисованные отрезки отстают на точку, а + растущий кончик каждый кадр приклеен к центру едущей шайбы. Рисуется + тёмным ореолом со светлой сердцевиной — читается на любой заливке комнат. + При смене вида (зум, этаж, возврат на вкладку) шайба телепортируется, а + не едет через весь план. +- Поддержаны Xiaomi Cloud Map Extractor, dreame-vacuum (Tasshack) и + Valetudo. Проверено на живом Dreame X50 Master. + +### Кроме того + +- **«Свет по источникам» — режим по умолчанию для новых пространств** и + первый в списке. Существующие планы не трогаем: пространство, где режим + не выбирали, выглядит как раньше. +- **Исправлено: поиск «что запускать» не показывал результатов.** Список — + прокручиваемый блок, и как flex-элемент он схлопывался в полоску 1px: + совпадения были, но отрисовывались в ничто. + ## v1.53.1 — 2026-07-30 - **Фикс: поиск «что запускать» не показывал результатов.** Результаты были — diff --git a/docs/STATUS.md b/docs/STATUS.md index d622bf6..83508c3 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -15,12 +15,12 @@ | Item | State | |---|---| -| Version | **v1.53.1** everywhere (manifest, const.py, package.json, CARD_VERSION); deployed to the home instance | +| Version | **v1.54.0** everywhere (manifest, const.py, package.json, CARD_VERSION); deployed to the home instance | | Workflow | Since 2026-07-22: minor changes go to branch **`dev`** (build + smokes → deploy home → commit → push, NO release); releases are batched on the owner's command (merge dev→main, one tag, one release with a summary changelog, CI checked on dev beforehand) | | GitHub | https://github.com/Matysh/houseplan-card — **`main` carries every published release, the latest tag is the current version above**; `dev` is where work lands and is merged into `main` at release time (so `dev` is normally equal to or ahead of `main`, never behind). Push via SSH key `ha_jb` (remote git@github.com:…); API releases via the fine-grained PAT in `~/.git-credentials` (Contents R/W, issued 2026-07-23) | | CI | validate.yml (hacs + hassfest + frontend + backend) green; release.yml attaches the bundle on release publish | | HACS | Custom repository works. **Inclusion PR: hacs/default#9004** — open, valid, labeled, mergeable clean, never drafted. Queue: 1212 open, 835 older than ours. Merge rate COLLAPSED: 75 in July but almost all in the first decade, 0 in the last week (checked 2026-07-29) — maintainers process in rare bursts; ETA unknowable, months at best. Nothing actionable on our side | -| Home instance | ha.jbstudio.pro (SSH port **22222**, key `ha_jb`; HA config root is `/mnt/data/supervisor/homeassistant` — `/config` does NOT exist in this SSH environment), deployed **v1.53.1** via direct copy (HACS custom repo also installed) | +| Home instance | ha.jbstudio.pro (SSH port **22222**, key `ha_jb`; HA config root is `/mnt/data/supervisor/homeassistant` — `/config` does NOT exist in this SSH environment), deployed **v1.54.0** via direct copy (HACS custom repo also installed) | | Localization | UI en/ru (src/i18n/*.json), everything user-visible localized incl. kiosk popover | | Tests | Four layers: frontend unit (`npm test`, node:test over `test-build/`), pure backend (`pytest tests_backend`, runs anywhere), HA-harness backend (same folder, CI only — needs py3.13 + pytest-homeassistant-custom-component), and browser smokes (`demo/smoke_*.mjs`, headless chromium). **Counts are not written down here** — they went stale within two releases while the version line beside them was kept current, which reads as less coverage than exists (review R5-2). Run `npm run inventory` for the current numbers, or read them off the last CI run | | Vacuums | Live robot vacuums shipped (docs/VACUUM.md): puck, server-side trails with display modes, fit-panel calibration. Verified on a live Dreame X50 Master | diff --git a/package-lock.json b/package-lock.json index f0097d4..f32d312 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "houseplan-card", - "version": "1.53.1", + "version": "1.54.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "houseplan-card", - "version": "1.53.1", + "version": "1.54.0", "license": "MIT", "dependencies": { "lit": "^3.1.3", diff --git a/package.json b/package.json index efb523d..0e72f0d 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "houseplan-card", - "version": "1.53.1", + "version": "1.54.0", "description": "Interactive house plan Lovelace card for Home Assistant", "license": "MIT", "type": "module", diff --git a/src/houseplan-card.ts b/src/houseplan-card.ts index a1a27d0..09b71d8 100755 --- a/src/houseplan-card.ts +++ b/src/houseplan-card.ts @@ -42,7 +42,7 @@ import { cardStyles } from './styles'; import { fitInSquare, contentBounds, spaceModels } from './space-geometry'; import { langOf, t, type I18nKey } from './i18n'; -const CARD_VERSION = '1.53.1'; +const CARD_VERSION = '1.54.0'; const LS_KEY = 'houseplan_card_layout_v1'; const LS_CFG = 'houseplan_card_cfg_v1'; // cache of the server config+layout for instant rendering const LS_ZOOM = 'houseplan_card_zoom_v1';