# Основные принципы LightHub > Этот раздел объясняет базовые сущности, на которых построен контроллер: > **item** (канал), **itemCmd** (унифицированная команда) и **execObj** > (унифицированный объект действия). Понимание этих трёх вещей делает > предсказуемым всё остальное: конфигурацию, MQTT-топики, поведение > модулей и расширение системы новыми типами устройств. > > Раздел адресован инженерам-интеграторам и разработчикам модулей. --- ## 1. Item — виртуальный канал **Item — это канал**: единая абстракция для всего, чем LightHub управляет или что измеряет. Лампа за DMX, реле на GPIO, термостат, датчик 1-Wire, Modbus-устройство, кондиционер Haier, группа светильников, система полива — всё это items. ### 1.1 Как item описан в конфигурации Каждый item — запись в глобальном объекте `items` JSON-конфигурации. Имя записи — имя канала (оно же база MQTT-топика), значение — массив: ```json "items": { "lamp_living": [0, 5], "pump": [6, 23], "heating": [13, [[500, 100, 10800], {"item": "rheat/set"}], 21] } ``` Позиции массива фиксированы (см. `item.h`, `I_TYPE…I_TIMESTAMP`): | Индекс | Поле | Содержимое | |---|---|---| | 0 | `I_TYPE` | Тип канала `CH_*` (0–23) — выбирает драйвер | | 1 | `I_ARG` | Аргумент драйвера: пин, адрес или массив параметров | | 2 | `I_VAL` | Последнее значение (уставка) — сохраняется, переживает перезагрузку | | 3 | `I_CMD` | Последняя команда + служебные флаги | | 4 | `I_EXT` | Расширение драйвера | | 5 | `I_TIMESTAMP` | Отложенная команда (время срабатывания) | В конфигурации обычно задают только `[тип, аргумент]`; остальные поля контроллер создаёт и ведёт сам. Опционально, можно задать третий параметр масива - значение, которое будет изначально записано в уставку канала при включении (при восстановлении состояния из топика MQTT оно будет перезаписано, если в топике сохранено предыдущее значение) Также, можно определить четвертый параметр массива - команду, с которой будет инициализирован канал (по умолчанию OFF, но можно задать иное значение (в виде цифры). Также, будет перезаписано при восстановлении, если в топике сохранено предыдущее значение) Специальная возможность: к значению этого параметра можно добавить специальное побитовое значение, которое инициализирует канал сразу с одним или несколькими специальными флагами, например, в заблокированном состоянии (см команда FREEZE), в котором команды и/или значения будут игнорироваться вплоть до команды UNFREEZE, или "Запрещенном" (команда DISABLE) состоянии, выход из которых возможен по команде ENABLE Полный перечень флагов: ``` #define FLAG_DISABLED 0x100UL // 256 в десятичном виде #define FLAG_LOCKED_CMD 0x200UL // 512 в десятичном виде #define FLAG_LOCKED_SET 0x400UL // 1024 в десятичном виде #define FLAG_FREEZED 0x600UL // 1536 - игнорируем и команду и значение ``` ### 1.2 Тип канала выбирает драйвер, общее — наследуется Тип канала (`CH_RELAY`, `CH_PID`, `CH_MBUS`, …) определяет **драйвер** — модуль, который знает, как говорить с конкретной природой устройства. Но все драйверы наследуются от общего базового класса (`abstractOut`), поэтому у всех каналов есть одинаковый набор базовых свойств, работающих **одинаково для каналов разной природы**: - **Команды** `ON / OFF / TOGGLE / HALT / REST / XON / XOFF / FREEZE / UNFREEZE / ENABLE / DISABLE` — включая сквозные флаги: `HALT` гасит канал с возможностью обратного включения по `REST`; `XON/XOFF` — моментные включения; `FREEZE` блокирует изменения. - **Значения** `/set` — числовая уставка (яркость, температура, положение задвижки, скважность), `/val` — измеренное вовне значение полезное каналу. - **Отложенные команды** — `ON 5000` выполнится через 5 секунд (если направлено в суффикс /del) либо немедленно выполненная команда ON с отложенной через 5 сек противоположной командой (тут- OFF) если отправлено в суффикс /cmd - **Статусы** — контроллер публикует состояние канала в MQTT и рассылает по CAN по единой схеме. - **Группы** — item типа `CH_GROUP` просто перечисляет имена других items; команда группе рекурсивно выполняется на всех членах. Группы могут вкладываться друг в друга и пересекаться. - **Персистентность** — значение и команда канала хранятся в MQTT брокере и восстанавливаются после перезагрузки. Именно поэтому, научившись управлять диммером, вы уже умеете управлять реле, термостатом и Modbus-устройством: интерфейс один, разница — только в аргументах драйвера. ### 1.3 Item с суффиксом: `item/subitem/suffix` Канал адресуется строкой `имя_канала/суффикс`, где суффикс указывает, какое свойство канала затрагивается (`cmd`, `set`, `val`, `fan`, `mode`, `hue`, …). Ряд драйверов (MULTIVENT, HUMIDIFIER, SPRINKLER, MBUS) делит один item на **подканалы** по имени: `vents/zal3/set`, `hum/bedr1/cmd`. Имена подканалов — это ключи объекта-аргумента канала. Полный список суффиксов — в [справочнике суффиксов](suffixes_reference.md). --- ## 2. itemCmd — унифицированная команда **Всё, что приходит в канал, кодируется одной структурой — `itemCmd`.** Неважно, откуда пришло сообщение (MQTT, CAN, кнопка на входе, HTTP API, другой item) и куда оно идёт: сначала оно разбирается в `itemCmd`, и уже в этом виде передаётся каналу. Это «единая валюта» управляющего ядра. ### 2.1 Что внутри `itemCmd` упаковывает в два 32-битных слова три вещи: - **Код команды** (`CMD_*`): `ON`, `OFF`, `TOGGLE`, `HALT`, `REST`, `XON`, `XOFF`, `HEAT`, `COOL`, `AUTO`, `FAN_ONLY`, `DRY`, `HEAT_COOL`, `HIGH/MEDIUM/LOW`, `ENABLE/DISABLE`, `FREEZE/UNFREEZE`, `RGB`, `HSV`, `RESET` — текстовые имена, массив кодов 1–0x19. - **Тип значения + само значение** (`ST_*`): целое 0–255 (проценты), float (температура и пр), «десятые» целочисленное представление вещественного числа без потери точности (12.3 кодируется как 123), цвета HSV/RGB/RGBW и пр. - **Суффикс** (`S_*`): какому свойству канала адресовано — `cmd`, `set`, `val`, `fan`, `mode`, `hue`, `sat`, `temp`, `raw` и т.д. Команда и значение могут присутствовать одновременно: `HEAT` в `/cmd` кондиционера — это команда; `22.5` в `/set` — значение с суффиксом `set`. Одна и та же структура используется и в обратную сторону — статусы каналов сериализуются из `itemCmd` в текст MQTT. Такая плотная побитная упаковка позволяет уместить унифицированную команду/уставку/измерянное значение или статус канала всего в 64 бита - размер CAN сообщения, что делает возможным надежную коммуникацию между каналами в физически разных контроллерах по шине CAN, ### 2.2 Как это выглядит снаружи По MQTT сообщение — это просто payload в топике канала с суффиксом: ``` house/lh1/pump/cmd ← ON (команда) house/lh1/pump/set ← 128 (значение) house/lh1/ac/cmd ← HEAT (команда) house/lh1/ac/set ← 22.5 (значение, float) house/lh1/rgb/HSV ← 240,100,200 (цвет) house/lh1/lamp/cmd ← ON 5000 (отложенная команда, 5 сек) ``` Внутри конфигурации (EXEC-объекты, входы) команды задаются строками `"icmd": "TOGGLE"`, `"icmd": "%+4"` (инкремент текущего значения на 4) — они разбираются тем же парсером в `itemCmd`. ### 2.3 Преобразования Универсальность `itemCmd` позволяет ядру преобразовывать формулы между форматами без знания о природе устройства: - **`map`** — линейное масштабирование значения и/или подстановка текстовых команд в числа регистра (для Modbus): `"map": {"val": [400, 2000, 0, 100], "cmd": [["HEAT", 4], ["COOL", 2]]}`. - **Проценты** — 0–100 в топике без суффикса и 0–255 внутри. --- ## 3. execObj — унифицированный объект действия **execObj (EXEC-объект)** — это маленький JSON-объект, описывающий «что сделать». Он используется во многих модулях — везде, где нужно описать реакцию: маршрутизация параметров Modbus-шаблона, каскады MULTIVENT (`cas`), выходы PID-регулятора, обработчики кнопок и входов, `onFault` спринклера, `onextcmd` центральной установки. Исполнением занимается единая функция `executeCommand` (utils.cpp): один и тот же формат работает и в конфиге, и во время исполнения. ### 3.1 Ключи execObj ```json { "item": "имя_item[/subitem][/suffix]", "emit": "MQTT-топик", "can": [адрес_контроллера, номер_item, "суффикс", subitem], "icmd": "команда/значение для item", "ecmd": "payload для emit", "map": { ...преобразование значения... } } ``` | Ключ | Назначение | |---|---| | `item` | Вызвать локальный канал: строка `имя/суффикс` (например `pump/cmd`, `ac/set`). Если `icmd` не задан — передаётся само значение, с которым исполнен execObj | | `emit` | Опубликовать в MQTT. Топик поддерживает подстановки `${ключ}` из секции `topics` и `${sfx}` — текущий суффикс | | `can` | Отправить команду на другой контроллер LightHub по CAN-шине: `[id_контроллера, id_item, суффикс]`, четвёртый элемент — номер подканала. На приёме адресуется в item целевого контроллера | | `icmd` | Текст команды для `item` (`ON`, `TOGGLE`, `%+4`, число) | | `ecmd` | Текст payload для `emit`; без него публикуется значение | | `map` | Преобразование значения перед доставкой (см. [преобразования map](analog_inputs_and_map.md)) | Входы (секция `in`) дополнительно понимают пары `icmd`/`irev`, `ecmd`/ `erev` — чередующиеся команды для повторных срабатываний (например, одна кнопка увеличивает яркость, повторное нажатие уменьшает). ### 3.2 Примеры Значение регистратора Modbus → в PID и в MQTT: ```json "co2": { "emit": "house/snsr/co2living/val", "item": "co2living_pid/val", "@S": null } ``` Команда кнопки → включить локальный насос и отправить по CAN на контроллер 15, item 431: ```json "click": { "item": "pump/cmd", "icmd": "TOGGLE" }, "dclick": { "can": [15, 431, "cmd"], "icmd": "TOGGLE" } ``` Выход PID → уставка реле и каскад уставки на панель: ```json "tliving": [13, [500, 100, 10800, 50, 200, 200, 30], [ { "item": "rliving/set" }, { "item": "p_living/settemp" } ], 22] ``` execObj может быть **массивом** объектов — выполнятся все по очереди (как в примере выше: два адресата одного значения). Строка вместо объекта — тоже execObj (легаси-форма): она трактуется как `emit`. ### 3.3 Служебные пометки `@S` / `@V` В объектах маршрутизации параметров Modbus (и аналогичных) используются служебные ключи-пометки: - `"@S": 0` — не публиковать статус повторно, если значение не изменилось; - `"@S": null` — публикация при каждом опросе (для датчиков); - `"@V"` — запоминание последнего отправленного значения (защита от «эха»: значение, записанное нами самими, не обрабатывается как изменение от устройства). --- ## 4. Как всё сходится ``` MQTT / CAN / кнопка / HTTP │ (текст: "ON", "22.5", "HEAT") ▼ ┌─────────┐ │ itemCmd │ унифицированная команда/значение └────┬────┘ │ адресация: item/subitem/suffix ▼ ┌───────────────────┐ │ Item (канал) │ общие свойства: команды, значения, │ + драйвер CH_* │ статусы, группы, персистентность └─────────┬─────────┘ │ (реакция модуля) ▼ ┌─────────┐ │ execObj │ куда доставить результат: item / emit / can └─────────┘ ``` - **Вход** любой природы превращается в `itemCmd`. - **`itemCmd` адресуется в item** (канал) по имени и суффиксу. - **Драйвер канала** выполняет команду и/или вычисляет результат. - **Результат уходит наружу** через `execObj`: в другой item, в MQTT или на другой контроллер по CAN. Смежные разделы: [справочник суффиксов](suffixes_reference.md), [типы каналов](channel_types_reference.md), [техническая таблица типов](technical_channel_types_table.md), [работа с MQTT](mqtt_api_reference.md), [примеры конфигураций](../../config-examples/README.md).