mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
feat v1.8.3: мгновенный рендер плана и значков из кэша конфига (stale-while-revalidate), свежие данные в фоне
This commit is contained in:
@@ -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.8.2"
|
VERSION = "1.8.3"
|
||||||
|
|
||||||
DEFAULT_CONFIG: dict = {
|
DEFAULT_CONFIG: dict = {
|
||||||
"spaces": [],
|
"spaces": [],
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -15,5 +15,5 @@
|
|||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"issue_tracker": "https://github.com/justbusiness/houseplan-card/issues",
|
"issue_tracker": "https://github.com/justbusiness/houseplan-card/issues",
|
||||||
"requirements": [],
|
"requirements": [],
|
||||||
"version": "1.8.2"
|
"version": "1.8.3"
|
||||||
}
|
}
|
||||||
Vendored
+4
-4
File diff suppressed because one or more lines are too long
@@ -1,5 +1,17 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v1.8.3 — 2026-07-04 (мгновенный старт: кэш конфига, данные в фоне)
|
||||||
|
- Иконки и план появлялись с задержкой — карточка ждала ответа сервера (WS `config/get`
|
||||||
|
+ ретраи прогрева соединения), до этого модель пространств была пуста.
|
||||||
|
- Внедрён кэш «stale-while-revalidate»: снимок серверного конфига + rev + раскладки
|
||||||
|
сохраняется в `localStorage['houseplan_card_cfg_v1']`. В `setConfig` (синхронно, до прихода
|
||||||
|
hass) он восстанавливается — план и значки рисуются сразу из кэша (проверено: setConfig
|
||||||
|
наполняет модель за 0.2 мс без сервера), а свежие данные догружаются в фоне и обновляют
|
||||||
|
карточку. Живые состояния/температуры/сигнал подтягиваются по мере прогрева hass.
|
||||||
|
- Кэш обновляется после каждой успешной загрузки, live-ресинка и сохранения позиций иконок,
|
||||||
|
поэтому при следующем открытии виден актуальный снимок. При недоступном сервере показывается
|
||||||
|
последний известный план вместо пустого онбординга.
|
||||||
|
|
||||||
## v1.8.2 — 2026-07-04 (векторно-чёткий зум через viewBox + полная ширина сцены)
|
## v1.8.2 — 2026-07-04 (векторно-чёткий зум через viewBox + полная ширина сцены)
|
||||||
- Зум переведён с CSS `transform: scale()` на манипуляцию `viewBox` SVG. Раньше слой
|
- Зум переведён с CSS `transform: scale()` на манипуляцию `viewBox` SVG. Раньше слой
|
||||||
растрировался и потом масштабировался — при приближении всё «мылилось». Теперь браузер
|
растрировался и потом масштабировался — при приближении всё «мылилось». Теперь браузер
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "houseplan-card",
|
"name": "houseplan-card",
|
||||||
"version": "1.8.2",
|
"version": "1.8.3",
|
||||||
"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",
|
||||||
|
|||||||
+30
-1
@@ -13,8 +13,9 @@ import {
|
|||||||
} from './logic';
|
} from './logic';
|
||||||
import './editor';
|
import './editor';
|
||||||
|
|
||||||
const CARD_VERSION = '1.8.2';
|
const CARD_VERSION = '1.8.3';
|
||||||
const LS_KEY = 'houseplan_card_layout_v1';
|
const LS_KEY = 'houseplan_card_layout_v1';
|
||||||
|
const LS_CFG = 'houseplan_card_cfg_v1'; // кэш серверного конфига+раскладки для мгновенного рендера
|
||||||
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
||||||
const NORM_W = 1000; // ширина рендер-пространства для нормированных конфигов
|
const NORM_W = 1000; // ширина рендер-пространства для нормированных конфигов
|
||||||
|
|
||||||
@@ -283,6 +284,31 @@ class HouseplanCard extends LitElement {
|
|||||||
} catch {
|
} catch {
|
||||||
this._zoomBySpace = {};
|
this._zoomBySpace = {};
|
||||||
}
|
}
|
||||||
|
// мгновенный рендер из кэша (stale-while-revalidate): показать план и значки
|
||||||
|
// сразу, не дожидаясь ответа сервера — свежие данные догрузятся в фоне.
|
||||||
|
try {
|
||||||
|
const c = JSON.parse(localStorage.getItem(LS_CFG) || 'null');
|
||||||
|
if (c && c.config && Array.isArray(c.config.spaces)) {
|
||||||
|
this._serverCfg = c.config;
|
||||||
|
this._cfgRev = c.rev || 0;
|
||||||
|
this._layout = c.layout || {};
|
||||||
|
this._serverStorage = true;
|
||||||
|
if (config.default_floor) this._space = config.default_floor;
|
||||||
|
else if (!this._model.find((sp) => sp.id === this._space)) this._space = this._model[0]?.id || this._space;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Сохранить снимок конфига+раскладки в localStorage для мгновенного старта. */
|
||||||
|
private _cacheSnapshot(): void {
|
||||||
|
if (!this._serverCfg) return;
|
||||||
|
try {
|
||||||
|
localStorage.setItem(LS_CFG, JSON.stringify({ config: this._serverCfg, rev: this._cfgRev, layout: this._layout }));
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
@@ -384,6 +410,7 @@ class HouseplanCard extends LitElement {
|
|||||||
if (this._norm && !this._model.find((s) => s.id === this._space)) {
|
if (this._norm && !this._model.find((s) => s.id === this._space)) {
|
||||||
this._space = this._model[0]?.id || this._space;
|
this._space = this._model[0]?.id || this._space;
|
||||||
}
|
}
|
||||||
|
this._cacheSnapshot();
|
||||||
this._restoreZoom();
|
this._restoreZoom();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// не последняя попытка — молча ждём следующего обновления hass (прогрев WS)
|
// не последняя попытка — молча ждём следующего обновления hass (прогрев WS)
|
||||||
@@ -409,6 +436,7 @@ class HouseplanCard extends LitElement {
|
|||||||
const cfg = resp?.config;
|
const cfg = resp?.config;
|
||||||
this._serverCfg = cfg && Array.isArray(cfg.spaces) ? cfg : null;
|
this._serverCfg = cfg && Array.isArray(cfg.spaces) ? cfg : null;
|
||||||
this._cfgRev = resp?.rev || 0;
|
this._cfgRev = resp?.rev || 0;
|
||||||
|
this._cacheSnapshot();
|
||||||
this._regSignature = '';
|
this._regSignature = '';
|
||||||
this._maybeRebuildDevices();
|
this._maybeRebuildDevices();
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
@@ -431,6 +459,7 @@ class HouseplanCard extends LitElement {
|
|||||||
.callWS({ type: 'houseplan/layout/update', device_id: id, pos })
|
.callWS({ type: 'houseplan/layout/update', device_id: id, pos })
|
||||||
.catch((e: any) => this._showToast('Не удалось сохранить позицию: ' + (e?.message || e)));
|
.catch((e: any) => this._showToast('Не удалось сохранить позицию: ' + (e?.message || e)));
|
||||||
}
|
}
|
||||||
|
this._cacheSnapshot();
|
||||||
} else {
|
} else {
|
||||||
localStorage.setItem(LS_KEY, JSON.stringify(this._layout));
|
localStorage.setItem(LS_KEY, JSON.stringify(this._layout));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user