Меню как протокол: menu.yaml ↔ mqtt_contract.yaml ↔ портал¶
Три файла — три роли¶
| Файл | Кто владеет | Что описывает |
|---|---|---|
src/menu/menu.yaml |
твой продукт | меню устройства: параметры, действия, структура |
contracts/mqtt_contract.yaml |
idryer-core | список известных смыслов: что значит каждый role: и как портал его отображает |
frontend-v2/src/contracts/mqtt-api.types.ts |
генерируется | TypeScript-типы для портала |
role: — смысловое имя пункта меню. Прошивка говорит «у меня есть iheater.heat_start», а не «у меня есть кнопка номер 35». Это стабильный контракт между устройством и порталом — внутренние имена прошивки могут меняться, role: остаётся неизменным.
Виджет — как портал отображает этот пункт: кнопка, слайдер, переключатель, или сложный компонент (выбор цвета, редактор профиля). Определяется контрактом по role:, не прошивкой.
Пункт меню с role: виден порталу. Без role: — приватный, только на дисплее устройства.
1. Сборка прошивки (pio run)¶
menu.yaml → pre_gen_menu.py проверяет каждый role: против canonical_roles в контракте → если роль неизвестна, сборка падает с ошибкой и списком допустимых ролей → menu_gen.py генерирует C++ файлы в src/menu/
Проверка встроена в сборку — написать несуществующую роль физически невозможно.
2. Обновление TypeScript для портала (regen.sh)¶
mqtt_contract.yaml → gen_ts_types.py генерирует mqtt-api.types.ts → файл копируется в frontend-v2/src/contracts/
Запускается вручную при изменении контракта. Результат коммитится в репозиторий.
3. Runtime: устройство ↔ портал¶
Устройство подключается → публикует меню в MQTT топик config → портал читает каждый item с полем r: → смотрит CanonicalRoles[r].widget → рендерит виджет из WIDGET_REGISTRY.
Параметры (min, max, val) берутся из самого menu item — прошивка знает актуальные значения.
Как добавить новое действие на дашборд портала¶
role: — это не свободное поле. Значение должно быть из закрытого списка canonical_roles в контракте. Нельзя придумать роль на ходу — сборка упадёт с ошибкой. Список доступных ролей: contracts/mqtt_contract.yaml → секция canonical_roles, или шаблон menu.template.yaml.
1. Подобрать подходящую роль из контракта. Если подходящей нет — сначала добавить в mqtt_contract.yaml → canonical_roles и запустить regen.sh:
2. Добавить пункт в menu.yaml:
3. Обработать в прошивке (main.cpp):
pio run → валидация → C++ → прошивка публикует r: "my.action" → портал рендерит кнопку.
Как добавить настройку (NVS параметр)¶
- id: my_param
type: value
role: my.param # если нужен на портале; без role: — только на дисплее
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 = значение сохраняется после перезагрузки.
Портал меняет значение через commands/set { "id": <id>, "val": <value> }.
Что НЕ нужно делать¶
- Не добавлять
widget:вmenu.yaml— виджет определяется контрактом поrole:, не прошивкой - Не редактировать
mqtt-api.types.tsвручную — он генерируетсяregen.sh - Не трогать
Config.hasXxxфлаги для новых действий — они только для телеметрии (датчики, состояния)