PIN_CATALOG: 16 доп. GPIO с возможностями (аналоговый вход/цифровой
вход/цифровой выход) и настоящими координатами на плате — те же,
что заняты sensor-1/fan-1/light-1/pump-1, исключены. Список в форме
фильтруется под тип добавляемого устройства (вход/выход, аналоговый/
дискретный) и не предлагает уже занятые пины.
Устройство с выбранным пином рисуется проводом прямо от этого пина
на схеме (левый/правый край платы, высота = позиция пина), а не от
общей пунктирной шины EXT — та осталась только как запасной вариант
для устройств без указанного пина.
Раньше "добавить сигнал" дописывал ещё один sensor_type к sensor-1 —
концептуально неверно и для дискретных входов, и тем более для выходов.
Теперь и вход (датчик), и выход (актуатор) создаются как полноценное
отдельное MQTT-устройство со своим external_id/zone_id — так же, как
завели бы новое Device на платформе.
state.Store обобщён: 4 фиксированных слота (плата, GPIO-пины, нельзя
удалить) + произвольное число доп. устройств (свой slot "extra-N",
можно удалить). HTTP: POST /api/devices (создать, kind=sensor|actuator),
DELETE /api/devices/{slot}.
На схеме доп. устройства подвешены к пину EXT пунктирным проводом —
визуально отделены от жёстко распаянных GPIO4/16/17/18. Можно указать
и произвольный пин (просто текстовая метка на схеме, без валидации) —
подписывается у провода вместо общего EXT.
Карточки создаются/удаляются динамически при получении снапшота через
SSE, без перезагрузки страницы.
transform-origin: center у SVG-группы лопастей считался от bounding box,
а не от геометрического центра ступицы (0,0) — вентилятор вращался вокруг
смещённой точки. Зафиксировано в 0 0.
Датчик больше не подписан как «DHT22» (сбивало с толку при добавлении
дискретных сигналов вроде door_open) — просто «Датчик», а сами сигналы
в карточке визуально разбиты на группы «Аналоговые»/«Дискретные».
Абстрактные кружки заменены на схему ESP32 DevKit с подписанными
GPIO-пинами и проводами к каждому периферийному устройству — видно,
что куда подключено. Датчик больше не ограничен фиксированными
температурой/влажностью: можно добавить любой именованный сигнал
(аналоговый слайдер или дискретный тумблер вкл/выкл) — бэкенд уже
принимал произвольный sensor_type, не хватало только формы.
Главный фикс: значения теперь публикуются в MQTT автоматически
(debounce ~350мс после слайдера, мгновенно для тумблера), а не только
по кнопке — раньше несохранённые локальные правки терялись при любом
переподключении SSE (сервер присылает эталон при каждом новом
сабскрайбе), из-за чего казалось, что слайдеры ни на что не влияют.
Плата и подписи обновляются локально сразу при вводе, без ожидания
ответа сервера.
Go-сервис без внешних фронтенд-зависимостей: embedded HTML-страница
с SVG-изображением платы (датчик + вентилятор + свет + помпа) и
Server-Sent Events для live-обновления. Слушает devices/+/commands
и реально отвечает ack'ом, публикует devices/{id}/telemetry —
протокол зеркалит ingest-service/device-control-service основного
репозитория, общего кода между репозиториями нет. Дефолтные
external_id (sensor-1/fan-1/light-1/pump-1) совпадают с
DemoGrowboxSeeder платформы.