ГлавнаяАкадемияОсновы умного дома → Сценарии для гостиниц: Статусы DND/MUR

Сценарии для гостиниц: Статусы DND/MUR

Урок 9 · Основы умного дома · 30 мин · theory

Введение в логику DND/MUR: концепция и аппаратная часть

В современной гостиничной индустрии комфорт и приватность гостя являются ключевыми факторами успеха. Сценарии DND/MUR (Do Not Disturb / Make Up Room) — это базовый, но критически важный элемент системы автоматизации номера, позволяющий гостю легко и невербально коммуницировать свои потребности персоналу отеля.

> 📋 Ключевые понятия:

> * DND (Do Not Disturb): Статус, запрещающий персоналу входить в номер и беспокоить гостя.

> * MUR (Make Up Room): Статус-запрос на уборку номера.

> * Приоритет DND: Логическое правило, согласно которому активация статуса DND автоматически отменяет статус MUR. Приватность гостя всегда в приоритете.

Типовые аппаратные компоненты

Для реализации этого функционала требуется минимальный набор специализированного оборудования, работающего в связке:

  • Прикроватная панель управления: Это основной интерфейс для гостя. Представляет собой настенную панель, обычно у изголовья кровати, с двумя кнопками (часто сенсорными) и светодиодной подсветкой для индикации текущего статуса. При нажатии на кнопку панель отправляет сигнал в систему автоматизации.
  • Коридорный индикатор: Устройство, устанавливаемое снаружи номера, рядом с дверью. Оно дублирует статусы DND и MUR с помощью ярких и интуитивно понятных иконок (например, красный колокольчик для DND, зеленая пиктограмма горничной для MUR). Некоторые модели также включают в себя кнопку дверного звонка.
  • Центральный контроллер: «Мозг» системы. В нашем случае это контроллер HI на базе Linux. Он принимает сигналы от кнопочных панелей, обрабатывает логику и отправляет команды на коридорные индикаторы и в централизованную систему управления отелем (PMS).
  • Обзор протоколов для реализации

    Связь между панелями, индикаторами и контроллером может быть организована по разным протоколам. Наиболее распространены в гостиничном сегменте:

    Логическая цепочка: от нажатия до отображения

    Весь процесс от действия гостя до реакции системы можно представить в виде последовательной цепочки событий:

  • Действие гостя: Гость нажимает кнопку "DND" на прикроватной панели.
  • Сигнал от панели: Панель фиксирует нажатие и изменяет состояние своего внутреннего Modbus-регистра (например, `Discrete Input`).
  • Опрос контроллером: Контроллер HI через драйвер `wb-mqtt-serial` периодически опрашивает панель по шине RS-485 и обнаруживает изменение в регистре.
  • Публикация в MQTT: Драйвер преобразует Modbus-событие в сообщение и публикует его в соответствующий MQTT-топик, например, `/devices/room_101_panel/controls/dnd_button`.
  • Обработка в Node-RED: Поток в Node-RED, подписанный на этот топик, получает сообщение.
  • Применение бизнес-логики: Узел `Function` обрабатывает событие: сохраняет новый статус (DND: `true`) в персистентном контексте, отключает MUR (если был активен) и формирует команды для исполнительных устройств.
  • Отправка команд: Node-RED публикует команды в MQTT-топики управления:
  • * `/devices/room_101_corridor_indicator/controls/dnd_led/on` со значением `1`.

    * `/devices/room_101_corridor_indicator/controls/mur_led/on` со значением `0`.

    * `hi/hotel/floor1/room101/status/dnd` со значением `true` для центральной системы мониторинга (PMS/iRidium).

  • Реакция устройств: Коридорный индикатор, опрошенный тем же `wb-mqtt-serial`, получает команду через Modbus и зажигает красный светодиод DND. Панель персонала в iRidium обновляет иконку статуса для номера 101.
  • Эта, на первый взгляд, сложная цепочка обеспечивает невероятную гибкость, масштабируемость и простоту диагностики, поскольку каждый этап можно отследить через стандартные инструменты, такие как MQTT-клиент.

    ---

    Реализация на Modbus: Wirenboard и кнопочные панели

    Практическая реализация DND/MUR на нашей платформе HI (аналогичной Wirenboard) начинается с физического подключения и настройки программного моста между шиной Modbus RTU и брокером MQTT. Эту роль выполняет служба `wb-mqtt-serial`.

    Подключение панелей по RS-485

  • Физическое подключение: Все кнопочные панели и коридорные индикаторы для одного или нескольких номеров подключаются параллельно к одной шине RS-485. Используйте кабель "витая пара", соблюдая полярность (клемма `A` -> `A`, `B` -> `B`) и стандарт цветового кодирования, как описано в уроке по схемам подключения.
  • Адресация: Каждому устройству на шине (панели, индикатору) необходимо присвоить уникальный Modbus Slave ID (адрес от 1 до 247). Обычно это делается с помощью DIP-переключателей на корпусе устройства. Задокументируйте присвоенные адреса. Например:
  • * Номер 101, прикроватная панель: ID 21.

    * Номер 101, коридорный индикатор: ID 22.

    > 💡 Подсказка: Используйте утилиту `modbus_client` в консоли контроллера для быстрой проверки связи с устройством и отладки карты регистров без участия `wb-mqtt-serial`. Это позволяет убедиться, что физическое подключение и параметры шины верны.

    >

    >

    > # Прочитать состояние 1-го Discrete Input (адрес 0) с устройства с ID 21

    > modbus_client --debug -m rtu -p /dev/ttyRS485-1 -b 9600 -s 1 -a 21 -t 0x02 -r 0 1

    >

    Настройка wb-mqtt-serial

    Конфигурация драйвера `wb-mqtt-serial` происходит в текстовом файле `/etc/wb-mqtt-serial.conf`. Здесь мы описываем наши устройства, их Modbus-адреса и карту регистров, чтобы драйвер знал, что опрашивать и как это публиковать в MQTT.

    Ниже представлен пример конфигурации для связки "прикроватная панель + коридорный индикатор" для номера 101.

    Пример `wb-mqtt-serial.conf`:
    {
    

    "ports": [

    {

    "path": "/dev/ttyRS485-1",

    "baud_rate": 9600,

    "parity": "N",

    "data_bits": 8,

    "stop_bits": 1,

    "enabled": true,

    "devices": [

    {

    "name": "Room_101_Panel",

    "id": "room_101_panel",

    "slave_id": "21",

    "device_type": "DND_MUR_Panel",

    "enabled": true,

    "channels": [

    {

    "name": "DND Button",

    "reg_type": "discrete",

    "address": 0,

    "type": "switch",

    "readonly": true

    },

    {

    "name": "MUR Button",

    "reg_type": "discrete",

    "address": 1,

    "type": "switch",

    "readonly": true

    },

    {

    "name": "DND LED Feedback",

    "reg_type": "coil",

    "address": 0,

    "type": "switch"

    },

    {

    "name": "MUR LED Feedback",

    "reg_type": "coil",

    "address": 1,

    "type": "switch"

    }

    ]

    },

    {

    "name": "Room_101_Corridor_Indicator",

    "id": "room_101_corridor_indicator",

    "slave_id": "22",

    "device_type": "Corridor_Display",

    "enabled": true,

    "channels": [

    {

    "name": "DND LED",

    "reg_type": "coil",

    "address": 0,

    "type": "switch"

    },

    {

    "name": "MUR LED",

    "reg_type": "coil",

    "address": 1,

    "type": "switch"

    },

    {

    "name": "Doorbell Button",

    "reg_type": "discrete",

    "address": 0,

    "type": "switch",

    "readonly": true

    }

    ]

    }

    ]

    }

    ]

    }

    Структура MQTT-топиков

    После сохранения конфигурации и перезапуска службы `systemctl restart wb-mqtt-serial` драйвер начнет опрос устройств и создаст в MQTT-брокере следующую структуру топиков:

    Для чтения состояния кнопок (входящие в Node-RED): Для управления индикаторами (исходящие из Node-RED):

    Имея эту четкую и предсказуемую структуру топиков, мы можем перейти к созданию логики в Node-RED.

    ---

    Обработка статусов в Node-RED: базовая логика и состояния

    Теперь, когда аппаратная часть настроена и данные с кнопок поступают в MQTT, мы можем реализовать саму логику управления статусами DND/MUR в Node-RED. Главная задача — создать надежный конечный автомат (FSM), который будет хранить состояние номера и корректно реагировать на действия гостя.

    > 🔗 Связанный материал: Как мы рассмотрели в уроке `COURSE-01-M05-L09: Логика ключ-карты`, статусы DND/MUR должны сбрасываться при выезде гостя, что определяется по отсутствию ключ-карты. Мы интегрируем этот сигнал в наш поток.

    Создание потока в Node-RED

    Поток будет состоять из нескольких ключевых узлов:

  • `mqtt in`: Два узла, подписанные на топики кнопок DND и MUR. Мы будем реагировать только на нажатие (сообщение `1`).
  • `function`: Один главный узел, который будет содержать всю логику.
  • `mqtt out`: Несколько узлов для отправки команд на светодиоды и публикации обобщенного статуса.
  • `link in` / `link out`: Для связи с другими потоками, например, с потоком логики ключ-карты.
  • ASCII-схема потока:
    // Входящие сигналы
    

    [mqtt in: DND button] --(msg.payload == '1')--> [function: DND/MUR Logic] --+

    [mqtt in: MUR button] --(msg.payload == '1')--> [function: DND/MUR Logic] |

    [link in: Guest Status] ----------------------> [function: DND/MUR Logic] |

    |

    // Исходящие команды |

    +--> [switch: route by topic] --+-- [mqtt out: Corridor DND LED]

    |

    +-- [mqtt out: Corridor MUR LED]

    |

    +-- [mqtt out: Bedside DND LED]

    |

    +-- [mqtt out: Bedside MUR LED]

    |

    +-- [mqtt out: Publish Global Status]

    Использование персистентного контекста

    Состояние номера (DND, MUR или IDLE) должно сохраняться между перезагрузками контроллера. Для этого мы будем использовать контекст потока (flow context) с настроенным файловым хранилищем.

    В файле `settings.js` вашего Node-RED убедитесь, что `contextStorage` настроен на использование файловой системы:

    contextStorage: {
    

    default: { module: "memory" },

    file: { module: "localfilesystem" }

    },

    Это позволит нам использовать `flow.set('status', newStatus, 'file')` для надежного сохранения данных.

    Реализация логики в узле Function

    Основная магия происходит в центральном узле `Function`. Он принимает сообщения от кнопок и от системы статуса присутствия, обновляет состояние и генерирует команды.

    // Инициализация. Выполняется один раз при старте потока.
    

    // Получаем текущий статус из персистентного хранилища или устанавливаем 'IDLE'.

    let status = flow.get('room_status', 'file') || 'IDLE';

    // Входящее сообщение msg содержит topic, по которому мы определяем источник

    let topic = msg.topic;

    // 1. ОБРАБОТКА ВХОДЯЩИХ СИГНАЛОВ

    if (topic.includes('dnd_button')) {

    // Нажата кнопка DND

    if (status === 'DND') {

    // Повторное нажатие - отключаем DND

    status = 'IDLE';

    } else {

    // Включаем DND (приоритет над MUR)

    status = 'DND';

    }

    } else if (topic.includes('mur_button')) {

    // Нажата кнопка MUR

    if (status === 'MUR') {

    // Повторное нажатие - отключаем MUR

    status = 'IDLE';

    } else if (status !== 'DND') {

    // Включаем MUR, если не активен DND

    status = 'MUR';

    }

    } else if (topic.includes('guest_status')) {

    // Получили статус от логики ключ-карты

    // msg.payload должен быть "CHECK_OUT"

    if (msg.payload === 'CHECK_OUT') {

    status = 'IDLE'; // Сбрасываем все статусы при выезде

    }

    }

    // Сохраняем новое состояние в персистентный контекст

    flow.set('room_status', status, 'file');

    // Обновляем визуальный статус узла для отладки

    node.status({ fill: "blue", shape: "dot", text: "Status: " + status });

    // 2. ФОРМИРОВАНИЕ ИСХОДЯЩИХ КОМАНД

    // Создаем массив сообщений для отправки в узел switch

    // Это позволяет одним узлом function управлять множеством выходов

    let commands = [];

    // Команда для коридорного индикатора DND

    commands.push({

    topic: '/devices/room_101_corridor_indicator/controls/dnd_led/on',

    payload: (status === 'DND') ? 1 : 0

    });

    // Команда для коридорного индикатора MUR

    commands.push({

    topic: '/devices/room_101_corridor_indicator/controls/mur_led/on',

    payload: (status === 'MUR') ? 1 : 0

    });

    // Команды для подсветки кнопок на прикроватной панели

    commands.push({

    topic: '/devices/room_101_panel/controls/dnd_led_feedback/on',

    payload: (status === 'DND') ? 1 : 0

    });

    commands.push({

    topic: '/devices/room_101_panel/controls/mur_led_feedback/on',

    payload: (status === 'MUR') ? 1 : 0

    });

    // Публикация обобщенного статуса для PMS/iRidium

    commands.push({

    topic: 'hi/hotel/floor1/room101/status/service',

    payload: JSON.stringify({

    "status": status, // "DND", "MUR", "IDLE"

    "ts": Date.now()

    })

    });

    // Возвращаем массив сообщений

    return [commands];

    Этот код реализует полный цикл: получает событие, обновляет состояние, сохраняет его и рассылает команды всем связанным устройствам и системам. Использование массива сообщений на выходе позволяет элегантно управлять всеми выходами из одного места.

    ---

    Расширенная логика: Учет статуса присутствия и интеграция с PMS

    Базовая логика DND/MUR — это только первый шаг. Для создания действительно интеллектуальной и удобной системы для гостя и персонала, эти статусы необходимо интегрировать с другими подсистемами номера, в первую очередь со статусом присутствия и центральной системой управления отелем (PMS).

    > ⚠️ Внимание: Передача и хранение информации о статусах DND/MUR затрагивает приватность гостя. Убедитесь, что ваше решение соответствует локальным законам о защите персональных данных, таким как GDPR. Например, данные о DND не должны храниться дольше, чем это необходимо, и доступ к ним должен быть строго регламентирован.

    Взаимосвязь со статусом присутствия

    Как мы знаем из урока `COURSE-01-M05-L09`, статус присутствия гостя определяется наличием ключ-карты в карточном приемнике. Это дает нам два основных состояния: «Гость в номере» (CHECK_IN) и «Гость покинул номер» (GUEST_OUT).

    Интеграция с этими состояниями позволяет реализовать умные правила:

  • Автоматический сброс MUR: Когда гость покидает номер (извлекает карту), статус "Убрать номер" может быть автоматически сброшен. Логика проста: если гость вернулся, а номер еще не убран, он может нажать кнопку MUR снова. Это предотвращает ситуации, когда бригада уборки приходит в номер, в который только что вернулся гость.
  • Блокировка дверного звонка: Это критически важная функция для статуса DND. Если статус DND активен, сигнал от кнопки дверного звонка (полученный через Modbus или дискретный вход) должен быть проигнорирован. В Node-RED это реализуется просто: поток, обрабатывающий звонок, должен сначала проверить `flow.get('room_status')`. Если статус равен 'DND', поток просто завершается (`return null;`), и звук звонка не активируется.
  • Умное управление DND при выходе гостя: Должен ли статус DND сбрасываться, когда гость уходит? Здесь нет единого мнения.
  • * Подход 1 (сбрасывать): Гость ушел — приватность не нужна. Просто и понятно.

    * Подход 2 (не сбрасывать): Гость мог выйти ненадолго и не хочет, чтобы в его отсутствие входили в номер (например, для пополнения мини-бара). Этот подход более ориентирован на гостя. При возвращении (вставке карты) статус DND продолжает действовать. Полный сброс происходит только при полном выселении (CHECK_OUT). Выбор подхода зависит от политики отеля.

    Интеграция с PMS (Property Management System)

    PMS — это сердце IT-инфраструктуры отеля. Она управляет бронированиями, счетами, регистрацией гостей и работой персонала. Интеграция нашей системы автоматизации с PMS выводит управление на новый уровень, позволяя:

    Протоколы для обмена с PMS

    Интеграция с PМS — сложная задача, так как каждая система имеет свой API или протокол. Наиболее известным является FIAS (Fidelio Interface Application Specification), разработанный компанией Micros-Fidelio (сейчас Oracle Hospitality).

    * Запрос на смену статуса номера: `LS|RN101|SAD|...` (Room Status Announcement)

    * Запрос от PMS на Check-in: `GI|RN101|...` (Guest In)

    Для реализации такой интеграции в Node-RED обычно используют узлы `tcp request` или пишут кастомный код в `function` для поддержания постоянного соединения и парсинга сообщений. Часто для этого требуется специализированный шлюз или коннектор, предоставляемый производителем PMS.

    ---

    Визуализация и управление в iRidium pro

    Для эффективной работы персонала, особенно службы хаускипинга, необходим наглядный и удобный инструмент для мониторинга статусов всех номеров. Платформа iRidium pro отлично подходит для создания таких панелей управления, которые могут работать на планшетах, стационарных ПК или специализированных панелях.

    Создание интерфейса для персонала в Iridium Studio

    В среде разработки Iridium Studio создается графический интерфейс. Для нашего случая это может быть план этажа или просто список номеров. Для каждого номера на экране размещаются иконки, отображающие статусы DND и MUR.

    * Иконка "DND" (например, красный колокольчик)

    * Иконка "MUR" (например, зеленая щетка)

    * Текстовое поле для отображения статуса гостя (Заселен / Свободен)

    Подписка сервера iRidium на MQTT-топики

    Сервер iRidium выступает в роли MQTT-клиента. В настройках проекта необходимо настроить подключение к нашему MQTT-брокеру и создать каналы (Channels) и теги (Tags), которые будут соответствовать MQTT-топикам.

    Мы будем использовать обобщенный топик состояния, который наш поток Node-RED уже публикует: `hi/hotel/floor1/room101/status/service`.

  • Настройка драйвера: В Iridium Studio добавляется драйвер "MQTT Client". В его настройках указывается IP-адрес брокера и порт (1883).
  • Создание каналов и тегов:
  • * Channel: `Room_101_Service_Status_Topic`

    * Topic: `hi/hotel/floor1/room101/status/service`

    * Type: `Subscribe`

    * Tag (Feedback): `Room_101_Service_Status`

    * Channel: `Room_101_Service_Status_Topic`

    Теперь тег `Room_101_Service_Status` на сервере iRidium будет содержать JSON-строку, которую отправляет Node-RED: `{"status": "DND", "ts": 1678886400000}`.

    Привязка графических элементов к тегам

    Ключевой этап — это "оживление" интерфейса.

  • Выбираем иконку DND для номера 101.
  • В свойствах элемента, в разделе `States`, настраиваем его видимость.
  • Создаем привязку (Relation):
  • * Event: `In Value Changed` (При изменении значения тега)

    * Driver: `Server Tags`

    * Feedback: `Room_101_Service_Status`

    * Action: `Set Visibility` (Установить видимость)

  • Условие: В поле `Value` мы пишем простое JS-условие. Так как тег содержит JSON, нам нужно его распарсить.
  •     // Сделать иконку видимой, если в JSON поле status равно 'DND'

    return JSON.parse(value).status === 'DND';

    Аналогичная логика настраивается для иконки MUR (условие `JSON.parse(value).status === 'MUR'`). Таким образом, интерфейс будет автоматически обновляться в реальном времени при изменении статуса номера.

    Пример скрипта на iRidium Script (JS)

    Для более сложной логики, например, подсчета количества номеров, требующих уборки, можно использовать скрипты iRidium.

    Этот скрипт может запускаться по таймеру или при изменении любого статуса номера.

    // iRidium Script для подсчета номеров с MUR
    

    // Предполагается, что у нас есть теги вида Room_101_Service_Status, Room_102_Service_Status и т.д.

    function countRoomsToClean() {

    var roomCount = 10; // Общее количество номеров на этаже

    var cleanRequestCount = 0;

    for (var i = 1; i <= roomCount; i++) {

    var roomNumber = 100 + i;

    var tagName = 'Room_' + roomNumber + '_Service_Status';

    var tagValue = IR.GetVariable('Server.Tags.' + tagName);

    if (tagValue) {

    try {

    var statusData = JSON.parse(tagValue);

    if (statusData.status === 'MUR') {

    cleanRequestCount++;

    }

    } catch (e) {

    IR.Log('Error parsing status for room ' + roomNumber);

    }

    }

    }

    // Обновляем тег, к которому привязан виджет-счетчик на панели

    IR.SetVariable('Server.Tags.Rooms_To_Clean_Count', cleanRequestCount);

    }

    // Запускаем функцию при старте и подписываемся на изменения

    IR.AddListener(IR.EVENT_START, 0, countRoomsToClean);

    // Также нужно настроить вызов этой функции при изменении любого из тегов статусов

    ---

    Итоги и лучшие практики

    В этом уроке мы детально разобрали весь жизненный цикл одного из самых важных сценариев гостиничной автоматизации — управления статусами DND (Не беспокоить) и MUR (Убрать номер). Мы прошли путь от физического подключения оборудования до создания интеллектуальной логики и ее визуализации для персонала.

    Краткий обзор всей цепочки реализации

  • Оборудование: Прикроватная панель и коридорный индикатор подключаются к контроллеру HI по шине RS-485/Modbus.
  • Драйвер: Служба `wb-mqtt-serial` опрашивает Modbus-устройства и транслирует их состояния в MQTT-топики.
  • Логика: Поток в Node-RED подписывается на MQTT-сообщения от кнопок, реализует конечный автомат (FSM) для управления состояниями (IDLE, DND, MUR) и сохраняет текущий статус в персистентном контексте.
  • Управление: Node-RED отправляет команды через MQTT обратно в `wb-mqtt-serial` для управления светодиодными индикаторами на панелях.
  • Интеграция: Node-RED публикует обобщенные статусы в отдельный, структурированный MQTT-топик для верхнеуровневых систем.
  • Визуализация: Панель управления, созданная в iRidium pro, подписывается на эти статусы и отображает их в реальном времени, предоставляя персоналу удобный инструмент для работы.
  • Важность отказоустойчивости

    Статус, установленный гостем, не должен теряться. Ключевым элементом надежности является использование персистентного контекста, настроенного на хранение в файловой системе (`localfilesystem`). Это гарантирует, что даже после перезагрузки контроллера из-за сбоя питания или обновления ПО, все статусы номеров будут восстановлены, и система продолжит работать корректно. Обязательно используйте узлы `Catch` для перехвата ошибок (например, отказ Modbus-устройства) и их логирования.

    Рекомендации по проектированию

    * Пример: `hi/hotel/floor3/room312/service/status`.

    * Это упрощает подписки, управление правами доступа и интеграцию со сторонними системами.

    Альтернативные подходы

    Стоит отметить, что существуют и другие способы реализации. Например, в системах на базе KNX подобная логика часто программируется непосредственно на уровне самих устройств. Кнопка и исполнительное устройство (реле, диммер) могут быть связаны групповым адресом и работать автономно. Центральный контроллер в такой архитектуре выполняет скорее функции мониторинга и шлюза в IP-сеть, а не основной логической обработки. Этот подход более отказоустойчив на уровне отдельного номера, но значительно дороже и требует специализированных навыков для программирования.

    Выбор между централизованной логикой (как в нашем случае с Node-RED) и децентрализованной (KNX) зависит от бюджета проекта, требований к отказоустойчивости и квалификации инженеров.

    Что дальше

    В следующем уроке мы рассмотрим еще один важный аспект гостиничной автоматизации — управление доступом, но уже не на уровне номера, а для служебных помещений, и его интеграцию с расписанием работы персонала.