Reported by the owner minutes after v1.53.0: typing a name showed no results. The results existed — .candlist is a scrollable box, and a scrollable flex item inside the dialog body collapses happily: 26 matching rows rendered inside a 1px strip. The binding dropdown never showed this because it sits inside .droppanel, a block context. flex: 0 0 auto + a min-height keeps it open. The smoke now MEASURES the list and the first row instead of counting DOM nodes — counting is exactly why it passed a build where nothing was visible.
99 KiB
Executable File
История изменений
Русская версия docs/CHANGELOG.md. Переведены записи начиная с v1.42.0 (2026-07-26); более ранние доступны только в английском файле.
Правило проекта: оба файла пополняются в одном коммите с самим изменением — как и остальная документация (см. docs/STATUS.md).
v1.53.1 — 2026-07-30
- Фикс: поиск «что запускать» не показывал результатов. Результаты были — список это прокручиваемый блок, и как flex-элемент внутри тела диалога он схлопывался в полоску 1px: 26 подходящих автоматизаций отрисовывались в ничто. Найдено владельцем через минуты после v1.53.0. Теперь список держит высоту; смок измеряет эту высоту, а не просто считает строки в DOM — именно поэтому он и пропустил сломанную сборку.
v1.53.0 — 2026-07-30
Нажатие может запускать вашу автоматизацию (ТЗ владельца)
- Новое действие по нажатию: «Запустить автоматизацию/скрипт/сцену». Мы
не можем знать каждое экзотическое устройство, но вы знаете, что должен
делать тап по нему: выберите любую автоматизацию, скрипт или сцену из
списка с поиском — тап её запустит (
automation.trigger,script.turn_onилиscene.turn_onпо типу цели, с тостом «Запущено»). Скрипт — идиоматичное «действие» HA, поэтому предлагаются все три запускаемых типа: не нужны автоматизации-пустышки без триггера. Сохранение без цели блокируется; удалённая позже цель подсвечивается в диалоге и безопасно отвечает тостом при тапе. - Чекбокс «Спрашивать подтверждение» — защита от случайных нажатий для любого действия, меняющего состояние: и toggle, и запуск, включая настенные выключатели с привязанными целями. Диалог собственный (Esc, клик мимо или «Отмена» — ничего не происходит), поэтому работает и на настенном планшете.
- Шторы переключаются нативно: cover и valve добавлены в домены card-wide toggle — с одним осознанным исключением: гаражные ворота, двери и калитки (классы garage/door/gate) остаются ВНЕ дефолтного переключения — случайный тап не должен открыть гараж. Явный per-device toggle остаётся осознанным выбором владельца, а замки и охранные панели, как и всегда, с плана неприкосновенны.
v1.52.2 — 2026-07-29
По ревью v1.52.1 (рантайм не менялся — качество теста и формулировок)
- Проверка регресса в редакторе плана целится ИМЕННО в лампу (HP-1521-01): старый ассерт принимал любой жёлтый значок, и горящая розетка из той же фикстуры прошла бы его даже без фикса лампы. Проверено мутацией: откат гейта v1.52.1 валит смок.
- Последняя формулировка «жёлтый в любом режиме» — пункт чек-листа и комментарий в _stateClass — приведена к фактическому контракту: состояние источника считается условием glow-пула, а жёлтая подложка показывается только там, где пятно не рисуется (HP-1521-02).
v1.52.1 — 2026-07-29
По ревью v1.52.0
- У горящей лампы всегда ровно один индикатор (HP-1520-01). В редакторе плана glow-слой скрыт, но подавление жёлтого там всё равно действовало — горящая лампа не показывала ни пятна, ни подложки. Гейт подавления теперь равен фактической видимости слоя: где пятно не рисуется, жёлтая подложка возвращается.
- Статичная карточка уважает размер и поворот маркера (HP-1513-01). Один и тот же маркер рендерился на read-only карточке базовым и неповёрнутым. Теперь она зеркалит --dev-scale и угол полной карточки — только геометрия, по-прежнему схема.
- Документация догнала цветовой контракт v1.52.0 (HP-1520-02): ожидания RGB-окраски в TESTING/UX-MODES и устаревший комментарий в коде.
v1.52.0 — 2026-07-29
Один вид источников света, чем бы их ни переключали (правило владельца)
- Цвет лампы живёт только в её свечении. RGB-окраска иконки, рамки и тени убрана: в зависимости от того, пришли ли с состоянием данные о цвете, одна и та же лампа попадала то в ветку «цветная иконка на тёмной подложке», то в «жёлтая подложка» — выключение одной лампы тапом и остальных выключателем выглядело по-разному. Ветки больше нет.
- В режиме glow индикатор — само пятно света. Подложка источника всегда стандартная, горит он или нет — всё говорит свет вокруг. Розетка, вентилятор или чайник остаются жёлтыми и в glow: они не светят, правило — только для источников.
- В остальных режимах горящий источник — просто жёлтый, как греющая термоголовка; RGB и обычные лампы одинаково.
- Морфинг иконки (контур «лампа с лучами») остаётся во всех режимах, а цвет пульсации по-прежнему берёт фолбэком цвет свечения лампы.
v1.51.3 — 2026-07-29
- Множитель «размер значка» масштабирует и сам глиф, а не только подложку. Изменение размера устройства растило подложку, пульсацию и бейджи значений, но иконка внутри оставалась дефолтной — большая пустая рамка вокруг маленького глифа (репорт пользователя). Теперь глиф считается от того же поустройственного размера, что и всё остальное, и держит пропорцию при любом множителе и зуме.
v1.51.2 — 2026-07-29
По ревью v1.51.1
- Авто-сетка одинакова на обеих карточках (HP-1511-01). Полная карточка резервирует клетки сетки за скрытыми устройствами (их призраки держат место в редакторе); статичная уплотняла сетку по одним видимым — свежее устройство без сохранённой позиции вставало на разных местах двух карточек. Теперь статичная передаёт сетке полный список и по-прежнему рисует только видимых.
- Ripple-призрак сохраняет базовую иконку (HP-1511-02). Скрытый маркер с отображением «пульсация» рендерился безликим неподвижным пульсом — в редакторе его нельзя было узнать. Призрак теперь полностью снимает «одежду» отображения: базовая иконка и имя, каким бы ни был режим.
v1.51.1 — 2026-07-29
По ревью v1.51.0
- Статичная карточка снова учитывает скрытые устройства в LQI комнаты (HP-1510-01). Её фильтр видимости незаметно стал фильтром агрегации: одна и та же комната показывала разное Zigbee-здоровье на двух карточках. Теперь агрегация и отрисовка используют разные списки — скрытые устройства считаются в сигнале на обеих карточках, не рисуются ни на одной и по-прежнему не дают света.
- Призрак не показывает живых чисел (HP-1510-02). Скрытое устройство в «Показать скрытые» подавляло цвета состояний, но по-прежнему рисовало значение, температуру, влажность, бейдж LQI и морф иконки. Всё это убрано: у призрака только базовая иконка и имя — достаточно, чтобы узнать устройство и открыть диалог.
v1.51.0 — 2026-07-29
Скрытие — теперь явная галка (docs/FILTERING.md)
- В диалоге каждого устройства — включая виртуальные — есть галка «Скрыть устройство с плана». Старый фильтр «на лету» остался только сеятелем этих галок: при первом открытии редактором конфиг один раз материализуется — нефизические устройства (мосты, сцены, служебные интеграции, лампы, свёрнутые в световую группу) получают галку, и дальше она принадлежит вам. Снятая галка — навсегда: сеятель не возвращается к устройству, о котором вы уже решили. Новые нефизические прячутся молча; физические — с красной точкой, как раньше.
- «Показать все» стала «Показать скрытые» — локальный инструмент редактора устройств (на настенных планшетах ничего не мигает): скрытые рисуются полупрозрачными СИНИМИ пунктирными призраками — не спутать с серым недоступным — и совсем без живой индикации: призрак — это конфигурация, а не статус. Клик — и галку можно снять. «Удалить с плана» у привязанных устройств убрана (галка — единственный путь); «Удалить» у виртуального по-прежнему удаляет.
- Скрытые устройства учитываются в Zigbee-сигнале комнаты, но не дают ни пятна света, ни заливки — не видно устройства, не видно и света. Климат комнат без изменений. Старые конфиги ведут себя по-старому, пока их не материализует клиент с правом записи.
Жёлтый значит «работает прямо сейчас»
- Один принцип для светящегося значка: лампа светит, розетка подаёт, вентилятор крутится, медиа играет, пылесос убирает — или термоголовка РЕАЛЬНО греет (hvac_action), а не просто включена на зиму. Раньше термоголовка могла светиться жёлтым из-за включённой защиты от накипи, а реально греющая оставалась тёмной: выбор главной сущности пропускал служебный свитч производителя вперёд видимого climate. Исправлено — служебная сущность больше никогда не побеждает видимую основную функцию (это чинит и tap-переключение, и морфинг иконок у таких устройств).
- Пятно света и цвет значка задают один и тот же вопрос: горящая лампа желтит значок в любом режиме заливки ровно тем условием, что зажигает её пятно. README (en+ru) описывает язык цветов.
Редакторы на телефоне
- Пинч-зум и панорамирование жестами работают во всех редакторах: рисование кликовое, так что они совместимы — палец с движением панорамирует, два пальца зумируют, отпускание после жеста не ставит точку, чистый тап — ставит.
Кнопка настроек комнаты
- Отвязана от (передвигаемого) названия: стоит в ВИЗУАЛЬНОМ центре комнаты — центр наибольшей вписанной окружности с тяготением к центроиду площади, поэтому вытянутая комната центрирует её по обеим осям, а Г-образная держит в середине широкой части и не утаскивает в узкую.
- Вдвое меньше прежней, размер от иконки устройства (70% бокса) и зумится ВМЕСТЕ с планом, а не держит постоянный экранный размер.
- Мелкие подписи под названием комнаты (температура, влажность, сигнал, свет) теперь видны и в редакторе плана, а название рендерится ровно в том же месте в просмотре и в редакторе.
v1.50.4 — 2026-07-29
По ревью v1.50.3
- Обе карточки строят модель одним кодом (HP-1503-01). Полная карточка
носила рукописную копию общего построителя модели, и копия не получила
фолбэки для legacy-store из v1.50.3 — один и тот же битый store в
статической карточке рендерился починенным, а в основной — пустым
viewBox="0 0 0 0". Дубликат удалён: полная карточка вызывает общий построитель и лишь подменяет сырой url плана, который нужен её подписи. Новый смок прогоняет точный вектор аудита через обе модели и оба DOM-дерева и проверяет паритет.
v1.50.3 — 2026-07-29
По ревью v1.50.2
- Размер — не координата (HP-1502-01). Граница ±4 из v1.50.2 мерила все
четыре элемента view_box и w/h комнат одинаково, поэтому
[0, 0, 0, 0]и отрицательные размеры всё ещё проходили — а нулевая ось сериализуется вviewBox="0 0 0 0": пустой план у всех клиентов, иaspect-ratio: 0 / 0у статичной карточки сверху. У размеров теперь свой validator: строго положительные, пол — одна тысячная холста; координаты по-прежнему могут быть отрицательными — начало кропа законно выходит за край. А поскольку в сторе битый viewport может уже лежать, обе карточки падают на полный холст вместо пустого экрана, и legacy-прямоугольник с отрицательным размером читается как тот же прямоугольник, нарисованный из другого угла.
Также в этом релизе
- Кнопка настроек комнаты переехала в самый низ карточки комнаты, а имя комнаты отображается ровно в одном и том же месте в просмотре и в редакторе плана — кнопка и метрики больше не участвуют в центрировании.
v1.50.2 — 2026-07-29
По ревью v1.50.1
- Величины геометрии ограничены на обоих слоях (HP-1501-01). v1.50.1 ограничила позиции устройств, но прямоугольники комнат, вершины полигонов, view_box и координаты проёмов всё ещё принимали любое конечное число — одна проходящая схему вершина 1e100 растягивала кадр так, что план становился точкой у всех клиентов, и сервер хранил это как вполне корректную конфигурацию. Теперь схема конфига ограничивает геометрию ±4 (углы ±360°), а рамка содержимого применяет к вершинам комнат тот же конверт холста, что уже применяла к позициям устройств — конфиг, где абсурдная координата уже лежит с прежних времён, всё равно рендерится: точка рисуется, где стоит, просто кадром больше не командует. Вершина чуть за краём холста работает как раньше.
- Пустой repair больше не съедает бэкап (HP-1501-02). Опечатка в space_id
«успешно» отвечала moved: 0 — и её пустой результат заменял бэкап глубиной
один, уничтожая единственный путь назад ровно тогда, когда он нужнее всего:
сразу после починки не того пространства. Теперь «нечего чинить» — ошибка
(
nothing_to_repair): ничего не пишется, ревизия не растёт, предыдущий repair по-прежнему отменяем.
v1.50.1 — 2026-07-29
По ревью v1.50.0
- Карточка ниже другого контента дашборда снова получает сцену (HP-1500-02). Замер высоты в v1.50.0 брал абсолютную координату документа: высокая карточка перед этой записывалась в «шапку», и сцена схлопывалась в ноль. Теперь меряется только собственная обвязка плюс ограниченная поправка на то, что дашборд держит сверху; перемер — по resize окна, слушатель снимается при демонтаже.
- Рамка содержимого больше не бывает вырожденной или абсурдной (HP-1500-03). Одинокий значок в пустом пространстве давал viewBox нулевой площади — пустую сцену; одна сохранённая координата вида 1e100 (любое конечное число проходило проверку) растягивала рамку так, что план становился точкой — у всех зрителей пространства. Почти нулевая ось теперь раскрывается до минимального кадра вокруг значка, точки далеко за холстом рамкой не командуют (рисуются, где стоят), а сервер отклоняет координаты вне ±4 — щедрый запас для значка, утащенного за край, но не для абсурда. Узкая комната сохраняет тесный кадр, датчик калитки чуть за краём по-прежнему учитывается.
- Путь восстановления для установок, застрявших в окне миграции v1.48
(HP-1500-01). Если старая миграция упала между двумя записями, значки
пространства остаются в старых координатах, и по данным это недоказуемо — а
повторное преобразование правильных координат их испортит, поэтому ничего
автоматического тут быть не может. Явный ответ —
houseplan/geometry/repair {space_id, aspect}:dry_runпоказывает точные перемещения, прежние позиции уезжают той же записью хранилища как резервная копия глубиной один,undoих возвращает, и обычные перетаскивания эту копию больше не затирают. Протоколgeom_pendingиз v1.50.0 уже защищает все будущие миграции; это — для тех, кому он опоздал.
v1.50.0 — 2026-07-28
Задачи владельца
- Масштаб по умолчанию считает устройства содержимым. Им можно стоять вне комнат — датчик калитки у забора, камера на столбе — и стартовый вид теперь включает их, даже в пространстве совсем без комнат.
- Вход в редактор больше не сдвигает план. Высота сцены считалась как «экран минус 118px шапки», а шапка редактора выше: сцена уезжала вниз на разницу, низ пропадал за краём. Карточка измеряет, где сцена начинается на самом деле, и отдаёт ей остаток экрана.
- Масштаб теперь и отдаляется. До 0.4×; в отдалении план висит по центру, а не прилипает к углу.
По ревью v1.49.0
- Миграция на квадратный холст переживает сбой между двумя записями (HP-1490-01). Конфиг и позиции живут в разных хранилищах, пишутся по очереди, и первая запись удаляла именно те поля, которые нужны второй — сбой между ними навсегда оставлял значки в старых координатах. Теперь намерение миграции сохраняется до того, как что-либо меняется, и снимается той же записью, что сохраняет позиции: какая половина не успела — ту следующий запуск и доделает, ровно один раз.
- Параллельные загрузки не проскакивают квоту вместе (HP-1490-02). N загрузок мерили хранилище до того, как любая из них записала файл, и все проходили предел, под который помещалась одна. Замер и запись — один атомарный шаг под отдельным замком; отдельным — чтобы медленный обход папки не тормозил сохранения конфига.
- Редакторы снова видят весь холст (HP-1490-03). Рамка содержимого ограничивала и панорамирование, и координаты указателя, так что после первой комнаты рисовать вторую было негде. Режимы редактирования меряют от полного квадрата; просмотр остаётся по содержимому, а смена режима пересчитывает вид, вместо того чтобы тащить его прижатым не к той основе.
- Сохранение ждёт пропорции выбранного плана (HP-1490-04). Save до ответа картинки записывал пропорции ПРЕДЫДУЩЕГО файла, и новый план навсегда оставался в чужой форме. Выбор плана сразу стирает старое значение, Save дожидается ограниченного по времени чтения; не дождался — записывается «неизвестно»: честный квадрат лучше унаследованной формы.
- Гигиена релиза из §5: package-lock.json догнал версию пакета, задвоенный комментарий в space-geometry.ts убран.
v1.49.0 — 2026-07-28
Холст стал квадратным (см. v1.48.0, выпущена вместе с этой).
- Масштаб открывается по нарисованному, а не по всему холсту. Пространство без подложки теперь вписывается по границам своих комнат с полями 5%: маленький план на большом холсте заполняет экран, а не сидит посередине точкой. С подложкой ничего не меняется — картинка и есть план, и обрезать её по комнатам значило бы спрятать то, что ещё не обведено.
- Переключение пространств свайпом и в киоске стало с анимацией. План уезжает туда, куда пошёл палец, следующий приходит с другой стороны. Уважает системную настройку «уменьшить движение».
- Кнопка настроек комнаты подписана «Настройки комнаты», а не просто «Комната», и слегка светлеет под курсором.
- Слово «курирование» заменено на «фильтрацию» — в интерфейсе, документации и коде.
По ревью v1.47.0
- Только что выбранный план больше нельзя удалить из того же диалога (HP-1470-02). Он ещё не сохранён, поэтому сервер справедливо считал его свободным — а сохранение потом записывало ссылку, за которой нет файла. Кнопка заблокирована, и, поскольку два клиента могут сделать это в любом порядке, сервер сверяет с диском каждую внутреннюю ссылку, которую конфигурация добавляет, и отказывает, если файла нет. Ссылку, уже записанную в конфигурации, он пропускает: файл может исчезнуть и мимо Home Assistant, а отказ заблокировал бы ровно ту правку, которая его отцепляет. Чужие ссылки не трогаются.
- Загрузки ограничены (HP-1470-01). По возрасту не удаляется ничего — это дважды стоило настоящих планов, — поэтому предел стоит там, где решение и так принимается: загрузка отклоняется, если хранилище перевалит за 256 МБ или 200 планов (1 ГБ и 1000 для вложений) либо если на диске останется меньше 512 МБ. Список планов отдаётся по 60 самых свежих, миниатюры грузятся лениво.
- Выбор сохранённого плана читает его настоящие пропорции (HP-1470-03). Карточка ничего не ждала и, если подпись для защищённой ссылки ещё не пришла, записывала пропорции «по умолчанию» — квадратный план получался растянутым. Теперь она дожидается подписи, привязывает результат к тому диалогу, который спрашивал, а превью в диалоге подписывается, как и всё остальное.
v1.48.0 — 2026-07-28 (холст всегда квадратный)
- У пространства больше нет собственных пропорций. Область рисования — квадрат, а картинка плана сохраняет свою форму и вписывается в него по центру: у широкого плана появляются поля сверху и снизу, у вытянутого — по бокам. Выбирать нечего, поэтому настройка ориентации холста для пространств без картинки убрана.
- Существующие планы переносятся один раз, при обновлении. В самом рисунке ничего не меняется: коробка дополняется до квадрата, и все координаты пересчитываются относительно неё — комнаты, двери и окна, декор, позиции значков и сохранённая область просмотра. Углы, пропорции комнат и взаимное расположение сохраняются точно. Для вытянутых планов заодно пересчитывается масштаб в сантиметрах на клетку: сетка привязана к ширине, и без этого стены молча стали бы короче.
v1.47.0 — 2026-07-28 (выбор из уже загруженных планов)
- Диалог пространства показывает планы, сохранённые на сервере. Отцепление плана оставляет картинку на диске — так с v1.46.4, но вернуть её можно было только найдя исходный файл у себя и загрузив заново. «Уже загруженные» показывают, что есть: миниатюра, размер файла и то, какое пространство его использует. Клик прикрепляет, пропорции читаются из самой картинки — так же, как при загрузке.
- Там же план и удаляется. Файл плана никогда не удаляется автоматически — ни за отцепление, ни за возраст, — и это разумная политика ровно до тех пор, пока видно, что именно хранится, и есть способ убрать это осознанно. Кнопка корзины делает это и отказывает, пока план используется пространством: ответ на вопрос «можно ли удалить» даёт сохранённая конфигурация, а не браузер.
- Документация догнала код: несколько комментариев всё ещё описывали удаление по возрасту, убранное в v1.46.6.
v1.46.6 — 2026-07-28 (обещание про отцепление, теперь выполненное)
- Переключение пространства в «нарисовать» больше не удаляет его картинку. v1.46.4 и v1.46.5 утверждали, что не удаляет, и плановая уборка действительно отцеплённые планы не трогала — но само сохранение удаляло файл в тот момент, когда снималась ссылка, ещё до всех этих проверок. Причина: файл, покинувший конфигурацию, считался «заменённым», а по одной этой разнице замену плана, отцепление и удаление пространства различить невозможно. Удалением, о котором просили, является только первое. Теперь переход классифицируется по пространству-владельцу, и та же разница применяется к вложениям: убрали файл у существующего устройства — он удаляется, удалили устройство — его инструкции остаются.
- План удалённого пространства сохраняется, а не тридцать дней, как обещала v1.46.5: тридцать дней по возрасту файла всё равно бессмысленны — обычно он загружен месяцы назад.
- По возрасту больше не удаляется ничего, кроме промежуточной папки диалога. Правило, которое вычищало «отвергнутые загрузки», оказалось в гонке с повторной попыткой: уборка удаляла файл неудавшегося сохранения ровно тогда, когда следующая попытка коммитила ссылку на него. Правило, способное удалить файл, на который кто-то вот-вот сошлётся, не стоит освобождаемого места. Файлы уходят по действию, в остальных случаях остаются.
v1.46.5 — 2026-07-28 (ревизия всех автоматических удалений)
- Отцеплённый план не удаляется никогда, ни в каком возрасте. В v1.46.4 ему давался месяц; теперь это навсегда, и причина записана там, где её увидит следующая правка. Правило, оно же в docs/SCOPE.md: компонент вправе удалить файл только тогда, когда об этом говорит действие пользователя — замена плана, удаление вложения, удаление устройства. «На это больше никто не ссылается» таким действием не является. Ошибки несимметричны: занятое зря место видно, стоит копейки и обратимо; удалённый файл — ничего из этого.
houseplan/files/cleanupбольше не сносит папку по слову клиента. После перепривязки устройства файлы копируются под новый id, а старая папка удалялась — черезrmtree, по тому id, который прислала карточка. Два плохих исхода: при частичном копировании часть ссылок продолжает указывать внутрь этой папки (миграция намеренно их не переписывает — то есть это были живые ссылки на удаляемые файлы), а неверный или устаревший id от любого клиента уничтожил бы инструкции существующего устройства. Теперь сервер сам сверяется с сохранённой конфигурацией, под её блокировкой, и удаляет только то, на что никто не ссылается.- План удалённого пространства ждёт тридцать дней вместо часа. Удаление пространства осознанно, но час — короткое окно, чтобы заметить промах.
v1.46.4 — 2026-07-28 (потеря данных: отцеплённые планы убирались как мусор)
- Отцеплённый план больше не удаляется через час. Переключение пространства
в режим «нарисовать» снимает ссылку и, как редактор всегда и говорил,
оставляет картинку на диске, чтобы её можно было вернуть. Уборка, добавленная
в v1.46.0, этой разницы не делала: считала «на файл сейчас никто не
ссылается» синонимом «файл брошен» и применяла часовое правило. На установке
автора плановый проход в итоге удалил два плана этажей, отцеплённых
несколькими неделями раньше, — восстановить их было нечем. Если вы отцепляли
план после v1.46.0 и инстанс перезапускался или проработал сутки — загляните в
config/houseplan/plans/и напишите в Telegram-чат, если файла нет. Правило теперь такое: коммит по-прежнему удаляет ровно то, что заменил, — это он знает наверняка. Дальше вопрос в том, означает ли «непривязан» «брошен», и ответ зависит от случая. У пространства, у которого плана нет вовсе, его отцепили — и, возможно, вернут: его файлы не удаляются никогда. У пространства, у которого план есть, лишние файлы могут быть только его же отвергнутыми загрузками — они по-прежнему уходят через час. Вложения вне промежуточной папки диалога ждут месяц; сама промежуточная папка, где по построению лежит только загрузка из несохранённого диалога, сохраняет часовое правило.
v1.46.3 — 2026-07-28 (перепроверка v1.46.2: HP-1462-01)
- Уборка при старте действительно убирает. Она искала свои же runtime-данные по домену, а во время запуска Home Assistant ещё не считает интеграцию загруженной — поэтому поиск возвращал пустоту, и проход тихо вырождался в удаление незавершённых передач, оставляя настоящую работу таймеру через 24 часа. При перезапусках чаще, чем раз в сутки, она не выполнялась вообще. Теперь используется объект, который у неё и так был на руках.
- Тест, который должен был это доказать, проходил по неверной причине. Он создавал лишние файлы до сохранения конфигурации, а сохранение тоже собирает мусор — так что к моменту перезапуска убирать было уже нечего. Переписан: файлы создаются после сохранения; добавлен второй тест, который дёргает плановый таймер отдельно, и третий, который запускает перезапуск и сохранение одновременно и проверяет, что принятая конфигурация никогда не ссылается на удалённый уборкой файл.
v1.46.2 — 2026-07-28 (перепроверка v1.46.1: HP-1461-01, -02)
- Файл, который в итоге никому не понадобился, убирается, даже если больше ничего не сохраняют (HP-1461-01). Сборка привязана к записи конфигурации — это верно для того, что запись вытесняет, но оставляет зазор: отмените диалог после того, как файл уже загрузился, потеряйте связь сразу после, или вызовите API загрузки напрямую — и на файл никто не ссылается, а будущей записи, которая бы это заметила, нет. Добавленное в v1.46.1 ежедневное подметание убирало только незавершённые передачи, поэтому обещание «отменённое вложение исчезнет через час» не выполнялось там, где никто ничего не правит. Теперь подметание сверяется с сохранённой конфигурацией — под той же блокировкой, что и запись, — и собирает устаревшие непривязанные вложения и планы тоже.
- Перетаскивание больше не отменяется чужим перемещением (HP-1461-02). Когда в v1.46.1 полная карточка научилась следить за позициями, она защищала те, что вы подвинули, но ещё не отправили, — вот только читала этот список после сброса отложенной записи, а сброс его первым делом опустошает. При настоящем перетаскивании, когда запись уже запланирована, список оказывался пустым, и старая серверная позиция закрашивала ваше движение. Теперь снимок снимается до сброса, и вдобавок удерживаются позиции, отправленные, но ещё не подтверждённые: пока сервер не подтвердил позицию, авторитет по ней — та карточка, которая её подвинула.
- Два теста доросли до своих же описаний: тест загрузки теперь действительно отменяет задачу запроса, а не только проходит по путям ошибок, а смоук синхронизации позиций планирует настоящую отложенную запись и задерживает её — именно этот порядок и терял перетаскивание.
v1.46.1 — 2026-07-28 (перепроверка v1.46.0: HP-1460-01 … -03)
- Две загрузки с одинаковым именем больше не сталкиваются (HP-1460-01).
v1.46.0 перестала затирать вложения, но выбор свободного имени и его занятие
были двумя шагами: две загрузки, попавшие между ними, сходились на одном
имени, обе рапортовали успех, и одни байты заменяли другие. Теперь имя
занимается атомарно в момент выбора — двадцать одновременных загрузок
manual.pdfдают двадцать файлов. Тот же механизм используется при переносе файлов на перепривязке, где был ровно такой же зазор. Заодно там же: имя предельной длины теряло расширение, а суффикс коллизии выталкивал его за предел, и вложение сохранялось под именем, которое сервер обратно не отдаёт — вечный 404 на файл, который интерфейс считал прикреплённым. - Прерванная загрузка больше не оставляет временный файл навсегда
(HP-1460-02). Уборка стояла в
except Exception, мимо которого отменённый запрос проходит насквозь, а сборщик заглядывает только в папки маркеров — поэтому оборванная передача оставляла.upload-*, и убрать его было некому. Теперь убирает любой путь выхода, запрос с двумя файлами отклоняется сразу, а брошенные временные подметаются при старте и раз в сутки. Запись идёт порциями по мегабайту, а не отдельной задачей на каждые 64 КБ. - Две полные карточки рядом держат одинаковые позиции (HP-1460-03). v1.46.0 научила следить за перемещениями статическую карточку и оставила позади полную, поэтому перетаскивание иконки в одном окне не двигало её в другом до перезагрузки. Теперь следит — и не мешает собственному перетаскиванию: чужая ревизия, пришедшая в его разгар, сливается, а не накатывается сверху, и карточка не перечитывает то, что записала сама.
v1.46.0 — 2026-07-28 (полный внешний аудит v1.45.4: HP-1454-01 … -10)
Безопасность
- Загруженный SVG-план больше не является живым документом origin вашего
Home Assistant (HP-1454-01, high — блокер релиза). Внутри карточки план
подключён через
<image>, где скрипты не выполняются; но тот же URL, открытый напрямую, становился документом верхнего уровня в origin самого HA, и<script>в нём мог читатьlocalStorageсессии и обращаться к API. Для загрузки нужно право записи, а оно по умолчанию есть у каждого аутентифицированного пользователя, и подписанную ссылку несложно передать администратору. Ответы с SVG теперь несут заголовок Content-Security-Policysandbox, который помещает документ в opaque origin. Только SVG — CSP на PDF способен сломать встроенный просмотрщик браузера, а растровая картинка ничего выполнить не может. Для существующих планов ничего не меняется: карточка рисует их ровно как раньше.
Целостность данных
- Инструкция, приложенная к устройству, больше не затирает предыдущую
(HP-1454-02). Загрузка писала прямо в
<маркер>/<имя файла>, вне транзакции конфигурации: отмена диалога или отвергнутое сохранение оставляли сохранённую ссылку указывающей на новые байты. А все новые иконки грузили в одну общую папку, поэтому две с файломmanual.pdfначинали ссылаться на один физический файл. Теперь загрузка занимает свободное имя и никогда не перезаписывает, новая иконка получает собственную промежуточную папку, файлы из которой переезжают к настоящей иконке при принятом сохранении, а загрузка, которую никто не сохранил, убирается через час. Имя, на которое уходит коллизия, сменилось сmanual (2).pdfнаmanual-2.pdf: старое санитайзилось на обратном пути, поэтому переименованное вложение записывалось и больше не отдавалось (нашёл новый тест; до этого релиза так же ломались коллизии при перепривязке). - Две быстрые правки больше не теряют вторую (HP-1454-03). Debounce разносил старты сохранения, а не сами сохранения. Если одно длилось дольше полусекунды — занятый сервер, медленная связь, — следующая правка уходила с той же ревизией, сервер принимал первую и отклонял вторую, а обработчик конфликта перечитывал серверную копию поверх локальной. Правка исчезала, и сообщение винило «другое окно», которого не было. Записи сериализованы: по одной за раз, каждая с ревизией, которую вернула предыдущая.
Корректность и пределы
- Открытые границы снова следуют за геометрией (HP-1454-04). Их кэш ключевался только по id комнат и связям, поэтому смена пропорций пространства или перетаскивание вершины оставляли открытые границы — и свет, который через них проходит, — в старых координатах до перезагрузки. Ключом стала сама отрисованная модель, из которой они и вычисляются.
- Конфигурацию больше нельзя сделать сколь угодно тяжёлой (HP-1454-05).
Внешние коллекции были ограничены, вложенные — нет. Полигон на 150 000 точек
или список из 100 000 идентификаторов проходили валидацию, а потом каждый
рендер по ним ходил. Появились пределы на вершины полигона, связи открытых
границ, управляемые сущности, вложения, длину текста и ссылок, плюс общий
предел размера сериализованной конфигурации. Устаревшее поле
segmentsотбрасывает сервер, а не надежда на современный клиент. - Большие файлы передаются потоком, а не через память (HP-1454-06). Инструкция на 50 МБ целиком читалась в память на приёме и ещё раз на отдаче; пара параллельных скачиваний — заметная нагрузка на слабый хост Home Assistant. Загрузка пишется во временный файл потоком, отдача идёт с диска.
Согласованность
- Статическая карточка пространства учитывает настройки заливки комнаты (HP-1454-07). Она строит модель другой функцией, и настройки комнаты в неё не переносились — комната, которой вы выключили заливку, всё равно закрашивалась.
- Перемещение иконки сразу видно на статической карточке (HP-1454-08). Layout — отдельное состояние без ревизии и без события: перетаскивание на полной карточке оставляло соседнюю статическую со старой позицией до изменения конфигурации или перезагрузки страницы. Теперь записи layout хранят ревизию, возвращают её и сообщают о себе — заодно оптимистическая блокировка на полной записи layout начала что-то значить, ведь точечная запись раньше сбрасывала счётчик.
- Предупреждение о пропавшем плане исчезает вместе с пространством (HP-1454-09). Уборка смотрела только на существующие пространства, поэтому удаление или переименование оставляло предупреждение в «Ремонте» навсегда.
- Сборка:
serialize-javascriptподнят выше двух advisory (HP-1454-10). В рантайме недостижим, production-зависимости и так были чисты, но это одна строка.
v1.45.4 — 2026-07-28 (ревью v1.45.3: R5-1, R5-2)
- Частично успешный ответ на подпись больше не пропускает выдержку (R5-1). Бэкенд подписывает каждый путь независимо: тот, что подписать не удалось, логируется, пропускается, и вызов всё равно завершается успешно с остальными ссылками. Карточка считала любой успешный вызов «весь батч готов», сбрасывала выдержку для всех путей в нём и записывала только вернувшиеся ссылки — и путь, который бэкенд стабильно пропускал, запрашивался заново на каждом рендере, то есть ровно то усиление, ради которого выдержка и вводилась в v1.45.2. Теперь путь считается подписанным, только если в ответе действительно есть ссылка на него; остальные уходят в выдержку по отдельности, ключи, которых не просили, игнорируются, а перерисовка запускается лишь при появлении хотя бы одной новой подписи.
- Снимок состояния больше не противоречит репозиторию (R5-2). Там всё ещё
было написано, что в
mainлежат релизы только до v1.40.1, и приводились счётчики тестов на несколько релизов назад — при том что строка версии рядом исправно обновлялась. Читающий его человек или агент получал неверную модель веток и заниженное представление о покрытии. Роли веток описаны точно, а счётчики убраны:npm run inventoryпечатает их из дерева, и устаревать больше нечему.
v1.45.3 — 2026-07-27
- «Значение вместо иконки» невозможно было сохранить (issue #3). Опция
появилась в редакторе устройств ещё в v1.26.0, но серверная схема всё это
время принимала только
badge,rippleиicon_ripple. При её выборе сохранение падало сnot a valid value for dictionary value @ data['config']['markers'][n]['display'], а поскольку один отвергнутый маркер валит всю запись конфигурации, план не сохранялся вообще, пока настройку не отменишь. Спасибо @RemyRoux за отчёт и точный текст ошибки. - Списки опций теперь в одном месте и сверяются между языками.
DISPLAY_MODES,TAP_ACTIONS,SPACE_FILL_MODESиROOM_FILL_MODESэкспортируются из карточки, и backend-тест читает их, проверяя, что схема принимает каждое значение, которое пользователь реально может выбрать. Теперь добавить опцию в редактор и забыть про схему — значит уронить тесты, а не узнать об этом из чужого сообщения об ошибке.
v1.45.2 — 2026-07-27 (закалка по ревью v1.45.1: R4-1, R4-2)
- Сбой уборки больше не превращает принятое сохранение в ошибку (R4-1).
Сборка вытесненных файлов плана идёт уже после того, как конфигурация
сохранена, но ошибка при обходе каталога — он может исчезнуть или стать
недоступным между проверкой и обходом — вылетала наружу из
config/set. Клиент видел неудачу для ревизии, которую сервер закоммитил, а его повтор возвращался с конфликтом. Теперь сборщик сообщает «ничего не убрано» вместо исключения, аconfig/setпишет в лог и продолжает: событие уходит, новая ревизия возвращается. - Один запрос подписи на ссылку вместо одного на рендер (R4-2). Множество
ожидающих очищалось в момент отправки батча, а не по возвращении, поэтому
пока запрос
content/signбыл в полёте, каждая перерисовка ставила ещё один — шесть вызовов там, где нужен один, и куда хуже на медленном (а не просто занятом) сокете. Состояния «в очереди» и «в полёте» теперь разделены, после ошибки включается выдержка (2 с с удвоением до 60 с) вместо повтора на следующем кадре, а запрос, который так и не завершился, перестаёт блокировать повторы через 15 с. Поздний ответ, пришедший после размонтирования карточки, больше не вызывает перерисовку. - Тесты: восемь юнит-тестов подписывателя с ручным разрешением promise (четыре падают на v1.45.1), backend-тест на то, что сломанный сборщик оставляет сохранение успешным с рабочей ревизией, и проверка исчезнувшего каталога в тестах чистого сборщика.
v1.45.1 — 2026-07-27 (повторное ревью v1.45.0: R3-1, R3-2)
- Уборка старых файлов плана перенесена внутрь транзакции конфига (R3-1,
high). v1.45.0 сделала загрузку безопасной, но отдала удаление клиенту:
после успешного сохранения карточка просила бэкенд убрать всё, кроме только
что закоммиченного файла. Два открытых редактора невозможно упорядочить —
задержавшийся запрос одного клиента удалял план, который только что сохранил
другой, и принятая конфигурация оставалась со ссылкой в пустоту, то есть
ровно с тем ущербом, ради которого вводился copy-on-write. Команда
houseplan/plan/cleanupубрана. Теперьconfig/setубирает сам, под своей блокировкой, сравнивая конфигурацию, которую заменил, с той, которую принял: файл, на который ссылалась старая ревизия и не ссылается новая, удаляется, а любая другая непривязанная загрузка не трогается, пока ей не исполнится час — свежая может принадлежать ещё не завершённой чужой транзакции. - Статическая карточка пространства снова показывает подложку (R3-2). Она
подписывала URL и выбрасывала результат —
getCardSize()правил временную модель, аrender()строил свою заново из конфига, — поэтому<image>запрашивал защищённый путь и на каждом рендере получал 401. Обе карточки теперь используют один подписыватель, и статическая заодно получила батчи, учёт срока годности и периодическое переподписывание, которые были только у основной. Её множество ожидающих запросов освобождается вfinally, так что одна неудача больше не блокирует ссылку до конца жизни страницы. - Новые тесты: пять backend-сценариев чередования двух клиентов из отчёта
(поздний коммит, незакоммиченная загрузка, устаревший сирота, чужие файлы,
отвергнутое сохранение), сборщик вынесен в чистый модуль и покрыт юнит-
тестами, плюс
smoke_space_card_bgна подписанный фон — он падает на v1.45.0, где в DOM попадает сырой URL.
v1.45.0 — 2026-07-27 (внешнее ревью v1.44.8: R2-1, R2-2, R2-3)
- Отвергнутое сохранение больше не может испортить рабочий план (R2-1,
high). Файл плана записывался под финальным именем — попутно удаляя вариант
с другим расширением — до проверки ревизии конфига. Если запись потом
отвергалась (конфликт ревизий, валидация, обрыв связи), живой план оказывался
уже подменён, а сохранённый конфиг мог ссылаться на удалённый файл. Теперь
загрузка идёт в версионированное имя (
<space>.<токен>.<ext>) и ничего не удаляется; карточка просит бэкенд убрать устаревшие файлы только после принятой записи конфига. Падение между шагами оставляет один осиротевший файл, который подберёт следующая успешная загрузка. - Подписанные ссылки больше не протухают навсегда на долгоживущих экранах (R2-2). Бэкенд подписывает не более 200 путей за запрос и молча отбрасывает остальные, а карточка отправляла весь кэш одним вызовом и считала любую запись годной вечно. Начиная с 201-го вложения поздние ссылки переставали обновляться и через 24 часа тихо ломались. Теперь запросы бьются на батчи по общему лимиту, записи помнят свой возраст (стареющая ссылка работает, пока едет замена, протухшая не отдаётся вовсе), а кэш чистится до ссылок, на которые конфиг всё ещё ссылается.
- Климат комнат считается один раз на обновление, а не на каждую комнату (R2-3). Каждая комната запрашивала температуру и влажность по отдельности, и каждый запрос заново обходил весь реестр сущностей: 60 комнат и 2000 сущностей — это ~120 обходов на рендер, целый кадр на метаданные, которые не менялись. Теперь один проход строит карту по всем зонам с привязкой к снимку Home Assistant: свежие состояния видны всегда, а посторонние перерисовки не стоят ничего. Замер в смоуке: 133 обхода реестра на обновление до, 2 после — и число больше не растёт с числом комнат.
smoke_ux_fixesписал скриншот по жёстко зашитому пути/tmpи не запускался на Windows — теперь берёт временную папку у ОС.- Новые тесты:
smoke_plan_upload_reject(чистка только после принятого сохранения),smoke_sign_cap(201 ссылка, батчи, чистка, срок годности),smoke_climate_once(число обходов не растёт с числом комнат), плюс backend-покрытие версионированных имён и юнит-тесты новых хелперов.
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). Эндпоинт с файлами
требует авторизации, поэтому карточка просит бэкенд подписать ссылку на план —
но подпись подставлялась внутри мемоизированной модели пространства, а она
кэшируется по отпечатку конфига. Неподписанная ссылка «замерзала» в кэше,
подпись до элемента
<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-сенсор, привязанный к помещению).
- Тот же раздел настроек появляется в диалоге комнаты сразу после замыкания контура.