diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..0b8bb72 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,30 @@ +name: Validate +on: + push: + pull_request: + schedule: + - cron: "0 4 * * 1" +jobs: + hacs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: HACS validation + 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 + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: { node-version: 22 } + - run: npm ci && npm run build + - name: Card bundle in sync with integration + run: cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..df61c8d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 JB (justbusiness) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index a407b01..d6d04a1 100755 --- a/README.md +++ b/README.md @@ -55,6 +55,13 @@ live_states: true ha-floorplan потребовал бы генератор SVG+конфига и всё равно не дал бы редактирование из UI. Своя карточка (~1 файл) оказалась дешевле и полностью повторяет прототип. Решение: **свой card**. +## Документация + +- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — устройство карточки и интеграции, координаты, WS API +- [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) — сборка, деплой, грабли окружения (обязательно к прочтению) +- [docs/CHANGELOG.md](docs/CHANGELOG.md) — история версий +- [docs/ROADMAP.md](docs/ROADMAP.md) — план до публикации в HACS (универсализация, редактор разметки, виртуальные устройства) + ## Разработка ```bash diff --git a/brand/icon.png b/brand/icon.png new file mode 100644 index 0000000..7939fbc Binary files /dev/null and b/brand/icon.png differ diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..6d2a264 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,92 @@ +# Архитектура House Plan + +Обновлено: 2026-07-04 (v1.2.2). Репозиторий = HACS-интеграция (категория **Integration**), +которая содержит и бэкенд (`custom_components/houseplan`), и Lovelace-карточку (`src/` → `dist/`). + +## Состав + +``` +houseplan-card/ +├─ src/ # исходники карточки (TypeScript + Lit 3) +│ ├─ houseplan-card.ts # карточка: рендер, состояния, drag, tooltip, sticky-шапка +│ ├─ editor.ts # GUI-редактор конфига (ha-form + селекторы) +│ ├─ rules.ts # правила иконок (iconFor), курирование, группы, приоритет доменов +│ └─ data/ +│ ├─ house.ts # геометрия: ROOMS (комнаты→area), FLOOR_VB (viewBox), названия +│ └─ backgrounds.ts # ВЕКТОРНЫЕ планы (SVG base64) + FLOOR_BG_RECT (позиционирование) +├─ dist/houseplan-card.js # сборка (rollup+terser), ~290 КБ, планы внутри +├─ custom_components/houseplan/ # интеграция HA +│ ├─ __init__.py # setup: Store, WS-команды, раздача JS (add_extra_js_url) +│ ├─ websocket_api.py # houseplan/layout/get|set|update +│ ├─ config_flow.py # одна запись; опция admin_only (правка только админам) +│ ├─ const.py # DOMAIN, STORAGE_KEY, VERSION, FRONTEND_URL +│ └─ frontend/houseplan-card.js # копия dist, раздаётся как /houseplan_files/houseplan-card.js +├─ assets/ # исходники планов: f1_plan.svg, f2_plan.svg (РЕМПЛАННЕР), *_bg.png (старые растровые) +├─ hacs.json # манифест HACS +└─ docs/ # эта документация +``` + +## Ключевые решения + +1. **Один репозиторий — интеграция + карточка.** Интеграция сама раздаёт JS + (`hass.http.async_register_static_paths` + `frontend.add_extra_js_url`), пользователю не + нужно прописывать ресурс Lovelace. Паттерн как у browser_mod/xiaomi_vacuum_map. +2. **Раскладка иконок — на сервере.** `helpers.storage.Store(1, "houseplan.layout")` → + `.storage/houseplan.layout`. Карточка читает/пишет через `hass.callWS` + (`houseplan/layout/get|set|update`). Fallback — localStorage (если интеграции нет). +3. **Без токена.** Всё из объекта `hass` фронтенда: `hass.states` (реактивно), + `hass.devices/entities/areas` (реестры). Никаких прямых WS-подключений. +4. **Реактивность.** Каждое изменение состояния в HA приводит к set hass → re-render. + Температуры/LQI/вкл-выкл live по определению (проверено подменой state). + +## Координатная система + +- Базовое пространство: **1489×1053** («пиксели» старого PNG-рендера, 1 ед. = 1 px). + Все комнаты, позиции иконок и viewBox этажей — в нём. НЕ менять без миграции раскладки. +- Векторные планы вставляются `` в прямоугольник `FLOOR_BG_RECT`: + - f1: scale **0.647**, offset **(490, 27)** → rect [490, 27, 774.2, 949.3] + - f2: scale **0.896**, offset **(351, 21)** → rect [351, 21, 1048.4, 961.4] + - вычислено растровой корреляцией (cv2.matchTemplate по бинаризованным картам темноты) + рендера SVG с эталонным PNG; точность ~1 px. Скрипты воспроизводимы (docs/DEVELOPMENT.md). +- Комнаты (`ROOMS`) сняппены к внутренним граням стен (полуавто: поиск ближайшей «тёмной линии» + по профилю + ручная доводка по overlay-рендерам). + +## Модель данных карточки (runtime) + +`DevItem`: id (device_id), name, model, area, floor, icon, entities[], primary (сущность для +more-info по приоритету доменов), temp, members[] (группа ламп), link/linkPrimary (Z2M-группа). + +Построение из реестров (`_buildDevices`), правила 1-в-1 из прототипа: +- показываются только устройства с area из списка комнат; +- скрываются: entry_type=service, интеграции из EXCLUDED_DOMAINS, model=Group, сцены, мосты, + под-устройства myheat, дубли по «имя|area»; +- **устройство с сущностью `lock.*` всегда получает `mdi:lock`** (TTLock-замки в реестре + называются «Дом»/«Терраса»/«Кладовка» — по имени не распознать); +- лампы (mdi:lightbulb) ≥2 в комнате схлопываются в группу `mdi:lightbulb-group` + (клик → меню: вся группа + отдельные). + +## Живые данные + +- Температура: сущность с device_class=temperature / °C / `_temperature$` → метка справа. +- LQI (zigbee): среднее по `*_linkquality`-сущностям → метка под иконкой; цвет + `lqiColor()`: ≤40 красный → ≥180 зелёный (hsl-градиент). Среднее по комнате — в тултипе комнаты. +- Классы состояния иконки: on (жёлтый), open (оранжевый: cover/valve/lock/binary_sensor + проблемных классов), unavail (прозрачность). + +## Размеры + +`icon_size` в конфиге = **% ширины видимой области плана** (дефолт 2.5). Реализация: +`.stage { container-type: inline-size }` + размеры в `cqw`. Легаси px-значения (>8) игнорируются. + +## Sticky-шапка + +`.head { position: sticky; top: var(--header-height, 56px) }`; ОБЯЗАТЕЛЬНО +`ha-card { overflow: visible }` — `overflow: hidden` ломает sticky. + +## WS API интеграции + +| Команда | Параметры | Ответ | +|---|---|---| +| `houseplan/layout/get` | — | `{layout: {device_id: {x,y}}}` | +| `houseplan/layout/set` | `layout` | `{ok}` (admin_only опционально) | +| `houseplan/layout/update` | `device_id`, `pos` | `{ok}` | diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md new file mode 100644 index 0000000..9bca38a --- /dev/null +++ b/docs/CHANGELOG.md @@ -0,0 +1,30 @@ +# Changelog + +## v1.2.2 — 2026-07-04 +- Тулбар карточки (вкладки этажей) закрепляется при скролле под шапкой HA + (`position: sticky; top: var(--header-height)`; ha-card overflow: visible). +- Инцидент: промежуточная сборка на нестабильном mount дала битый бандл, роняющий рендер + дашбордов; правило «собирать только в /tmp + md5-контроль» закреплено в DEVELOPMENT.md. + +## v1.2.1 — 2026-07-04 +- Иконки не увеличиваются при наведении/перетаскивании (убран transform: scale). + +## v1.2.0 — 2026-07-03 +- Границы комнат сняппены к стенам векторного плана. +- Zigbee LQI: значение под иконкой, среднее по комнате в тултипе, градиент красный→зелёный, + опция show_signal. +- `mdi:lock` для любых устройств с сущностью lock.* (TTLock). +- Выключатель прихожей: исправлена зона устройства в реестре HA (был в detskaia_elina). + +## v1.1.0 — 2026-07-03 +- Векторные подложки (SVG РЕМПЛАННЕР), автосовмещение масштаба/сдвига растровой корреляцией. +- Размер иконок в % ширины плана (container queries, дефолт 2.5%). + +## v1.0.1 — 2026-07-03 +- fix: вложенные SVG-фрагменты через lit svg`` (подложка/комнаты не рендерились). +- fix: версия из const вместо блокирующего чтения manifest.json в event loop. + +## v1.0.0 — 2026-07-03 +- Первый релиз: Lovelace-карточка (TS+Lit, без токена, от hass) + интеграция houseplan + (WS-хранилище раскладки, раздача JS). Перенос модели прототипа: курирование, группы ламп, + iconFor, температура, more-info, навигация в зоны, drag-раскладка. diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md new file mode 100644 index 0000000..078d5ff --- /dev/null +++ b/docs/DEVELOPMENT.md @@ -0,0 +1,58 @@ +# Разработка и деплой + +## Окружение (cowork-сессии) + +- Эталон кода — **git-репозиторий** (в сессии живёт в `/tmp/hpc`, восстанавливается из + `houseplan-card.git.bundle`: `git clone houseplan-card.git.bundle hpc`). +- Папка пользователя `houseplan/houseplan-card/` — зеркало репо (rsync после каждого коммита) + + актуальный `houseplan-card.git.bundle`. + +### ⚠️ Грабли файловой синхронизации (критично) +1. Сетевой mount иногда отдаёт файлы **обрезанными/перемешанными** — правки через Edit-tool + с Windows-стороны ненадёжны. Правило: **править python-патчами по чистой копии в /tmp, + писать через bash**, assert на count(old)==1. +2. **Сборку rollup вести ТОЛЬКО в /tmp/hpc** (`npm ci` уже сделан). Сборка на mount однажды + дала синтаксически валидный, но битый бандл («wi is not defined»), который валил рендер + ВСЕХ дашбордов HA (карточка грузится как extra_module на каждой странице!). +3. `.git` на mount не создаётся («Operation not permitted» на dot-каталогах) — поэтому bundle. + +## Сборка + +```bash +cd /tmp/hpc && npm ci # один раз +npx rollup -c # → dist/houseplan-card.js +node --check dist/houseplan-card.js +cp dist/houseplan-card.js custom_components/houseplan/frontend/ +``` + +## Деплой на дачу (ha.jbstudio.pro) + +- SSH: порт **323**, root, ключ `ha_jb` (пользователь загружает в чат; в песочнице /tmp/ha_jb, chmod 600). +- JS: `scp -P 323 -i /tmp/ha_jb dist/houseplan-card.js root@ha.jbstudio.pro:/config/custom_components/houseplan/frontend/` +- Интеграция целиком: tar c custom_components/houseplan (--exclude __pycache__) → tar x на сервере. +- **Проверка обязательна**: `md5sum` локально == на сервере == `curl http://homeassistant:8123/houseplan_files/houseplan-card.js | md5sum` + (внутри SSH-аддона `localhost` — НЕ HA, использовать хост `homeassistant`). +- Изменения Python требуют рестарта HA (`ha core restart`, держит соединение до конца, HTTP + поднимается через 1–3 мин). Изменения JS — только обновление страницы (static path отдаётся + с no-cache). +- После деплоя JS — проверить в браузере (Ctrl+F5) и console (не должно быть ошибок из + houseplan-card.js; битый бандл роняет все дашборды). + +## Релиз + +Тег `vX.Y.Z` + GitHub Release → workflow `.github/workflows/release.yml` собирает и прикладывает +`houseplan-card.js`. Версию бампать синхронно: `src/houseplan-card.ts` (CARD_VERSION), +`package.json`, `custom_components/houseplan/manifest.json`, `custom_components/houseplan/const.py`. + +## Воспроизводимые скрипты (данные) + +- Извлечение геометрии/подложек из прототипа и генерация `src/data/*` — см. историю коммитов + и docs/ARCHITECTURE.md (трансформации SVG→базовое пространство: f1 0.647/(490,27), f2 0.896/(351,21)). +- Подгонка комнат: рендер плана с прямоугольниками поверх (cv2) → снап к стенам → ручная доводка. + +## Прод-объекты в HA (дача) + +- Дашборд `plan-doma`, панельный вид, карточка `custom:houseplan-card` (icon_size 2.5). +- Интеграция houseplan: entry загружен, `.storage/houseplan.layout` — раскладка (сервер). +- Старый прототип `/config/www/houseplan/` (iframe) сохранён как запасной, не трогать. +- Бэкапы configuration.yaml: `.bak-avgtemp` (до правки среднего датчика). diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md new file mode 100644 index 0000000..7ab9f4a --- /dev/null +++ b/docs/ROADMAP.md @@ -0,0 +1,73 @@ +# Роадмап: от «карты дачи» к публикуемой универсальной интеграции + +Цель: опубликовать в HACS (сначала custom repository, затем PR в default) универсальную +интеграцию «интерактивный план дома»: свои планы на пространство, ручная разметка комнат, +полуавтоматическое размещение устройств, скрытие/переименование/смена иконок, виртуальные +устройства. Никакого хардкода конкретного дома в коде. + +## Принципы (зафиксировано) +- Пишем полноценный компонент по паттернам HA dev docs, не захардкоженную фичу. +- Все данные конкретного дома — это **конфигурация инстанса** (server-side Store), + а не код/бандл. Текущие данные дачи станут первым мигрированным инстансом. +- Документировать всё сразу в docs/ (контекст сессий теряется). +- Версионирование хранилищ (Store minor_version + async_migrate) с первого дня. + +## Фаза 0 — Гигиена публикации (быстро, без новых фич) +- [x] hacs.json, manifest с обязательными ключами, структура custom_components/* +- [x] CI: hacs/action + hassfest (workflow validate.yml) — добавлено, проверить на GitHub +- [x] brand/icon.png +- [ ] Публичный GitHub-репозиторий: description, topics, issues on; первый Release v1.2.x +- [ ] README EN (основной) + README.ru.md; скриншоты/GIF (обязательны для витрины HACS) +- [ ] Заменить codeowners/documentation/issue_tracker на реальные URL после создания репо + +## Фаза 1 — Конфиг на сервере (декаплинг от дачи) ← ФУНДАМЕНТ +Новое хранилище `houseplan.config` (Store v1): +```json +{ "spaces": [ { "id": "f1", "title": "1 этаж", "plan": {"media_id": "...", "type": "svg"}, + "view_box": [x,y,w,h], "rooms": [{"id","name","area_id","x","y","w","h"}] } ], + "device_overrides": { "": {"hidden":bool,"icon":str,"name":str} }, + "virtual_devices": [ {"id","space","name","icon","x","y","note"?, "entity_id"?} ], + "settings": {"exclude_integrations": [...], "group_lights": bool, ...} } +``` +- WS API v2: `houseplan/config/get|set`, `houseplan/plan/upload` (файл плана → + `/houseplan/` через process-executor, отдача через static path), layout как сейчас. +- Карточка: при наличии server-config использует его; бандл-данные дачи становятся + **fallback-примером** и затем выпиливаются (миграционный скрипт зальёт их в Store). +- Единицы координат: нормированные (0..1 от плана) для новых конфигов — независимость от + разрешения исходника; миграция дачи пересчитает 1489×1053 → нормированные. + +## Фаза 2 — Редактор разметки в карточке +- Режим «Настройка» (отдельно от drag-раскладки): рисование/ресайз прямоугольников комнат + поверх плана, привязка к area (селектор ha-area-picker), редактирование viewBox (кадр). +- Позже: полигональные комнаты (SVG path), повороты планов. +- Загрузка плана из UI (file upload → WS) + выбор существующего media. + +## Фаза 3 — Управление устройствами +- Панель устройств в режиме настройки: список неразмещённых (с фильтрами), drag из панели + на план; авто-раскладка «сеткой по комнате» кнопкой. +- Оверрайды per-device: скрыть, своя иконка (ha-icon-picker), своё имя. Хранение в config. +- Настраиваемое курирование: исключения интеграций/доменов в options flow вместо хардкода. + +## Фаза 4 — Виртуальные устройства +- CRUD виртуальных маркеров (имя, иконка, координаты, заметка; опционально ссылка на + entity/URL): септик, кран, счётчик без датчика и т.п. Рендер как обычные иконки, + клик → карточка с заметкой или more-info привязанной сущности. + +## Фаза 5 — Полировка UX/фич +- Клик-действия по настройке: toggle для света/розеток, long-press → more-info. +- Тюнинг live-индикации (цвета по теме, badge-и), light-тема. +- Тултипы на тач-устройствах (long-press), доступность (клавиатура, aria). +- Опция LQI: порог «плохого» сигнала, скрытие меток на не-zigbee инстансах. + +## Фаза 6 — Качество и публикация +- Тесты: pytest (config_flow, websocket_api, миграции Store) + hassfest/hacs action в CI; + фронт: vitest на rules/geometry-утилиты. +- Типизация: strict TS-интерфейсы hass (custom-card-helpers или свои), mypy для python. +- Переводы integration+card: en + ru (translations/, локализация строк карточки). +- Quality scale bronze → silver чек-лист; PR в hacs/default; опционально PR в + home-assistant/brands (пока хватает brand/ в репо). + +## Открытые вопросы +- Имя для публикации: «House Plan Card»? домен `houseplan` не занят в HACS default — проверить. +- Лицензия MIT (в package.json уже MIT) — добавить LICENSE файл. +- Формат планов: SVG предпочтителен (вектор, вес), PNG поддержать обязательно.