Умный дом на Raspberry Pi: разложили 123 датчика по полкам

Домашняя автоматизация на zigbee2mqtt, Mosquitto, MariaDB и самописной PHP-панели доросла до состояния, когда в интерфейсе стало невозможно найти нужный датчик. Рассказываю, как из одной простыни карточек получились группы, что именно пришлось менять в коде и почему это не «косметика», а вопрос удобства эксплуатации.

28 октября 2026 · Raspberry Pi, Zigbee, MQTT, PHP 8.2, MariaDB

умный домMQTTzigbee2mqtt PHPинтерфейсыэксплуатация
Содержание
  1. Что было не так
  2. Датчики: группы и «Служебные»
  3. Правила: группы и счётчики
  4. Как это устроено внутри
  5. Чем проверяли
  6. Что осталось

1. Что было не так

Система собиралась годами, и в базе накопилось 123 разных MQTT-топика. Панель честно показывала их все — сеткой карточек, одну за другой. Проблема не в том, что карточек много, а в том, что большинство из них — не датчики.

zigbee2mqtt с настройкой output: json публикует одно сообщение на устройство, а логгер раскладывает JSON на поля: temperature, humidity, battery, но вместе с ними — linkquality, indicator_mode, power_on_behavior, калибровки, режимы, интервалы опроса и даже сами команды .../set. В справочнике это всё выглядело как равноправные «датчики».

123топика в базе
63из них технические
61настоящий датчик
1общая куча карточек

Второй слой той же проблемы — правила автоматизации. Их 18, и они лежали точно так же: списком, где «Нагрев_РЫБ_вкл», «земляника подсветка вкл 8:00» и «Защита от заливания» стоят подряд. Найти нужное правило, чтобы поправить порог, — это чтение всего списка глазами.

Почему это не косметика. Пока датчики в одной куче, невозможно быстро ответить на рабочие вопросы: «этот датчик вообще живой?», «какие правила у аквариума?», «почему реле щёлкнуло не по расписанию?». В эксплуатации такие вопросы возникают каждую неделю.

2. Датчики: группы и «Служебные»

Страница датчиков (/admin/sensors.php) теперь состоит из сворачиваемых групп:

Каждая группа — обычный <details> со счётчиком датчиков. Состояние «свернуто/развёрнуто» запоминается в браузере, так что при следующем заходе страница выглядит так, как её оставили. Есть кнопки «Раскрыть все / Свернуть все» и поиск по имени, комнате или топику — при поиске нужные группы раскрываются сами.

Возраст значения в карточке

К каждой карточке добавилось последнее значение и когда оно пришло: «23.4 · 5 мин назад», «— · 18 дн назад». Это закрывает старую ловушку самописных систем: строка в таблице последних значений живёт вечно, поэтому устройство, которое молчит неделю, в интерфейсе выглядит бодрым. Теперь молчание видно сразу, и это повод проверить связь, а не менять батарейку наугад.

3. Правила: группы и счётчики

В таблицу automations добавилась колонка group_name. Правила разложены по группам, у каждой в заголовке — счётчик вида «✅ 6 / ❌ 0»: сколько включено и сколько выключено. Выключенные правила в самописных системах копятся и забываются, а теперь они видны в своей группе, а не растворяются в общем списке.

Группа правилПравилЧто внутри
Аквариум6нагрев по температуре, свет аквариума по расписанию
Свет4коридор по движению, выключение через 60 с, подсветка земляники
Полив3полив по влажности почвы, страховка от заливания
Климат2тревоги по PM2.5 и ЛОС с письмом
Безопасность1протечка в ванной — включить свет
Тесты2старые черновики, выключены

В форме правила появилось поле «Группа правила» с подсказками. Сохранение проверил живьём: создал правило с группой, перенёс в другую группу, удалил — всё отработало, в базе снова 18 правил.

4. Как это устроено внутри

Группа датчика не задаётся вручную для 123 топиков

Прописывать группу каждому топику — это работа на вечер и вечная поддержка. Логика получилась трёхступенчатая:

  1. поле «Группа» в справочнике датчиков (если заполнено — оно главное);
  2. иначе — группа любого другого поля того же устройства: поставил группу батарейке датчика, и все поля этого устройства переехали туда же;
  3. иначе — комната из справочника, и только потом «Без группы» (эта секция всегда внизу).

Отдельно решается судьба технических топиков: функция iot_is_service() считает служебными полный JSON устройства (тема без третьего сегмента), всё, что содержит /set, и перечисленные поля вроде linkquality или калибровок. Важная деталь: state в этот список не входит — у реле и розетки это главное значение, а не мусор.

Мелочь, которая экономит нервы. Устройства, которых нет в справочнике совсем, попадали бы в «Без группы». Для них есть маленькая карта $devFallback, а имена вида zigbee2mqtt/0x… автоматически относятся к «Розеткам».

Сворачивание без JavaScript-библиотек

Основа — нативный <details>/<summary>: свёрнутость работает даже если скрипты не загрузились. JavaScript добавляет только две вещи: запоминание состояния в localStorage и фильтрацию карточек по подстроке.

<details class="sensor-group" data-group="Аквариум" open>
  <summary>📁 Аквариум <span>9</span></summary>
  <div class="sensors-grid">…карточки…</div>
</details>

Миграция базы

Колонку добавлял администратор базы, а не приложение: у прикладной учётки нет права ALTER — и это правильно. Перед изменением снят дамп таблицы правил, чтобы откат был не «по памяти», а по файлу.

ALTER TABLE automations
  ADD COLUMN group_name VARCHAR(64) NULL DEFAULT NULL AFTER name;
UPDATE automations SET group_name='Аквариум' WHERE id IN (151,152,170,171,196,197);
-- и так далее по группам

5. Чем проверяли

Правки в работающей системе проверяются не «на глаз», а гейтами:

Что особенно приятно. Всё это — без единого нового демона и без перезапуска служб: изменения только в двух PHP-страницах и одной колонке в базе. Движок правил, логгер и брокер работали всё время.

6. Что осталось

  1. Мобильная вёрстка групп. На узком экране счётчики в заголовках могут переноситься — надо посмотреть на телефоне.
  2. Названия групп. Разбивка сделана по смыслу устройств; если захочется делить по комнатам — это одна колонка и один запрос.
  3. Сведение cron-автоматики в движок. Часть логики (кормушка, нагрев, свет) до сих пор живёт в отдельных cron-скриптах, то есть в двух местах. Это следующий архитектурный шаг: одно место для правил проще отлаживать.
Проект целиком. Отдельным документом лежит описание проекта и архива: состав файлов, службы и порты, схема базы, порядок развёртывания с нуля и список грабель, на которые уже наступали. Смотри project.html рядом с этой статьёй.