mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
fix v1.33.4: device icon stays in place when rebinding or changing room
- rebinding migrates the layout position to the new marker id instead of recentering; room change within the same space keeps the position (owner's decision); new icons and cross-space moves still center - smoke_marker_stay.mjs; TESTING/CHANGELOG same-commit
This commit is contained in:
@@ -11,7 +11,7 @@ PLANS_DIR = "houseplan/plans" # relative to the HA configuration directory
|
||||
FILES_URL = "/houseplan_files/files"
|
||||
FILES_DIR = "houseplan/files"
|
||||
CONF_ADMIN_ONLY = "admin_only"
|
||||
VERSION = "1.33.3"
|
||||
VERSION = "1.33.4"
|
||||
|
||||
DEFAULT_CONFIG: dict = {
|
||||
"spaces": [],
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -16,5 +16,5 @@
|
||||
"issue_tracker": "https://github.com/Matysh/houseplan-card/issues",
|
||||
"requirements": [],
|
||||
"single_config_entry": true,
|
||||
"version": "1.33.3"
|
||||
"version": "1.33.4"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { launch } from './serve.mjs';
|
||||
const { page, browser } = await launch();
|
||||
const res = await page.evaluate(async () => {
|
||||
const out = {};
|
||||
const c = window.__card;
|
||||
c._setMode('devices'); await c.updateComplete;
|
||||
const near = (a, b) => a && b && Math.abs(a.x - b.x) < 1e-6 && Math.abs(a.y - b.y) < 1e-6 && a.s === b.s;
|
||||
// подопытный: авто-устройство с сохранённой позицией
|
||||
const dev = c._devices.find((d) => !d.virtual && !d.marker && c._layout[d.id]);
|
||||
out.hasDev = !!dev;
|
||||
const pos0 = { ...c._layout[dev.id] };
|
||||
// 1) смена комнаты (та же space) → позиция не меняется
|
||||
const otherRoom = c._spaceModel(dev.space).rooms.find((r) => r.id && r.area && r.area !== dev.area);
|
||||
out.hasOtherRoom = !!otherRoom;
|
||||
c._openMarkerDialog(dev);
|
||||
c._markerDialog = { ...c._markerDialog, room: dev.space + '#' + otherRoom.area };
|
||||
await c._saveMarker(); await c.updateComplete;
|
||||
out.stayAfterRoomChange = near(c._layout[dev.id], pos0);
|
||||
// вернуть комнату назад
|
||||
c._openMarkerDialog(c._devices.find((d) => d.id === dev.id));
|
||||
c._markerDialog = { ...c._markerDialog, room: dev.space + '#' + dev.area };
|
||||
await c._saveMarker(); await c.updateComplete;
|
||||
// 2) смена привязки device -> entity (id меняется) → позиция мигрирует
|
||||
const freeEnt = Object.keys(c.hass.states).find((e) => e.startsWith('sensor.') &&
|
||||
!c._devices.some((d) => d.id === 'lg_' + e));
|
||||
out.hasFreeEnt = !!freeEnt;
|
||||
c._openMarkerDialog(c._devices.find((d) => d.id === dev.id));
|
||||
c._markerDialog = { ...c._markerDialog, binding: 'entity:' + freeEnt };
|
||||
await c._saveMarker(); await c.updateComplete;
|
||||
const newId = 'lg_' + freeEnt;
|
||||
out.migrated = near(c._layout[newId], pos0);
|
||||
out.oldGone = !c._layout[dev.id];
|
||||
// 3) новый виртуальный маркер по-прежнему центрируется в выбранной комнате
|
||||
c._openMarkerDialog();
|
||||
const room = c._spaceModel().rooms.find((r) => r.id && r.area);
|
||||
c._markerDialog = { ...c._markerDialog, name: 'Тест', binding: 'virtual', room: c._space + '#' + room.area };
|
||||
await c._saveMarker(); await c.updateComplete;
|
||||
const vid = c._serverCfg.markers.find((m) => m.name === 'Тест')?.id;
|
||||
const center = c._roomCenter(room);
|
||||
const vpos = c._layout[vid];
|
||||
const aspect = c._curSpaceCfg.aspect || 1;
|
||||
out.newCentered = vpos && Math.abs(vpos.x * 1000 - center[0]) < 1 && Math.abs(vpos.y * (1000 / aspect) - center[1]) < 1;
|
||||
return out;
|
||||
});
|
||||
console.log(JSON.stringify(res, null, 1));
|
||||
await browser.close();
|
||||
File diff suppressed because one or more lines are too long
Vendored
+2
-2
File diff suppressed because one or more lines are too long
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## v1.33.4 — 2026-07-22
|
||||
- Editing a device no longer makes its icon jump. Changing the HA binding
|
||||
(which changes the marker id) migrates the saved position to the new id;
|
||||
changing the room within the same space keeps the icon exactly where it
|
||||
stands (previously it re-centered in the new room). Only a brand-new icon,
|
||||
or a move to a room in a different space, is centered.
|
||||
|
||||
## v1.33.3 — 2026-07-22
|
||||
- Device dialog: when no icon is set explicitly, the icon picker no longer
|
||||
looks empty — it shows the **auto-derived icon** (from the icon rules /
|
||||
|
||||
@@ -140,6 +140,10 @@ Run the *core flows* (marked ★ below) in each environment at least once per mi
|
||||
(explicit ripple color still wins); off/white lights unchanged [auto]
|
||||
- [ ] Alarm pulse (v1.27.0): leak/smoke/gas/CO/siren in 'on' pulse a red ring over any
|
||||
display mode; clears on 'off'; unavailable never alarms [auto]; reduced-motion static
|
||||
- [ ] Icon stays on edit (v1.33.4): rebinding a device (HA device/entity) or
|
||||
changing its room within the same space never moves the icon — the saved
|
||||
or auto position migrates to the new marker id; only a brand-new icon or
|
||||
a move to another space centers it in the target room [auto]
|
||||
- [ ] Icon picker placeholder (v1.33.3): with no explicit icon the device
|
||||
dialog's icon picker shows the auto-derived icon as its placeholder, plus
|
||||
an "Auto: mdi:..." hint line with the icon preview; the hint disappears
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "houseplan-card",
|
||||
"version": "1.33.3",
|
||||
"version": "1.33.4",
|
||||
"description": "Interactive house plan Lovelace card for Home Assistant",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
|
||||
+23
-5
@@ -32,7 +32,7 @@ import './space-card';
|
||||
import { cardStyles } from './styles';
|
||||
import { langOf, t, type I18nKey } from './i18n';
|
||||
|
||||
const CARD_VERSION = '1.33.3';
|
||||
const CARD_VERSION = '1.33.4';
|
||||
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';
|
||||
@@ -2218,11 +2218,29 @@ class HouseplanCard extends LitElement {
|
||||
// remove the previous marker (by the old id and by the new id)
|
||||
cfg.markers = cfg.markers.filter((m) => m.id !== id && m.id !== oldId);
|
||||
cfg.markers.push(marker);
|
||||
// position: a new icon OR the room changed → place it at the center of the room/space.
|
||||
// Write POINT-WISE (layout/update), not the whole layout — a full layout/set overwrites
|
||||
// positions changed in other windows (the v1.4.4 incident).
|
||||
// Position rule (owner's decision, v1.33.4): editing an existing icon —
|
||||
// rebinding it to another HA device/entity or to another room — must NOT
|
||||
// move it. Its current position (saved or the ephemeral auto one) is
|
||||
// migrated to the new marker id. Only two cases still center the icon:
|
||||
// a truly NEW icon, and a move to a room in a DIFFERENT space (keeping
|
||||
// the old coordinates there would be meaningless).
|
||||
// Write POINT-WISE (layout/update), not the whole layout — a full layout/set
|
||||
// overwrites positions changed in other windows (the v1.4.4 incident).
|
||||
let newPos: { s: string; x: number; y: number } | null = null;
|
||||
if (!this._layout[id] || roomChanged) {
|
||||
const targetSpace = space || prevDev?.space || this._space;
|
||||
const prevRec = oldId ? this._layout[oldId] : null;
|
||||
const prevPos = prevRec
|
||||
? { s: prevRec.s || prevDev?.space || this._space, x: prevRec.x, y: prevRec.y }
|
||||
: oldId && prevDev && this._defPos[oldId]
|
||||
? this._normPos(prevDev.space, this._defPos[oldId].x, this._defPos[oldId].y)
|
||||
: null;
|
||||
if (prevPos && prevPos.s === targetSpace) {
|
||||
// stays in place; pin it under the (possibly new) id
|
||||
if (id !== oldId || !this._layout[id] || roomChanged) {
|
||||
newPos = { s: prevPos.s, x: prevPos.x, y: prevPos.y };
|
||||
this._layout = { ...this._layout, [id]: newPos };
|
||||
}
|
||||
} else if (!this._layout[id] || roomChanged) {
|
||||
const spm = this._spaceModel(space || undefined);
|
||||
let cx = spm.vb[0] + spm.vb[2] / 2;
|
||||
let cy = spm.vb[1] + spm.vb[3] / 2;
|
||||
|
||||
Reference in New Issue
Block a user