mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
v1.44.8: an uploaded plan never reached the config
Found on the owner's install: the image lands in /config/houseplan/plans, the space keeps plan_url=null, the plan never shows and re-saving does not help. _saveSpaceDialog held a reference to the space object across the await that uploads the file. _reloadConfigOnly() — which runs on every houseplan_config_updated event — REPLACES _serverCfg, so that reference became an orphan: plan_url, aspect, title and every display setting were written into a detached object while the save shipped the untouched config. In 'create' mode the whole new space was lost the same way. - upload first, then touch the config; no reference is held across an await. - _saveConfigNow() sets _cfgWriting like the debounced writer, so a revision arriving mid-save defers its reload instead of replacing the config (audit L2 extended to this path). - demo/smoke_plan_upload_race.mjs: on v1.44.7 the sent config still carries the OLD plan_url and the created space is missing; passes here. The demo's config/get now returns a fresh object, as a real server does — returning the same reference is what hid this class of bug from the smoke layer. - DEVELOPMENT.md: the deploy target is custom_components/houseplan/frontend/, and deploy verification must go over HTTP. A copy placed next to __init__.py is served by nobody — that cost two deployments today. - docs: CHANGELOG.md + CHANGELOG.ru.md + TESTING.md + STATUS.md.
This commit is contained in:
@@ -1,5 +1,23 @@
|
||||
# Changelog
|
||||
|
||||
## v1.44.8 — 2026-07-27
|
||||
- **An uploaded plan is actually attached to the space.** `_saveSpaceDialog`
|
||||
held a reference to the space object across the `await` that uploads the
|
||||
image. Every `houseplan_config_updated` event runs `_reloadConfigOnly()`,
|
||||
which *replaces* `_serverCfg` — so the reference became an orphan and
|
||||
`plan_url`, `aspect`, the title and all display settings were written into a
|
||||
detached object while the save shipped the untouched config. The file landed
|
||||
on disk, the plan never appeared, and re-saving could not help. Creating a
|
||||
space in that window lost the space entirely.
|
||||
The upload now happens *before* the config is touched, and nothing is held
|
||||
across an await.
|
||||
- **`_saveConfigNow` marks the write in flight** (`_cfgWriting`), like the
|
||||
debounced writer already did, so a remote revision arriving mid-save defers
|
||||
its reload instead of replacing the config underneath it (audit L2 extended
|
||||
to this path).
|
||||
- Regression test `demo/smoke_plan_upload_race.mjs` fails on v1.44.7 and passes
|
||||
here.
|
||||
|
||||
## v1.44.7 — 2026-07-27
|
||||
- **Plan backgrounds are visible again (regression from v1.44.5).** Since the
|
||||
content endpoint requires authentication, the card asks the backend to sign
|
||||
|
||||
@@ -6,6 +6,24 @@
|
||||
> **Правило проекта:** оба файла пополняются в одном коммите с самим
|
||||
> изменением — как и остальная документация (см. docs/STATUS.md).
|
||||
|
||||
## v1.44.8 — 2026-07-27
|
||||
- **Загруженная подложка действительно привязывается к пространству.**
|
||||
`_saveSpaceDialog` держал ссылку на объект пространства через `await`
|
||||
загрузки картинки. Любое событие `houseplan_config_updated` запускает
|
||||
`_reloadConfigOnly()`, а оно *заменяет* `_serverCfg` — ссылка становилась
|
||||
осиротевшей, и `plan_url`, `aspect`, заголовок и все настройки отображения
|
||||
писались в отсоединённый объект, тогда как на сервер уходил нетронутый
|
||||
конфиг. Файл попадал на диск, подложка не появлялась, пересохранение не
|
||||
помогало. Создание пространства в этот момент теряло пространство целиком.
|
||||
Теперь загрузка идёт *до* обращения к конфигу, и ни одна ссылка не живёт
|
||||
через await.
|
||||
- **`_saveConfigNow` помечает запись как выполняющуюся** (`_cfgWriting`) — так
|
||||
же, как отложенный писатель, — поэтому чужая ревизия, пришедшая посреди
|
||||
сохранения, откладывает перечитывание вместо подмены конфига (аудит L2,
|
||||
расширен на этот путь).
|
||||
- Регрессионный тест `demo/smoke_plan_upload_race.mjs` падает на v1.44.7 и
|
||||
проходит здесь.
|
||||
|
||||
## v1.44.7 — 2026-07-27
|
||||
- **Подложки снова отображаются (регрессия с v1.44.5).** Эндпоинт с файлами
|
||||
требует авторизации, поэтому карточка просит бэкенд подписать ссылку на план —
|
||||
|
||||
+12
-2
@@ -51,9 +51,19 @@ cp dist/houseplan-card.js custom_components/houseplan/frontend/
|
||||
|
||||
- SSH: port **323**, root, key `ha_jb` (the user uploads it to the chat; in the sandbox /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/`
|
||||
- **The `frontend/` subfolder is not optional.** `__init__.py` registers
|
||||
`Path(__file__).parent / "frontend" / "houseplan-card.js"` as the static path.
|
||||
A copy dropped next to `__init__.py` (…/houseplan/houseplan-card.js) is served
|
||||
by nobody: md5 on the server matches, the browser still gets the old bundle,
|
||||
and hours go into debugging a bug that was already fixed. Cost this mistake
|
||||
once: 2026-07-27, two releases deployed into the void.
|
||||
- The whole integration: tar c custom_components/houseplan (--exclude __pycache__) → tar x on the server.
|
||||
- **Verification is mandatory**: `md5sum` locally == on the server == `curl http://homeassistant:8123/houseplan_files/houseplan-card.js | md5sum`
|
||||
(inside the SSH add-on `localhost` is NOT HA, use the host `homeassistant`).
|
||||
- **Verification is mandatory, and it must go over HTTP** — comparing md5 against
|
||||
the file you just copied proves nothing about what the browser receives. The
|
||||
one check that counts:
|
||||
`curl -s https://ha.jbstudio.pro/houseplan_files/houseplan-card.js | grep -o '1\.[0-9]*\.[0-9]*' | sort -u`
|
||||
must print the version just built. (Inside the SSH add-on `localhost` is NOT
|
||||
HA — use the host `homeassistant`.)
|
||||
- Python changes require an HA restart (`ha core restart`, holds the connection until it finishes, HTTP
|
||||
comes back up in 1–3 min). JS changes — just a page refresh (the static path is served
|
||||
with no-cache).
|
||||
|
||||
+2
-2
@@ -15,12 +15,12 @@
|
||||
|
||||
| Item | State |
|
||||
|---|---|
|
||||
| Version | **v1.44.7** everywhere (manifest, const.py, package.json, CARD_VERSION); deployed to the home instance |
|
||||
| Version | **v1.44.8** 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` = releases up to **v1.40.1**; `dev` ahead with v1.40.2+ (speaker icons, kiosk). 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; ~864 older open PRs but merge rate ≈180/mo; realistic ETA 1–3 months (checked 2026-07-24) |
|
||||
| Home instance | ha.jbstudio.pro (SSH port 323, key `ha_jb`), deployed **v1.44.7** via direct copy (HACS custom repo also installed) |
|
||||
| Home instance | ha.jbstudio.pro (SSH port 323, key `ha_jb`), deployed **v1.44.8** via direct copy (HACS custom repo also installed) |
|
||||
| Localization | UI en/ru (src/i18n/*.json), everything user-visible localized incl. kiosk popover |
|
||||
| Tests | 121 frontend (node:test) + 12 pure backend + 12 HA-harness (CI, py3.13); ~30 demo smoke suites (headless chromium) |
|
||||
| Community | **Telegram chat: https://t.me/ha_houseplan** (created 2026-07-27) — the primary user-facing support channel; GitHub issues stay for bugs/features. Link it from any new release notes and posts |
|
||||
|
||||
@@ -234,6 +234,11 @@ Run the *core flows* (marked ★ below) in each environment at least once per mi
|
||||
are only reachable through /api/houseplan/content/… with a session; the
|
||||
old /houseplan_files/plans|files paths return 404 after a restart; old
|
||||
stored URLs keep working (rewritten on read) [auto+manual]
|
||||
- [ ] Plan upload survives a concurrent config revision (v1.44.8): with a second
|
||||
tab open on the same plan, attach a background image in space settings —
|
||||
the plan shows immediately, `plan_url` is in `.storage/houseplan.config`,
|
||||
and the same holds when the space is being CREATED, not edited
|
||||
[auto: smoke_plan_upload_race]
|
||||
- [ ] Signed plan background (v1.44.7): a space whose plan lives on the content
|
||||
endpoint renders its background image with an `authSig` query — the plan is
|
||||
visible after a plain page load, and Home Assistant logs NO failed-login
|
||||
|
||||
Reference in New Issue
Block a user