Меню как протокол: menu.yaml ↔ mqtt_contract.yaml ↔ портал¶
Три файла — три роли¶
| Файл | Владелец | Что описывает |
|---|---|---|
src/menu/menu.yaml |
ваш продукт | меню устройства: параметры, действия, структура |
contracts/mqtt_contract.yaml |
idryer-core | список известных смыслов: canonical_roles с подписями на нескольких языках |
frontend-v2/src/contracts/mqtt-api.types.ts |
генерируется | TypeScript-типы для портала |
role: — семантическое имя пункта меню. Прошивка говорит «у меня есть iheater.heat_temp», а не «у меня пункт номер 35». Внутренние имена прошивки могут меняться, role: остаётся постоянным.
Меню — зеркало настроек прибора. Прошивка публикует все пункты menu.yaml — с role: и без. Портал рисует каждый пункт по его типу: значение, переключатель, действие, подменю. role: даёт пункту подпись из контракта на языке пользователя; без role: портал показывает название, которое прислало устройство.
Карточку устройства меню не строит. Что показывает карточка и какие операции запускает, описывает card-манифест — см. Карточка устройства: card-манифест. Действие карточки может взять пределы и значение по умолчанию своего параметра из пункта меню.
1. Сборка прошивки (pio run)¶
menu.yaml → menu_gen.py сверяет каждый role: с canonical_roles в контракте → если роль неизвестна, сборка падает с ошибкой и списком допустимых ролей → генератор пишет C++-файлы в src/menu/.
2. Обновление TypeScript для портала (regen.sh)¶
mqtt_contract.yaml → gen_ts_types.py генерирует mqtt-api.types.ts и подписи ролей roles.{lang}.json → файлы копируются в портал.
Запускается при изменении контракта. Результат коммитится.
3. Runtime: устройство ↔ портал¶
Прошивка публикует меню в топик config (это делает код продукта по команде get_config; продукты iDryer — ещё и при выходе в онлайн) → бэкенд портала сохраняет его → портал получает меню через GET /devices/:id/menu-config → каждый пункт рисуется по типу t (val, tog, act, sub), подпись — canonical_roles[r].labels[lang], затем английская, затем название n от устройства.
Параметры (min, max, val) приходят из самого пункта меню — текущие значения знает прошивка.
Портал меняет значение командой commands/set { "id": <id>, "val": <value> }.
Как добавить настройку (параметр в NVS)¶
- id: my_param
type: value
role: my.param # необязательно: подпись из контракта
title: { ru: "ПАРАМЕТР", en: "PARAM" }
unit: { ru: "°C", en: "°C" }
vtype: uint16
min: 0
max: 100
step: 1
bind: my_param # ключ NVS (≤ 15 символов)
persist: true
scope: global
default: 50
bind = ключ NVS. persist: true = значение переживает перезагрузку.
role: — не произвольное поле: значение должно быть из canonical_roles в контракте, иначе сборка упадёт. Список — в contracts/mqtt_contract.yaml → canonical_roles или в menu.template.yaml. Новая роль сначала добавляется в контракт, затем regen.sh.
Как добавить операцию на карточку устройства¶
Операции (запуск, остановка, нагрев, подсветка) объявляются действиями карточки, а не пунктами меню. Пределы можно взять из пункта меню:
idryer::card_menu::attach(s_link.card());
s_link.card().action("storage", "STORAGE", onStorage)
.param("temperature", "target_temperature", MENU_TARGET_TEMP);
s_link.card().action("stop", "IDLE", onStop);
Подробно — Карточка устройства: card-манифест.
Чего НЕ делать¶
- Не добавлять
widget:вmenu.yaml. Полеwidgetвcanonical_rolesсправочное: портал и приложение его не читают. - Не править
mqtt-api.types.tsруками — его генерируетregen.sh. - Не трогать флаги
Config.hasXxxради новых действий — они только для телеметрии (сенсоры, состояния).