Проектирование графа состояний: Home, Away, Night
Введение в конечные автоматы и их роль в умном доме
ведение в конечные автоматы и их роль в умном доме
Чтобы превратить набор разрозненных устройств в единую, интеллектуальную систему, необходима модель, описывающая ее поведение. Если строить автоматизации только по принципу «если произошло событие X, сделай Y» (If-This-Then-That), при росте числа устройств логика быстро превращается в неконтролируемый хаос. Одной из самых мощных и в то же время простых для понимания моделей, решающих эту проблему, является конечный автомат, или Finite State Machine (FSM).
> 📋 Ключевые понятия:
> * Конечный автомат (FSM) — это математическая модель вычислений, абстрактный автомат, который может находиться в одном из конечного числа состояний.
> * Автомат может переходить из одного состояния в другое в ответ на внешние события.
> * Процесс изменения текущего состояния называется переходом.
Простыми словами, представьте себе обычный турникет в метро. У него есть два состояния: «Закрыто» и «Открыто».
- Состояния: `Закрыто`, `Открыто`.
- События: `Опустили жетон/Приложили карту`, `Человек прошел`, `Таймер истек`.
- Переходы:
* Из `Открыто` в `Закрыто` по событию `Человек прошел` или `Таймер истек`.
Эта простая логика идеально ложится на управление умным домом. Ваш дом в любой момент времени находится в определенном, строго заданном состоянии: «Жильцы дома», «Никого нет», «Ночь», «Гости» или «Отпуск». Каждое состояние действует как глобальный фильтр, определяющий, как именно должны вести себя подсистемы: климат-контроль, освещение, безопасность, мультимедиа.
Граф состояний — это визуальное представление конечного автомата, где состояния изображаются в виде кругов (или узлов), а переходы — в виде направленных стрелок между ними. Подпись на стрелке указывает на событие (триггер), вызвавшее этот переход.Для базовых режимов умного дома архитектура графа выглядит следующим образом:
stateDiagram-v2
[*] --> Home
Home --> Away : Нажата кнопка "Ухожу
Определение базовых состояний: Home, Away, Night
пределение базовых состояний: Home, Away, Night
Прежде чем приступать к реализации в Node-RED, необходимо формализовать, что означает каждое состояние. Это самый важный этап проектирования, от которого зависит вся дальнейшая работа. Три базовых режима (Home, Away, Night) формируют минимальный жизнеспособный граф состояний умного дома.
> 💡 Подсказка: Прежде чем писать код в Node-RED, опишите логику каждого состояния на бумаге или в виде таблицы. Какие системы задействованы? Какие датчики являются триггерами? Какие конкретно команды уходят в шину? Это сэкономит часы отладки в будущем.
Создадим инженерную матрицу, описывающую семантику каждого из трех базовых состояний и их отображение на аппаратные подсистемы.
| Параметр / Система | `Home` (Дома) | `Away` (Нет дома) | `Night` (Ночь) |
| :--- | :--- | :--- | :--- |
| Основная цель | Комфорт и функциональность | Безопасность и энергосбережение | Отдых и безопасность |
| Климат-контроль (HVAC) | Comfort: 22-24°C. Вентиляция по датчику CO₂ (от 600 ppm). | Economy/Protection: 16°C (зима) / 28°C (лето). Вентиляция выключена. | Night/Standby: 20°C. Минимальная скорость или тихий режим (Low noise). |
| Освещение (DALI/KNX) | Автоматизация активна. Целевая яркость 100% или адаптивная по DALI датчикам. | Выключено везде (Broadcast OFF). Активация спец. сцены "Имитация присутствия". | Ночная сцена (Nightlight): яркость 5-10%, теплый свет (2700K), плавное включение (Fade Time > 3 сек). |
| Электрические розетки | Все контакторы замкнуты (нормальный режим). | Отключаются некритичные контакторы (утюг, медиацентр). Исключение: холодильник, серверная, роутер. | Все активны, блокировка отключения по тайм-аутам (чтобы стиральная машина могла закончить цикл). |
| Безопасность | Периметр снят с охраны. Внутренние датчики движения используются для автоматизации света. | Полная постановка на охрану (периметр + объем). | Постановка на охрану только периметра (окна, внешние двери, гараж). |
| Мультимедиа | Доступно. Зоны мультирум активны. | Отключено (команда Standby через IP/RS-232). | Отключено, ограничение максимальной громкости (Volume Cap). |
| Шторы и жалюзи | Динамическое регулирование по освещенности фасада и азимуту солнца. | Закрыты полностью (DPT 1.008: Move Down). | Закрыты полностью. |
| Уведомления | Только критичные (протечка, пожар, отказ оборудования). | Все события (движение, открытие двери, звонок в домофон) идут в Telegram/Push с фото. | Критичные + периметр. Push-уведомления без звука (Silent Mode). |
Семантика состояния 'Home' (Дома)
Это основное состояние, когда в доме есть люди. Приоритет отдается комфорту. Все инженерные системы работают в штатном режиме, чтобы обеспечить жильцам уют. Сценарии автоматизации, такие как «управление светом по движению» или «поддержание качества воздуха», полностью активны.
Для интеграции с KNX-термостатами переход в этот режим обычно отправляет значение `1` (Comfort) на объект DPT 20.102 (HVAC Mode).
Управление переопределениями (UX/Override):
Важно понимать, что нахождение в глобальном режиме `Home` не лишает пользователя ручного контроля. Если человек вручную настенным выключателем изменит яркость освещения в кабинете, локальный контроллер или логика Node-RED должна зафиксировать состояние `Manual Override` для этой конкретной зоны. Эта зона временно исключается из автоматики датчиков движения (на заданный тайм-аут, например, 2 часа), но глобальный статус системы остаётся `Home`.
Семантика состояния 'Away' (Нет дома)
Главная задача этого режима — обеспечить безопасность и максимальное энергосбережение. При переходе в `Away` система должна инициировать строго детерминированную последовательность действий (Cascade Shutdown), чтобы снизить пиковую нагрузку на шину:
Свет: Отправка DALI команды Broadcast OFF (или групповых KNX DPT 1.001 = 0 с небольшими задержками между телеграммами во избежание коллизий).
Климат: Отправка DPT 20.102 = `3` (Economy) или `4` (Building Protection). Установки температуры сдвигаются для экономии энергии.
Розетки: Снятие напряжения с розеточных групп отключением соответствующих каналов релейных актуаторов.
Шторы: Отправка команды полного закрытия, чтобы снизить теплопотери/нагрев помещения через окна.
Охрана: Передача сигнала в систему охранно-пожарной сигнализации (ОПС) для постановки на полную охрану.
⚠️ Защита от ложной активации: Переход в `Away` никогда не должен происходить просто "по потере движения на 10 минут". Нужно использовать комбинацию факторов: геофенсинг (GPS смартфонов владельцев отдалился > 500м), явное нажатие клавиши "Ушел" у входной двери (Leave Home button), или постановка ОПС на охрану со штатного пульта.
Семантика состояния 'Night' (Ночь)
Это гибридный режим, сочетающий элементы комфорта и безопасности. Жильцы находятся дома, но спят.
- Комфорт сна: Уставка климата переводится в DPT 20.102 = `2` (Standby / Night), вентмашина ограничивает максимальные обороты (например, через Modbus регистр записывается лимит скорости 30% во избежание шума в вентканалах).
- Безопасность: Активируется охрана периметра — датчики герконов на окнах. Внутренние датчики движения в жилых комнатах игнорируются ОПС.
- Ночное освещение (Nightlight Route): Если ночью кто-то встанет, пересечение датчика движения в санузле или коридоре активирует специальную DALI сцену (например, Scene 15). Вместо 100% яркости светильники включаются на 5-10% с параметром `Fade Time` = 3-5 секунд, обеспечивая сверхплавное нарастание света, которое не ослепит спросонья.
Чек-лист проектирования матрицы состояний
Перед переносом логики в платформу автоматизации, убедитесь, что матрица покрывает следующие пункты:
- [ ] Четко заданы триггеры ВХОДА в каждое состояние (как система узнает, что пора включить `Night`?).
- [ ] Четко заданы триггеры ВЫХОДА (как система возвращается в `Home` утром?).
- [ ] Прописано поведение всех инженерных систем для КАЖДОГО состояния (пустые ячейки в матрице недопустимы, "ничего не делать" — это тоже задокументированное действие).
- [ ] Определен механизм обработки ручного вмешательства (override) во время действия автоматических режимов.
План тестирования базовых состояний
Для проверки спроектированной матрицы в будущем (на этапе пусконаладки) потребуется выполнить следующий тест-план:
Из режима `Home` активировать `Away`. Проверить полное отключение света, снижение активности HVAC и щелчки контакторов отключения розеток. Убедиться в отсутствии ложных срабатываний сигнализации от остаточного тепла или сквозняков.
Имитировать возвращение: из `Away` перейти в `Home`. Проверить снятие с охраны, возврат HVAC на комфортную уставку, включение света в прихожей (Welcome-сценарий).
Переход `Home` -> `Night`. Проверить закрытие штор, снижение скорости вентиляции, блокировку аудиосистемы от случайного включения громкости.
Протестировать ночной проход по коридору в состоянии `Night` — убедиться в срабатывании DALI сцены с диммированием до ~5%.
Проектирование переходов: триггеры и события
роектирование переходов: триггеры и события
В предыдущем разделе мы зафиксировали базовые узлы нашего графа (состояния). Теперь необходимо определить правила, по которым система по нему перемещается.
Событие — это то, что заставляет конечный автомат сменить состояние. Триггер — это физическое или логическое условие, которое генерирует это событие.
Триггером может быть что угодно:
- Физическое действие: Нажатие на клавишу выключателя Gira/Jung (например, отправка значения `1` на групповой адрес `1/1/21` шины KNX).
- Сенсорное событие: Датчик движения не фиксирует активность в спальне и гостиной в течение 30 минут.
- Событие безопасности: Ввод пин-кода на панели и постановка на охрану.
- Сетевое/Геолокационное событие: Смартфон владельца покинул радиус 200 метров от дома или отключился от домашней Wi-Fi сети.
- Временное событие: Наступление события по Cron (например, на часах 23:00).
- Логическая команда: MQTT-сообщение от мобильного приложения или голосового ассистента.
Рассмотрим ключевые переходы и триггеры для базовой модели, а также нюансы их реализации.
Переход `Home` -> `Away` (Уход из дома)
Это один из самых ответственных переходов, так как он обычно влечет за собой отключение мощных потребителей, перекрытие воды и снижение температуры климата.
- Триггер 1: Явная команда. Пользователь нажимает кнопку «Уйти» возле входной двери (например, мастер-клавиша выключателя KNX, запрограммированная на отправку определенного адреса). Это самый надежный способ.
- Триггер 2: Постановка на охрану. Пользователь ставит дом на охрану с помощью кодовой панели или приложения. Система безопасности отправляет webhook или MQTT-сообщение о смене статуса.
- Триггер 3: Автоматическое определение. Более удобный, но сложный в отладке сценарий. Условия должны быть комплексными, например: (`смартфоны всех жильцов отключились от Wi-Fi`) И (`в течение 15 минут не было движения ни в одном помещении`) И (`событие открытия/закрытия входной двери происходило не более 20 минут назад`).
> ⚠️ Важно: Проверка события изменения состояния двери критична для автоматического определения ухода из дома. Если входная дверь не открывалась, отсутствие движения и потеря связи с Wi-Fi могут означать лишь то, что телефон разрядился, а жилец просто уснул на диване.
Переход `Away` -> `Home` (Возвращение домой)
При возвращении система должна восстановить комфортные параметры (включить вентиляцию, вывести теплые полы на рабочий режим, активировать базовый свет).
- Триггер 1: Снятие с охраны. Пользователь снимает дом с охраны легитимным способом. Система безопасности публикует соответствующее событие в шину. Это способ со 100% достоверностью.
- Триггер 2: Авторизованное открытие двери. Смарт-замок (например, Danalock или Aqara) на входной двери был открыт с помощью отпечатка пальца, кода или NFC-метки известного жильца.
Триггер 3: Приближение к геозоне (Pre-Home). Смартфон пересекает зону в радиусе 1 км от дома. Этого недостаточно для полноценного перевода в `Home` (открывать двери или отключать охрану по геолокации небезопасно), но это промежуточный триггер*, который может заранее разогреть бойлер или включить кондиционер до прибытия.
Переход `Home` -> `Night` (Отход ко сну)
Переход требует аккуратности. Сценарий не должен принудительно выключить свет, когда в одной из комнат кто-то еще продолжает читать или смотреть кино.
- Триггер 1: Явная команда. Нажатие кнопки «Ночь» на выключателе у кровати.
- Триггер 2: Голосовая команда. Команда «Алиса, я спать» или «Siri, good night», переданная через интеграцию в ядро контроллера.
- Триггер 3: Временной триггер. Наступление заданного времени, например, 23:00 в будние дни и 01:00 в выходные. Рекомендуется использовать как резервный вариант, а не основной.
- Триггер 4: Комбинированный. (`Время после 22:30`) И (`основной свет в гостиной и на кухне выключен`) И (`на телевизоре снизилось потребление питания - standby`) И (`датчик присутствия в спальне фиксирует статику/сон`).
Переход `Night` -> `Home` (Пробуждение)
Этот переход отвечает за возврат из ночного сберегающего режима к дневной активности: поднятие штор, включение полотенцесушителей, активация стандартных маршрутов освещения.
- Триггер 1: Срабатывание будильника. Интеграция с будильником на смартфоне жильца (например, отправка HTTP-запроса из Команд Apple или Android Tasker на webhook Node-RED контроллера HI).
- Триггер 2: Детектирование утренней активности. (`Время между 06:00 и 10:00`) И (`сработал датчик движения в коридоре по направлению на кухню или санузел`).
- Триггер 3: Явная команда. Нажатие мастер-кнопки «Доброе утро» на прикроватном выключателе.
Контракт сообщения для событий смены состояния
Чтобы все эти разнородные триггеры могли корректно управлять одним автоматом состояний, они должны отправлять данные в унифицированном формате. Определим для этого контракт сообщения. Все триггеры (Node-RED скрипты, встроенная логика контроллера HI, MQTT-мосты KNX) должны генерировать JSON-payload в единый топик `hi/system/state/request`.
json
{
"target_state": "Away",
"source": "knx_button_exit_door_1",
"reason": "Explicit user command",
"priority": 100,
"ts": 1678886400000,
"meta": {
"user_id": "user_alex"
}
}
- `target_state`: Желаемое новое состояние (`Away`, `Home`, `Night`). Это обязательное поле.
- `source`: Уникальный ID триггера, который инициировал событие. Критически важно для последующей отладки и аудита.
- `reason`: Человекочитаемое описание причины переключения (например, "Датчик движения не фиксировал активность 40 минут").
- `priority`: (Опционально) Вес события. Позволяет коне
Практическая реализация в Node-RED
рактическая реализация в Node-RED
Теперь, когда у нас есть четкая теоретическая модель графа состояний, реализуем ее в Node-RED. Нам понадобится центральный поток, который будет выполнять роль нашего программного конечного автомата.
> ⚠️ Внимание: Избегайте хранения состояния в нескольких разных местах (дублирования). Используйте единый источник истины (single source of truth), например, одну глобальную переменую с персистентным хранением, чтобы предотвратить рассинхронизацию режимов и конфликты "гонок" (race conditions).
Настройка персистентного контекста
Для хранения текущего состояния мы будем использовать контекст Node-RED. Чтобы активный режим (например, `Away`) не сбрасывался на `Home` при случайной перезагрузке контроллера или отключении питания, необходимо использовать персистентное (энергонезависимое) хранение.
Платформа HI уже поставляется с преднастроенным персистентным контекстом `file`. Под капотом в файле `settings.js` (конфигурации Node-RED) это реализуется следующим блоком:
javascript
// Пример конфигурации из settings.js
contextStorage: {
default: { module: "memory" }, // Обычный контекст (в ОЗУ)
file: { module: "localfilesystem" } // Персистентный контекст (на диске)
}
Создание State Machine Router
Создадим центральный поток, который будет слушать MQTT-топик `hi/system/state/request` (входящие команды от кнопок, Алисы, интерфейса) и обрабатывать эти запросы синхронно.
Схема потока (Flow Diagram):
text
[mqtt in: hi/system/state/request] -> [json] -> [function: State Machine] -> [mqtt out: hi/system/state]
|
+-----> [debug: State Changes]
Шаги настройки узлов:
Узел `mqtt in`: Подписывается на топик `hi/system/state/request`. Рекомендуется установить QoS = 1 (At least once), чтобы не терять запросы на смену состояний.
Узел `json`: Преобразует входящую строку JSON в объект `msg.payload`, чтобы с полями можно было работать напрямую.
Узел `function` (State Machine): Сердце нашего автомата. Здесь валидируются переходы и обновляется глобальная переменная.
Узел `mqtt out`: Публикует новое, подтвержденное состояние в утвердительный топик `hi/system/state`. Установите QoS = 1 и обязательно флаг Retain = true.
Код для узла `function: State Machine`
Скопируйте этот код в ваш узел `function`:
javascript
/*
* State Machine Router
*
* Хранит текущее состояние и обрабатывает запросы на переход.
* Публикует новое состояние для всей системы.
*/
// Получаем текущее состояние из глобального контекста ('file').
// Если оно не задано (первый запуск), устанавливаем 'Home' по умолчанию.
let currentState = global.get('system.state', 'file') || 'Home';
// Получаем запрашиваемое состояние из входящего сообщения
let requestedState = msg.payload.target_state;
// --- 1. Валидация ---
const validStates = ['Home', 'Away', 'Night'];
if (!requestedState || !validStates.includes(requestedState)) {
node.warn(`Invalid state change request received: ${JSON.stringify(msg.payload)}`);
return null; // Прекращаем обработку
}
// --- 2. Проверка на избыточность ---
if (currentState === requestedState) {
node.status({ fill: "blue", shape: "dot", text: `Already in state: ${currentState}` });
return null; // Состояние уже актуально
}
// --- 3. Жесткая логика переходов (Граф состояний) ---
// Блокируем нелогичные переходы. Например: нельзя уснуть, если никого нет дома.
if (currentState === 'Away' && requestedState === 'Night') {
node.warn(`Transition blocked: Cannot go from Away directly to Night.`);
return null;
// Опционально: здесь можно отправить уведомление пользователю об ошибке логики (UX/Override)
}
node.log(`State transition: ${currentState} -> ${requestedState}`);
// --- 4. Обновление состояния (Single Source of Truth) ---
// Сохраняем новое состояние в персистентный контекст 'file'.
global
Итоги и обзор следующих шагов
тоги и обзор следующих шагов
В этом уроке мы заложили архитектурный фундамент для управления режимами умного дома. Мы разобрали, почему конечный автомат (FSM) является идеальной моделью для такой задачи и как он помогает создавать предсказуемые, легко поддерживаемые и устойчивые к ошибкам системы.
Главные архитектурные выводы
Чек-лист проверки базового графа
Перед переходом к следующим этапам убедитесь, что ваш базовый FSM-автомат настроен корректно:
- [ ] Мониторинг шины: Подключитесь к брокеру через MQTT Explorer (или аналогичный клиент) и убедитесь, что при имитации триггеров в топик режима приходят ожидаемые значения.
- [ ] Хранение (Retain): Перезапустите ваш тестовый MQTT-клиент. Он должен немедленно получить последнее отправленное состояние режима (проверка работы флага Retain).
- [ ] Холодный старт: В логике Node-RED предусмотрен механизм инициализации (например, узел `Inject` с галочкой "inject once after startup"), который считывает последнее запомненное состояние или устанавливает безопасное состояние по умолчанию (обычно `Home`).
- [ ] Отсутствие петель: Проверьте тестовые сценарии: переходы `Home` -> `Night` -> `Home` -> `Away` происходят последовательно и не вызывают бесконечного зацикливания из-за встречных автоматизаций.
Взаимодействие с ручным управлением (Overrides)
Важно понимать, как глобальный граф состояний соотносится с ручным управлением. Переход дома в определенное состояние (например, `Night`) задает базовые уставки и выключает свет по всему дому. Однако это не означает жесткой аппаратной блокировки.
Пользователь (User Experience) должен всегда иметь возможность переопределить локальное состояние: если дом в режиме `Night`, человек все еще может включить прикроватную лампу или изменить температуру в своей спальне. Автомат лишь устанавливает "глобальный контекст", действующий в момент перехода, а дальнейшие точечные переопределения (overrides) полностью разрешены вплоть до следующей смены глобального режима.
Созданная нами базовая структура из трех состояний является прочным каркасом, на который мы будем наращивать дополнительную функциональность. Наша система стала не просто набором разрозненных скриптов, а целостным организмом, который "понимает" свой глобальный контекст.
> 💡 Что дальше: В следующем уроке мы расширим наш конечный автомат. Мы добавим более сложные состояния, такие как «Гостевой режим», который временно изменяет (ограничивает) правила работы систем и отключает некоторые детекторы присутствия, а также «Отпуск», который включает в себя многодневные сценарии имитации присутствия для повышения безопасности во время длительного отсутствия.