mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
676 lines
72 KiB
Markdown
Executable File
676 lines
72 KiB
Markdown
Executable File
# История изменений
|
||
|
||
> Русская версия [docs/CHANGELOG.md](CHANGELOG.md). Переведены записи начиная
|
||
> с v1.42.0 (2026-07-26); более ранние доступны только в английском файле.
|
||
>
|
||
> **Правило проекта:** оба файла пополняются в одном коммите с самим
|
||
> изменением — как и остальная документация (см. docs/STATUS.md).
|
||
|
||
## 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-Policy
|
||
`sandbox`, который помещает документ в 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-сенсор, привязанный к помещению).
|
||
- Тот же раздел настроек появляется в диалоге комнаты сразу после замыкания
|
||
контура.
|