feat v1.6.2: room field for all device markers (override placement, moves marker to room center); Edit button in info-card opens edit dialog

This commit is contained in:
JB
2026-07-04 13:21:35 +03:00
parent 74640318e9
commit e61d2c173f
6 changed files with 56 additions and 39 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ PLANS_DIR = "houseplan/plans" # относительно каталога ко
FILES_URL = "/houseplan_files/files" FILES_URL = "/houseplan_files/files"
FILES_DIR = "houseplan/files" FILES_DIR = "houseplan/files"
CONF_ADMIN_ONLY = "admin_only" CONF_ADMIN_ONLY = "admin_only"
VERSION = "1.6.1" VERSION = "1.6.2"
DEFAULT_CONFIG: dict = { DEFAULT_CONFIG: dict = {
"spaces": [], "spaces": [],
File diff suppressed because one or more lines are too long
+11 -8
View File
File diff suppressed because one or more lines are too long
+6
View File
@@ -1,5 +1,11 @@
# Changelog # Changelog
## v1.6.2 — 2026-07-04
- Поле «Комната» в диалоге устройства теперь для ВСЕХ значков (не только виртуальных):
для привязанных — «переопределить размещение» (по умолчанию по зоне устройства); смена
комнаты переставляет значок в её центр. Сохраняется в marker.space/area и выигрывает у зоны HA.
- В инфо-карточке добавлена кнопка «Редактировать» — открывает диалог редактирования устройства.
## v1.6.0 — 2026-07-04 (Фаза 3: редактор устройств) ## v1.6.0 — 2026-07-04 (Фаза 3: редактор устройств)
- Модель «маркеров» (config.markers[]): гибрид — авто-устройства HA появляются сами, маркеры - Модель «маркеров» (config.markers[]): гибрид — авто-устройства HA появляются сами, маркеры
правят/перепривязывают/дополняют их и описывают ручные/виртуальные значки. id маркера правят/перепривязывают/дополняют их и описывают ручные/виртуальные значки. id маркера
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "houseplan-card", "name": "houseplan-card",
"version": "1.6.1", "version": "1.6.2",
"description": "Interactive house plan Lovelace card for Home Assistant (dacha Kirillovskoe)", "description": "Interactive house plan Lovelace card for Home Assistant (dacha Kirillovskoe)",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
+26 -21
View File
@@ -12,7 +12,7 @@ import { FLOOR_BG, FLOOR_BG_RECT } from './data/backgrounds';
import { EXCLUDED_DOMAINS, iconFor, DOMAIN_PRIORITY } from './rules'; import { EXCLUDED_DOMAINS, iconFor, DOMAIN_PRIORITY } from './rules';
import './editor'; import './editor';
const CARD_VERSION = '1.6.1'; const CARD_VERSION = '1.6.2';
const LS_KEY = 'houseplan_card_layout_v1'; const LS_KEY = 'houseplan_card_layout_v1';
const NORM_W = 1000; // ширина рендер-пространства для нормированных конфигов const NORM_W = 1000; // ширина рендер-пространства для нормированных конфигов
@@ -671,7 +671,7 @@ class HouseplanCard extends LitElement {
const [kind, ref] = m.binding.split(':'); const [kind, ref] = m.binding.split(':');
if (kind === 'device') { if (kind === 'device') {
const dev = h.devices[ref]; const dev = h.devices[ref];
const area = dev?.area_id || m.area || ''; const area = m.area || dev?.area_id || '';
const space = (area && areaMap[area]?.space) || m.space || this._model[0]?.id || ''; const space = (area && areaMap[area]?.space) || m.space || this._model[0]?.id || '';
const entIds = dev ? entsBy[dev.id] || [] : []; const entIds = dev ? entsBy[dev.id] || [] : [];
let icon = dev ? iconFor(dev.name_by_user || dev.name || '', dev.model) : 'mdi:help-circle'; let icon = dev ? iconFor(dev.name_by_user || dev.name || '', dev.model) : 'mdi:help-circle';
@@ -693,7 +693,7 @@ class HouseplanCard extends LitElement {
rest.push(item); rest.push(item);
} else if (kind === 'entity') { } else if (kind === 'entity') {
const reg = h.entities[ref]; const reg = h.entities[ref];
const area = reg?.area_id || (reg?.device_id && h.devices[reg.device_id]?.area_id) || m.area || ''; const area = m.area || reg?.area_id || (reg?.device_id && h.devices[reg.device_id]?.area_id) || '';
const space = (area && areaMap[area]?.space) || m.space || this._model[0]?.id || ''; const space = (area && areaMap[area]?.space) || m.space || this._model[0]?.id || '';
const st = h.states[ref]; const st = h.states[ref];
const item: DevItem = { const item: DevItem = {
@@ -1281,12 +1281,12 @@ class HouseplanCard extends LitElement {
cfg.markers = cfg.markers || []; cfg.markers = cfg.markers || [];
// определить id маркера // определить id маркера
let id: string; let id: string;
let space: string | null = null; // комната (выбранная вручную) переопределяет пространство/зону для любого значка
let area: string | null = null; const [roomSp, roomAr] = dlg.room ? dlg.room.split('#') : ['', ''];
let space: string | null = roomSp || null;
let area: string | null = roomAr || null;
if (dlg.binding === 'virtual') { if (dlg.binding === 'virtual') {
const [sp, ar] = dlg.room ? dlg.room.split('#') : ['', '']; if (!space) space = this._space;
space = sp || this._space;
area = ar || null;
id = dlg.devId && dlg.devId.startsWith('v_') ? dlg.devId : 'v_' + Date.now().toString(36); id = dlg.devId && dlg.devId.startsWith('v_') ? dlg.devId : 'v_' + Date.now().toString(36);
} else { } else {
const [kind, ref] = dlg.binding.split(':'); const [kind, ref] = dlg.binding.split(':');
@@ -1303,15 +1303,19 @@ class HouseplanCard extends LitElement {
description: dlg.description.trim() || null, description: dlg.description.trim() || null,
pdfs: dlg.pdfs, pdfs: dlg.pdfs,
}; };
if (dlg.binding === 'virtual') { // сохраняем выбор комнаты (для виртуальных всегда; для привязанных — если выбрана)
if (dlg.binding === 'virtual' || dlg.room) {
marker.space = space; marker.space = space;
marker.area = area; marker.area = area;
} }
// сменилась комната → переставить значок в её центр
const prevDev = oldId ? this._devices.find((x) => x.id === oldId) : null;
const roomChanged = !!dlg.room && prevDev != null && (prevDev.space !== space || prevDev.area !== area);
// удалить прежний маркер (по старому id и по новому id) // удалить прежний маркер (по старому id и по новому id)
cfg.markers = cfg.markers.filter((m) => m.id !== id && m.id !== oldId); cfg.markers = cfg.markers.filter((m) => m.id !== id && m.id !== oldId);
cfg.markers.push(marker); cfg.markers.push(marker);
// позиция: если новый значок или сменилось пространство — поставить в центр комнаты/пространства // позиция: новый значок ИЛИ сменилась комната → поставить в центр комнаты/пространства
if (!this._layout[id]) { if (!this._layout[id] || roomChanged) {
const spm = this._spaceModel(space || undefined); const spm = this._spaceModel(space || undefined);
let cx = spm.vb[0] + spm.vb[2] / 2; let cx = spm.vb[0] + spm.vb[2] / 2;
let cy = spm.vb[1] + spm.vb[3] / 2; let cy = spm.vb[1] + spm.vb[3] / 2;
@@ -1799,6 +1803,9 @@ class HouseplanCard extends LitElement {
: nothing} : nothing}
</div> </div>
<div class="row"> <div class="row">
<button class="btn" @click=${() => { const dd = d; this._infoCard = null; this._openMarkerDialog(dd); }}>
<ha-icon icon="mdi:pencil"></ha-icon>Редактировать
</button>
${d.primary ${d.primary
? html`<button class="btn" @click=${() => { const p = d.primary; this._infoCard = null; this._openMoreInfo(p); }}> ? html`<button class="btn" @click=${() => { const p = d.primary; this._infoCard = null; this._openMoreInfo(p); }}>
<ha-icon icon="mdi:open-in-new"></ha-icon>Открыть в HA <ha-icon icon="mdi:open-in-new"></ha-icon>Открыть в HA
@@ -1857,16 +1864,14 @@ class HouseplanCard extends LitElement {
</div> </div>
</div> </div>
${isVirtual <label>Комната${isVirtual ? '' : ' (переопределить размещение)'}</label>
? html`<label>Комната</label> <select class="areasel"
<select class="areasel" @change=${(e: Event) => (this._markerDialog = { ...d, room: (e.target as HTMLSelectElement).value })}>
@change=${(e: Event) => (this._markerDialog = { ...d, room: (e.target as HTMLSelectElement).value })}> <option value="">${isVirtual ? '— выберите комнату —' : '— по зоне устройства (авто) —'}</option>
<option value="">— выберите комнату —</option> ${this._allRoomsFlat().map(
${this._allRoomsFlat().map( (r) => html`<option value=${r.value} ?selected=${r.value === d.room}>${r.label}</option>`,
(r) => html`<option value=${r.value} ?selected=${r.value === d.room}>${r.label}</option>`, )}
)} </select>
</select>`
: nothing}
<label>Иконка</label> <label>Иконка</label>
${customElements.get('ha-icon-picker') ${customElements.get('ha-icon-picker')