Добавить виджет и поддержку нового устройства¶
Полный цикл: от форка репозитория до принятого PR. Охватывает firmware, контракт, React-виджет и тестирование на портале.
Если вам нужна только прошивка без нового виджета — см. 01-add-new-product.md.
Предварительные требования¶
- Python 3.9+ с
pip install pyyaml jsonschema - Node.js 18+
- PlatformIO CLI
- Доступ к порталу iDryer для тестирования UIKit
Шаг 1. Fork и клонирование¶
- Форкните репозиторий
idryer-coreна GitHub. -
Склонируйте форк локально:
-
Убедитесь, что контракт проходит валидацию в текущем состоянии:
Шаг 2. Редактирование контракта¶
Все изменения вносятся в contracts/mqtt_contract.yaml. Правила в одном changeset:
Warning
Не редактируйте файлы в _generated/ — они перезаписываются генераторами.
2a. Capability vocabulary (новый тип периферии)¶
Если устройство имеет новый тип железа (например, CO2-сенсор), добавьте запись в секцию capability_vocabulary:
capability_vocabulary:
co2:
description: "CO2-сенсор (ppm)"
config_flag: hasAirCo2
telemetry_field: airCo2Ppm
Это автоматически добавит поле hasAirCo2: bool в iDryer::Config при следующей регенерации.
2b. Canonical roles (новая роль + виджет)¶
Если устройство предоставляет новый элемент меню, зарегистрируйте роль в canonical_roles:
Значение widget — имя React-компонента, который вы напишете на шаге 5.
2c. Invoke actions (если виджет отправляет команды)¶
Если виджет вызывает действие на устройстве, добавьте описание в invoke_actions:
invoke_actions:
my_device:
co2.calibrate:
description: "Запустить калибровку CO2-сенсора"
args:
targetPpm:
type: uint16
description: "Референсное значение CO2 (ppm)"
required: true
2d. Device profile (новый тип устройства)¶
Добавьте профиль в device_profiles:
device_profiles:
my_device:
description: "Моё устройство"
capabilities: [led, co2]
invoke_actions: [co2.calibrate]
Значения capabilities берутся из capability_vocabulary, определённой на шаге 2a.
Шаг 3. Валидация и регенерация¶
Флаги:
| Флаг | Что делает |
|---|---|
| (без флага) | Валидация + все генераторы + копирование на портал |
--firmware-only |
Только firmware-генераторы, без копирования на портал |
--help |
Справка |
При успехе в _generated/ обновятся:
uart_protocol.h,mqtt_topics.h— C++ заголовкиiDryer_api.h— фасад Config/DeviceTypemqtt-api.types.ts— TypeScript-типыscaffolds/my_device/— заготовка PlatformIO-проекта- На портале обновятся файлы в
src/components/widgets/
Если regen.sh завершается с ошибкой, исправьте проблему до продолжения.
Шаг 4. Реализация прошивки¶
Воспользуйтесь сгенерированным scaffold-проектом:
Заполните TODO-секции в src/main.cpp:
onOnline()— загрузка конфига из NVS, инициализация железа.loop()— опрос сенсоров, вызовs_runtime.publishTelemetry(tel).buildInfoJson()— уже заполнен генератором по capabilities.onInvoke()— реакция наco2.calibrate.
Подробнее — 01-add-new-product.md.
Шаг 5. Создание React-виджета¶
Виджеты живут в contracts/widgets/ и копируются на портал через regen.sh.
Note
Не редактируйте виджеты напрямую в portal/src/components/widgets/ — при следующем запуске regen.sh изменения будут перезаписаны. Редактируйте только в contracts/widgets/.
Создать файл виджета¶
// contracts/widgets/Co2Display.tsx
import type { WidgetProps } from "./widget-props";
export function Co2DisplayWidget({ device }: WidgetProps) {
const unit = device.units[0];
const co2 = unit?.co2Ppm ?? null;
return (
<div style={{ padding: "8px 16px" }}>
{co2 !== null ? `${co2} ppm` : "—"}
</div>
);
}
Зарегистрировать в index.ts¶
Зарегистрировать в widget-registry.tsx (на портале)¶
После следующего regen.sh файл появится в portal/src/components/widgets/Co2Display.tsx. Добавьте запись в widget-registry.tsx вручную:
import { Co2DisplayWidget } from "./Co2Display";
export const WIDGET_REGISTRY: Record<WidgetName, React.ComponentType<WidgetProps>> = {
// ...
Co2Display: Co2DisplayWidget,
};
Шаг 6. Тестирование в UIKit¶
Откройте portal/src/pages/UiKitPage.tsx и добавьте секцию с mock-данными в группу Device Dashboard Widgets:
<KitSection title="Co2Display">
<Co2DisplayWidget device={MOCK_DEVICE} item={MOCK_CO2_ITEM} socket={null} />
</KitSection>
Откройте портал локально и перейдите на /uikit — виджет должен отобразиться без логина.
Шаг 7. PR-чеклист¶
Перед отправкой PR убедитесь, что:
-
./contracts/regen.shзавершается без ошибок -
_generated/*добавлены в коммит (не в.gitignore) -
contracts/widgets/— новый файл виджета добавлен -
contracts/widgets/index.ts— виджет экспортирован -
widget-registry.tsxна портале — виджет зарегистрирован - Виджет виден в
/uikitбез ошибок в консоли - Scaffold в
_generated/scaffolds/my_device/корректно отражает capabilities - Описание PR содержит: что за устройство, какие capabilities, какой виджет
Отправьте PR против ветки main репозитория idryer-core.
Полный список изменений в одном PR¶
| Файл | Тип изменения |
|---|---|
contracts/mqtt_contract.yaml |
Источник правды |
contracts/_generated/* |
Автогенерация — добавляются целиком |
contracts/widgets/MyWidget.tsx |
Новый файл |
contracts/widgets/index.ts |
+1 строка экспорта |
(на портале после regen.sh) |
src/components/widgets/MyWidget.tsx — копия |
| (на портале вручную) | src/components/widgets/widget-registry.tsx — +1 запись |
| (на портале вручную) | src/pages/UiKitPage.tsx — +1 секция в KitGroup |