mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
The card signs content urls because a browser cannot authenticate an <image href>. But _display() was called inside _buildModel(), and the space model is memoized on the config fingerprint — so the UNSIGNED url froze in the cache and the signature, which did arrive, never reached the element. The plan never loaded, and the browser kept hitting the unsigned path: 401, which Home Assistant reports as a failed login attempt from the viewer's own IP (that is how the owner spotted it). PDF links were unaffected: they already resolved at render time. - _buildModel() keeps the raw plan_url; the render pass calls _display(). - _display() returns '' for an unsigned content url instead of the plain path, and the <image> is not emitted at all until the signature lands — no 401, no spurious login-attempt warning. - _resign() replaces 'drop everything and re-request': the previous urls are kept until the new ones arrive, so a wall tablet never blanks. - demo/smoke_plan_signed.mjs: reproduces on v1.44.6 (href stays ?v=..., never ?authSig=), passes here. TESTING.md row added. - docs: CHANGELOG.md + CHANGELOG.ru.md + STATUS.md.
264 lines
26 KiB
Markdown
Executable File
264 lines
26 KiB
Markdown
Executable File
# История изменений
|
||
|
||
> Русская версия [docs/CHANGELOG.md](CHANGELOG.md). Переведены записи начиная
|
||
> с v1.42.0 (2026-07-26); более ранние доступны только в английском файле.
|
||
>
|
||
> **Правило проекта:** оба файла пополняются в одном коммите с самим
|
||
> изменением — как и остальная документация (см. docs/STATUS.md).
|
||
|
||
## v1.44.7 — 2026-07-27
|
||
- **Подложки снова отображаются (регрессия с v1.44.5).** Эндпоинт с файлами
|
||
требует авторизации, поэтому карточка просит бэкенд подписать ссылку на план —
|
||
но подпись подставлялась внутри *мемоизированной* модели пространства, а она
|
||
кэшируется по отпечатку конфига. Неподписанная ссылка «замерзала» в кэше,
|
||
подпись до элемента `<image>` не доезжала, и план не грузился никогда. Теперь
|
||
ссылка вычисляется в момент отрисовки, вне кэша. (Ссылки на PDF не страдали —
|
||
там она и так вычислялась при отрисовке.)
|
||
- **Больше нет «неудачной попытки входа» с собственного IP.** Пока подложка была
|
||
сломана, браузер продолжал дёргать неподписанный путь, тот отвечал 401, и
|
||
Home Assistant поднимал предупреждение о неудачном входе с адреса самого
|
||
зрителя. Теперь до получения подписи не рисуется ничего, и неподписанный
|
||
запрос не уходит вовсе.
|
||
- **Долгоживущие экраны не моргают.** Переподписывание раз в 12 часов раньше
|
||
сбрасывало все подписи и ждало новые; теперь текущие ссылки держатся до
|
||
прихода замены, так что настенный планшет не показывает пустой план.
|
||
- Регрессионный тест `demo/smoke_plan_signed.mjs` падает на v1.44.6 и проходит
|
||
здесь.
|
||
|
||
## v1.44.6 — 2026-07-27
|
||
- **Климатом комнаты считается только температура *воздуха*.** После v1.44.5,
|
||
когда данные стали браться из реестра зон, а не с видимых значков,
|
||
кандидатами стали все скрытые датчики температуры в зоне — в том числе те,
|
||
что меряют вовсе не воздух. Перед усреднением теперь работают три фильтра:
|
||
пропускаются сущности с категорией диагностика/настройка, сущности
|
||
исключённых интеграций и сущности, в id которых назван не-воздушный носитель
|
||
(`water`, `coolant`, `flow_temp`, `return_temp`, `target`, `setpoint`, `chip`,
|
||
`cpu`, `processor`, `board`, `device_temp`, `batter`, `freezer`, `fridge`,
|
||
`oven`, `kettle`, `boiler`).
|
||
На живой установке с 60 зонами это убрало четыре реальных ложных
|
||
срабатывания: температуру процессора NAS, воду в умном чайнике, сауну с 90 °C
|
||
и виртуальный `better_thermostat`, дублирующий настоящий датчик.
|
||
- **Новые правила иконок:** чайники и термопоты получают `mdi:kettle`, сауны
|
||
(`sauna`, `harvia`, `парная`) — `mdi:hot-tub`. Раньше и те и другие попадали
|
||
под общее правило термометра, из-за чего и учитывались в климате комнаты.
|
||
|
||
## v1.44.5 — 2026-07-27
|
||
- **Климат комнаты считается по всем датчикам зоны**, включая устройства,
|
||
которых нет на плане (скрыты курированием или вами). Раньше среднее бралось
|
||
только по видимым значкам, поэтому скрытый термометр молча выпадал из
|
||
карточки комнаты, подсказки и температурной заливки. Курирование сохранено
|
||
(холодильники, термоголовки и розетки с температурой чипа не считаются), а
|
||
явно выбранный источник в настройках комнаты по-прежнему главнее.
|
||
- Из подсказки к комнате убрана фраза «открыть зону» — клик по комнате перестал
|
||
никуда вести ещё в v1.40.1, для перехода есть значок-ссылка у названия.
|
||
|
||
|
||
## v1.44.4 — 2026-07-27 (доработка по аудиту: B2, B5, L4)
|
||
- **Единая политика авторизации (B2).** HTTP-загрузка по-прежнему **разрешала**
|
||
запись, когда запись о конфигурации недоступна, тогда как WebSocket-путь уже
|
||
отказывал — они разошлись. Теперь оба вызывают общий помощник `may_write`,
|
||
который в неопределённой ситуации пропускает только администраторов.
|
||
- **NaN/Infinity отвергаются во всех координатах (B5).** Проверка на конечность
|
||
числа стояла только у позиций раскладки; прямоугольники комнат, вершины
|
||
полигонов, `view_box` и координаты проёмов принимали `"NaN"`, который при
|
||
записи превращается в `null` и необратимо портит геометрию. Ограничение
|
||
`MAX_OPENINGS` было объявлено, но нигде не использовалось — список проёмов
|
||
оставался безразмерным.
|
||
- **Укрепление перетаскивания (часть L4).** Безопасная обёртка над
|
||
`setPointerCapture` теперь используется во всех сценариях перетаскивания
|
||
(устройства, подписи, изменение размера), а не только у проёмов — «мёртвый»
|
||
идентификатор указателя мог оборвать перетаскивание. Фигуры декора получили
|
||
ограничение по границам: их больше нельзя утащить далеко за пределы плана и
|
||
сохранить там.
|
||
|
||
## v1.44.3 — 2026-07-27 (исправление: планы и инструкции снова загружаются)
|
||
- **У аутентифицированной выдачи контента не было рабочего пути для браузера.**
|
||
Версия v1.43.0 закрыла дыру правильно, но Home Assistant аутентифицирует
|
||
HTTP-запросы либо заголовком Bearer, либо подписанным путём `authSig` — а
|
||
`<image href>` внутри SVG и обычная ссылка `<a href>` не отправляют ни того,
|
||
ни другого. На настоящем дашборде фоны планов и ссылки на PDF отдавали
|
||
**401** (воспроизведено вживую до исправления). Теперь карточка просит бэкенд
|
||
подписать то, что собирается показать (`houseplan/content/sign`, 24 часа,
|
||
привязано к токену сессии, только для нашего эндпоинта), перерисовывается,
|
||
когда подписи приходят, и обновляет их каждые 12 часов, чтобы настенные
|
||
планшеты продолжали работать. Тест бэкенда скачивает подписанный адрес **без**
|
||
заголовка авторизации и проверяет 200, а без подписи — 401.
|
||
|
||
## v1.44.2 — 2026-07-27 (внешнее код-ревью: CR-1…CR-3)
|
||
|
||
Второе, состязательное ревью (версии v1.44.0) дало три находки — все закрыты.
|
||
|
||
- **Правило про замки теперь сформулировано точно и проверяется (CR-1).**
|
||
Рецензент справедливо отметил, что утверждение «замок нельзя открыть с плана»
|
||
было слишком абсолютным: кнопка в карточке двери действительно вызывает
|
||
сервис. Эта кнопка — осознанное продуктовое решение, поэтому правило
|
||
переписано там, где ему место («никогда случайным нажатием; ровно одна
|
||
подписанная поверхность»), отпирание теперь **спрашивает подтверждение**, а
|
||
новый смок-тест проверяет все пять путей управления и доказывает, что значки,
|
||
`controls[]` и карточка устройства по-прежнему отказывают замкам.
|
||
- **Перенос вложений стал транзакционным (CR-2).** При смене привязки маркера
|
||
файлы раньше ПЕРЕМЕЩАЛИСЬ до сохранения конфига с проверкой ревизии: если
|
||
сохранение отклонялось, на сервере оставались старые ссылки, а файлы уже
|
||
уехали. Теперь сервер копирует, конфиг фиксируется, и только после этого
|
||
старая папка удаляется (`houseplan/files/cleanup`).
|
||
- **Неудачный или частичный перенос больше не переписывает ссылки (CR-3).**
|
||
Копирование возвращает точное соответствие «исходное имя → записанное»;
|
||
переписываются только подтверждённые копии, при совпадении имён файл получает
|
||
уникальное имя вместо молчаливой ссылки на чужой файл, а ошибка переноса
|
||
показывается тостом.
|
||
|
||
## v1.44.1 — 2026-07-27
|
||
|
||
- Ссылка на чат сообщества добавлена везде, где её ищут:
|
||
**https://t.me/ha_houseplan** (бейдж и строка в шапке обоих README, раздел
|
||
«Помощь и обмен опытом», контакт-ссылки в шаблонах issue, CONTRIBUTING,
|
||
STATUS и SCOPE).
|
||
|
||
## v1.44.0 — 2026-07-27 (отзыв пользователя: сначала управление)
|
||
|
||
- **Карточка устройства стала поверхностью управления.** Она открывается со
|
||
списка управляемых сущностей: лампы, розетки и вентиляторы переключаются
|
||
прямо здесь кнопками под палец, шторы, замки и климат передают управление в
|
||
штатный more-info Home Assistant. Модель, ссылки и PDF-инструкции ушли ниже —
|
||
на настенном планшете эта карточка нужна для управления домом, а не для
|
||
чтения документации (из полевого отзыва). Служебные (config/diagnostic)
|
||
сущности в списке не показываются, замки по-прежнему не переключаются
|
||
нажатием в карточке.
|
||
- **«Это устройство — источник света»** — новый флаг у устройства. Умный
|
||
выключатель с обычными (не умными) светильниками теперь даёт ореол в заливке
|
||
«Свет по источникам» без создания хелпера-группы: свечение следует за самим
|
||
выключателем либо за лампами, привязанными в «Управляет источниками света».
|
||
|
||
## v1.43.3 — 2026-07-27 (отзыв пользователя: обнаруживаемость и тач)
|
||
|
||
- **Настройки комнаты невозможно было найти.** Шестерёнка из v1.42.0 жила
|
||
внутри подписи комнаты размером 0.9em от её шрифта и с прозрачностью 60% —
|
||
несколько бледных пикселей на обычном плане. Теперь это кнопка-пилюля
|
||
«⚙ Комната» фиксированного читаемого размера, не зависящая от масштаба
|
||
карточки, и она появляется **даже у комнат без имени** (их там и называют).
|
||
Заодно разблокировались слайдеры размеров шрифта, до которых никто не мог
|
||
добраться.
|
||
- **Строка показателей увеличена** с 0.62 до 0.75 от размера названия — автор
|
||
отзыва мог увеличить название, но строка датчиков оставалась нечитаемой на
|
||
планшете. Множители комнаты и пространства работают поверх.
|
||
- **Тултипы на тач-устройствах, вторая попытка.** Проверки `(hover: none)`
|
||
оказалось мало: некоторые устройства, оболочки, стилусы и подключённые мыши
|
||
сообщают `hover: hover`, и подсказки продолжали висеть под пальцем. Теперь
|
||
карточка запоминает первое же касание (touch/pen) и гасит открытую подсказку.
|
||
|
||
## v1.43.2 — 2026-07-27 (внешний аудит: слой тестов)
|
||
|
||
- **Смок-тесты наконец умеют падать (T1).** Все 48 браузерных смоков печатали
|
||
булевы значения и всегда завершались с кодом 0 — регрессия была видна в их
|
||
собственном выводе, а прогон считался успешным. `demo/serve.mjs` теперь
|
||
экспортирует `check`/`checkAll`/`finish`: каждый факт проверяется по имени,
|
||
несовпадения и необработанные исключения внутри карточки дают ненулевой код
|
||
возврата. Проверено намеренной поломкой блокировки редакторов в киоске —
|
||
соответствующий смок покраснел.
|
||
- **Набор гоняется в CI (T2)** отдельной джобой `smoke` после `frontend`,
|
||
против свежесобранного бандла (закоммиченная копия в `demo/srv/assets` —
|
||
снимок, на нём легко получить «зелёный» отчёт о несуществующем коде), с
|
||
выгрузкой логов при падении.
|
||
- **`docs/TESTING.md` приведён в соответствие (T3).** `[auto]` теперь означает
|
||
«существует именованная проверка, которая падает», и рядом написано, где она;
|
||
72 пункта, где автоматизация была намерением, честно помечены `[manual]`.
|
||
Исправлены два давних противоречия: строка про «ноль кнопок редактирования в
|
||
Просмотре» (неверна с v1.30.1) и строка про клик по проёму (снова верна
|
||
с v1.43.1).
|
||
- Три смока проверяли поведение, которого уже нет (ожидания времён v1.39.0 и
|
||
v1.25); теперь они тестируют текущий контракт.
|
||
|
||
## v1.43.1 — 2026-07-27 (внешний аудит: исправления P1)
|
||
|
||
- **Стоимость отрисовки (L1).** Home Assistant подменяет объект `hass` при
|
||
любом изменении состояния в доме, и каждая такая отрисовка пересчитывала всю
|
||
геометрию плана — `_openPairs()` вызывался по разу на комнату (кубическая
|
||
математика коллинеарных наложений), модель пространства строилась дважды.
|
||
Теперь и то, и другое мемоизируется по структурному отпечатку конфига и
|
||
вынесено из цикла по комнатам; сброс кэша происходит синхронно в момент
|
||
мутации, а не внутри дебаунса.
|
||
- **Тап против перетаскивания у проёмов (L4).** У перетаскивания двери или окна
|
||
не было порога движения, поэтому любое дрожание пальца считалось
|
||
перетаскиванием: диалог свойств не открывался, а в конфиг писалось
|
||
неизменённое состояние (что подпитывало гонку L2). Теперь порог 3 px, как во
|
||
всех остальных сценариях перетаскивания, и запись только при реальном
|
||
изменении геометрии.
|
||
- **Вогнутые комнаты (G2).** Вложенность определялась через среднее арифметическое
|
||
вершин — а оно лежит СНАРУЖИ U- и L-образных комнат, поэтому комнаты-острова
|
||
в них отвергались как пересечение, а дырка в заливке не рисовалась. Теперь
|
||
вычисляется настоящая внутренняя точка (`interiorPoint`).
|
||
- **Дедупликация стен (G3).** `segKey` сортировал концы по сырым float, а
|
||
печатал округлённые, поэтому одна общая стена могла дать два ключа и
|
||
рисовалась дважды. Сначала округление, потом сортировка.
|
||
- **Укрепление бэкенда (B2–B5).** Проверка прав на запись теперь **отказывает**,
|
||
когда запись о конфигурации недоступна (раньше во время перезагрузки
|
||
интеграции запись разрешалась); `layout/set` поддерживает `expected_rev` и
|
||
сообщает о конфликте так же, как хранилище конфига; `config/set` без
|
||
`expected_rev` поверх непустого хранилища пишет предупреждение в лог;
|
||
координаты отвергают NaN/Infinity, а у пространств, комнат, маркеров, декора
|
||
и раскладки появились щедрые ограничения размера.
|
||
|
||
## v1.43.0 — 2026-07-27 (внешний аудит: исправления P0)
|
||
|
||
Внешний аудит кода версии v1.41.1 нашёл четыре критические проблемы. Все четыре
|
||
исправлены и покрыты регрессионными тестами.
|
||
|
||
- **Молчаливая потеря правок при сохранении (L2).** Отложенная запись конфига
|
||
читала его в момент срабатывания, поэтому пришедшее в промежутке событие
|
||
`houseplan_config_updated` подменяло конфиг, и правка пользователя исчезала
|
||
без единой ошибки — воспроизводилось даже в одной вкладке. Теперь дебаунс
|
||
умеет `flush()`/`pending()`, перезагрузка сперва дописывает отложенную
|
||
запись и откладывается, пока запись в полёте, а неудачная перезагрузка
|
||
наконец сообщает о себе вместо молчания.
|
||
- **Разрез разрушал геометрию комнаты (G1).** Разрез, начинающийся и
|
||
заканчивающийся на ОДНОЙ стене (вырезание ниши — совершенно естественное
|
||
действие), давал две самопересекающиеся комнаты, суммарная площадь которых
|
||
вдвое превышала исходную, и проверка пересечений это не ловила. Теперь такие
|
||
разрезы корректно вырезают нишу, а инвариант разбиения (части в сумме дают
|
||
исходную площадь) отклоняет всё остальное.
|
||
- **Планы и загруженные файлы отдавались без авторизации (B1).** Любой, кто мог
|
||
достучаться до вашего Home Assistant, скачивал планы этажей и вложенные
|
||
инструкции без входа в систему. Теперь их отдаёт аутентифицированный
|
||
обработчик; сохранённые старые адреса переписываются на чтении, так что
|
||
ничего не ломается. **Старые публичные пути исчезают только после
|
||
перезапуска Home Assistant.**
|
||
- **Диалоги могли воскреснуть и обнулить карточку (L3).** Закрытие диалога во
|
||
время неудачного сохранения превращало его состояние в пустую «оболочку»,
|
||
отрисовщик падал, и карточка оставалась пустой до перезагрузки страницы.
|
||
Защита добавлена во все четыре процедуры сохранения, тост об ошибке
|
||
по-прежнему показывается.
|
||
|
||
## v1.42.2 — 2026-07-26
|
||
|
||
- На тач-устройствах подсказки при наведении больше не выскакивают при каждом
|
||
касании (из отзыва: «на планшете при тапе вылезают доп. надписи — мешают»).
|
||
Подсказки теперь только для мыши; на тач та же информация есть в карточках
|
||
комнат и в карточке устройства по долгому нажатию.
|
||
|
||
## v1.42.1 — 2026-07-26 (размеры шрифтов карточек комнат)
|
||
|
||
- Закрываем отзыв «нельзя настроить размер шрифта»: **три слайдера**. В
|
||
настройках пространства появился базовый размер шрифта карточек комнат для
|
||
всего пространства; в настройках комнаты — независимые размеры **названия** и
|
||
**строки показателей** (50–300% каждый). Эффекты перемножаются и складываются
|
||
с растягиванием карточки за уголки и множителем экрана в киоск-режиме.
|
||
- В обоих диалогах показывается **живой пример карточки**, который меняется
|
||
прямо во время перетаскивания слайдеров.
|
||
|
||
## v1.42.0 — 2026-07-26 (настройки комнаты — третий уровень)
|
||
|
||
- **У настроек теперь четыре уровня**: общие → пространство → комната →
|
||
устройство; более конкретный уровень переопределяет более общий (решение
|
||
владельца, зафиксировано в ARCHITECTURE). В этом релизе добавлен уровень
|
||
КОМНАТЫ.
|
||
- У каждой карточки комнаты в редакторе плана появилась **шестерёнка**:
|
||
переименовать комнату, сменить её зону HA, переопределить **тип заливки**
|
||
только для этой комнаты (работает и в glow-пространствах — «без заливки»
|
||
выводит комнату из темноты) и выбрать явный **источник температуры и
|
||
влажности** — любое устройство или сущность HA вместо среднего по комнате.
|
||
Источник питает карточку комнаты, всплывающую подсказку и температурную
|
||
заливку и работает даже у комнат без зоны HA (случай из отзыва: собственный
|
||
template-сенсор, привязанный к помещению).
|
||
- Тот же раздел настроек появляется в диалоге комнаты сразу после замыкания
|
||
контура.
|