Мой первый вечер с Home Assistant закончился анекдотом в трёх актах. Переключатель насоса в интерфейсе щёлкал с приятной анимацией — реле на плате молчало. Термометр из теплицы красовался карточкой с надписью «недоступно» — хотя Serial платы показывал честные 21.4 градуса, публикуемые каждую минуту. А третий девайс, датчик двери, сначала работал, но после первого же рестарта HA вечно показывал последнее состояние, живо оно или давно не существует.
Ни одна из трёх бед не была связана с платой: прошивки работали, брокер гонял сообщения, mosquitto_sub показывал поток. Виноваты оказались три разные мелочи на стороне умного дома: интеграция смотрела не на тот брокер, в имени топика пряталась опечатка, а состояние не было retained. Каждая чинится одной строкой — если знать, куда смотреть.
В этом уроке соединяем два берега: брокер-аддон и интеграцию MQTT на стороне Home Assistant, ручное описание сущностей в configuration.yaml на стороне конфига, и тестовый прогон через mosquitto_pub с mosquitto_sub до всякого интерфейса. Платную сторону обратного потока — подписку на команды — мы уже собирали в уроке про subscribe и callback, здесь встретимся с ней на другом конце провода.
Брокер-аддон и интеграция: кто с кем разговаривает
Схема у умного дома с MQTT ровно та же, что мы рисовали в уроке про издателя и подписчика: Home Assistant не ходит в платы напрямую — он подписчик и издатель, как и ваш скетч. Всё общение идёт через брокер. В Home Assistant OS роль брокера обычно играет официальный аддон Mosquitto broker: ставится из магазина аддонов, после старта слушает порт 1883. Пара нюансов аддона: в логе старта видна строка про открытый 1883, а для внешних клиентов — ваших плат и ноутбучных утилит — в настройках аддона включается локальный доступ. Параллельно в системе появляется интеграция MQTT — она подключается к брокеру и слушает весь поток, это её руки и уши.
Ключевая фраза, на которой я спотыкался сам: устройство и интеграция обязаны смотреть на один и тот же брокер. Если на малине остался старый mosquitto с урока про установку брокера, а аддон поднялся рядом — платы, подключённые к старому, для HA невидимы при живом, слава богу, потоке данных. Проверяется за минуту: в настройках интеграции MQTT виден адрес брокера, с которым она соединилась, — сверьте его с адресом в скетчах.
Второй этаж той же проверки — утилиты. Подпишитесь на поток с ноутбука, указав адрес того брокера, куда публикует плата: mosquitto_sub -h адрес -t home/# -v. Если поток виден, а в HA пусто — вы смотрите в одного брокера, а HA в другого. Если пусто везде — виновата плата или сеть, и это уже территория диагностики из первых уроков раздела.
# /config/configuration.yamlmqtt: sensor: - name: "Теплица температура" state_topic: "home/dacha/greenhouse/temp" unit_of_measurement: "°C" availability_topic: "home/dacha/status" payload_available: "online" payload_not_available: "offline" switch: - name: "Насос теплицы" state_topic: "home/dacha/pump/state" command_topic: "home/dacha/pump/set" payload_on: "1" payload_off: "0" optimistic: false # ждём подтверждение из state_topic availability_topic: "home/dacha/status" payload_available: "online" payload_not_available: "offline" # после правки: Настройки -> Система -> Перезагрузка конфигурации MQTTРазбор — по полям switch, потому что именно там живут обе стороны диалога. state_topic — куда плата докладывает своё состояние, command_topic — куда HA отправляет команду. Имя сущности — человекочитаемый ярлык для карточки: на связь с платой оно не влияет, всё решают только топики. Плата подписана на set и в callback щёлкает реле — это та самая механика из урока про callback, один в один. Дальше — вторая половина, которую я поначалу пропускал: после выполнения команды плата обязана опубликовать факт в state_topic. Без этого optimistic: false держит переключатель в подвешенном состоянии: HA не догадывается, что команда сработала, пока не увидит подтверждение своими глазами.
availability_topic связывает карточку со статусом из урока про LWT: слову online соответствует payload_available, слову offline — payload_not_available. Именно так карточка термометра из вступления узнала бы, что плата умерла, вместо вечного «недоступно» наугад. Пара payload_on/payload_off — словарь сообщений команды: я гоняю 1 и 0, но HA с тем же успехом переварит ON/OFF, лишь бы плата и конфиг договорились об одном словаре.
Дальше — ритуал проверки, без которого я больше не трогаю ни один конфиг. Сначала руками, из консоли, как это делает HA: публикуем команду в command_topic и смотрим в соседнем терминале, что плата ответила в state_topic. Интерфейс — последняя инстанция, а не первая: он прячет слишком много, а консоль показывает сырые байты и не врёт.
# терминал 1: слушаем весь поток устройстваmosquitto_sub -h 192.168.1.60 -t 'home/dacha/#' -v # терминал 2: дёргаем насос, как это сделает HAmosquitto_pub -h 192.168.1.60 -t 'home/dacha/pump/set' -m '1' # в терминале 1 ожидаем два события подряд:# home/dacha/pump/set 1 <- команда пришла на брокер# home/dacha/pump/state 1 <- плата подтвердила фактТопики и retain: где прячутся вторые 80 процентов грабель
| Поле конфига | За что отвечает | Симптом опечатки |
|---|---|---|
| state_topic | куда плата публикует состояние | карточка вечно «неизвестно» |
| command_topic | куда HA шлёт команды | переключатель щёлкает впустую |
| payload_on / payload_off | словарь сообщений команды | команда уходит, плата её не понимает |
| availability_topic | жив ли источник статуса | карточка «недоступно» при живой плате |
| unit_of_measurement | единица измерения сенсора | число без единицы, графики без осей |
Регистр в топиках — грабля, на которую я наступал дважды и дважды же оправдывал роутер. home/dacha/Greenhouse/temp и home/dacha/greenhouse/temp — разные адреса: брокер различает байты, а не смыслы, и «почти одинаковые» топики просто не встречаются. Правило из урока про публикацию показаний спасает: топики целиком в нижнем регистре, слова через слэш, единицы в конфиге сущности, а не в payload. Схему адресов я рисую до первой строки YAML — на бумаге опечатка стоит секунду, в карточке «неизвестно» — вечер.
Retain на state_topic — да, без вариантов: после рестарта HA карточки показывают последние значения мгновенно, а не «неизвестно» до первой публикации. Число с условием: у меня термометр публикует раз в минуту, и без retain после каждого рестарта дома минуту все графики врут отсутствием данных — мелочь, на которую натыкаешься каждый раз заново. А вот retain на command_topic — прямая ловушка: брокер отдаст ретейнутую команду каждому новому подписчику, и переподключившаяся плата честно выполнит старое включение насоса — хоть сутки спустя. Команды не кэшируем, это событие, а не состояние.
Замыкает список классика эха: публикация подтверждения в тот же топик, куда слушает команда. Плата получает своё же 1, снова щёлкает реле, снова публикует — по кругу. Разводим адреса: set для команд, state для фактов, и оба объявлены в конфиге сущности. Если хочется перепроверить себя без железа, протокол разбора строк я гоняю в симуляторе командного парсера на Serial — там видно каждый топик и каждый payload до включения насоса.
У сущностей в Home Assistant есть и второй путь — MQTT discovery: устройство само публикует описание сущности в служебный топик homeassistant/switch/насос/config, брокер хранит его с retain, и HA строит карточку без единой строки YAML. Так по умолчанию делают ESPHome и прошивки Tasmota. Ручной YAML из этого урока я всё равно ставлю новичкам первым делом: он честный — вы своими руками называете каждое поле и понимаете, откуда берётся карточка. Discovery удобен, когда плат десять и все на ESPHome, но это уже другая история.
После правки YAML не забудьте последнюю милю: конфиг MQTT перезагружается отдельно — Настройки, Устройства и службы, интеграция MQTT, меню, Перезагрузить. Рестарт всего HA тоже спасает, но дольше. Если карточка после перезагрузки не появилась — причина уже лежит в логах интеграции, и чаще всего это нечитаемый YAML. В списке той же интеграции видно каждое устройство и его сущности с их state_topic и command_topic — удобное место для сверки адресов со скетчем, когда что-то не сходится.
Тупик напоследок: сущность задвоилась, и в списке болтались два насоса — старый и новый. Причина — retained discovery-сообщение от эксперимента недельной давности: брокер честно хранил конфиг со старым именем. Лечение — опубликовать пустой payload в тот самый config-топик, retained-запись стирается, призрак исчезает. После того случая служебные discovery-топики я всегда проверяю подпиской homeassistant/# — раз в пару месяцев там обнаруживается кто-то из прошлой жизни.
- Отправьте home/dacha/pump/set 1, затем set 0: в мониторе после каждого щелчка реле уходит подтверждение в state_topic — так выглядит диалог HA с платой при optimistic: false.
- Отправьте home/dacha/pump/set ON: конфиг обещал словарь 1 и 0, значит мусор уходит в игнор. Поменяйте payload_on в справочнике и в проверке на ON и добейтесь, чтобы обе стороны снова совпали.
- Отправьте home/dacha/temp 21.4: топик не заведён как command_topic — игнор. Это регистр и опечатки вживую: сущность не увидит данные, пока адреса не совпадут байт в байт.
- В конфиге урока добавьте сенсор door со state_topic home/dacha/door/state и availability_topic из LWT-урока: проверьте по таблице, что произойдёт с карточкой при offline плате.
- HA общается с платой только через брокер: аддон Mosquitto слушает 1883, интеграция MQTT слушает брокера — адреса в скетчах и интеграции совпадают байт в байт
- state_topic — куда плата докладывает факт, command_topic — куда HA шлёт команду; после команды плата публикует подтверждение, иначе optimistic: false вешает переключатель
- Ручное описание в configuration.yaml: mqtt, sensor или switch, словарь payload_on/payload_off, availability_topic со словами online/offline из завещания
- Retain на state — да, после рестарта HA карточки живые; retain на command — нет: ретейнутая команда выполнится при переподключении платы
- Порядок подключения нового устройства: mosquitto_sub -v увидеть поток, дёрнуть команду из консоли, и только потом YAML и интерфейс
- 1. Карточка термометра в HA вечно показывает «неизвестно», mosquitto_sub на адрес платы видит поток данных. Первая проверка?
- 2. Что обязана сделать плата после выполнения команды из command_topic?
- 3. Почему command_topic не помечают retain?
- 4. home/dacha/Greenhouse/temp и home/dacha/greenhouse/temp — это…
- 5. Карточка показывает «недоступно» при живой и публикующей плате. Какое поле конфига проверяете?
Частые вопросы по теме урока
- Карточка термометра в HA вечно показывает «неизвестно», mosquitto_sub на адрес платы видит поток данных. Первая проверка?
- Поток есть, а HA его не видит — классический признак двух брокеров. Интеграция и устройство обязаны смотреть в один адрес и порт. Куда смотрит интеграция MQTT: тот ли это брокер, куда публикует плата
- Что обязана сделать плата после выполнения команды из command_topic?
- При optimistic: false HA ждёт подтверждения из state_topic. Ответ в тот же command_topic — путь к эху, это разные адреса. Опубликовать факт в state_topic, чтобы карточка обновилась
- Почему command_topic не помечают retain?
- Брокер отдаёт retained-сообщение каждому новому подписчику. Для команд это повтор чужого действия в непредсказуемый момент. Состояние — retain, команда — нет. Новый подписчик получит старую команду и выполнит её — например, включит насос сутки спустя
- home/dacha/Greenhouse/temp и home/dacha/greenhouse/temp — это…
- Топики чувствительны к регистру. Моё правило: всё в нижнем регистре, слова через слэш, и схема адресов до первой строки YAML. Разные топики: брокер сравнивает байты, а не смыслы
- Карточка показывает «недоступно» при живой и публикующей плате. Какое поле конфига проверяете?
- Статус живости приходит из availability_topic: слова должны совпадать с тем, что публикует плата и что публикует брокер по завещанию из LWT-урока. availability_topic и словарь payload_available/payload_not_available