Имплементация поддержки любых лампочек #16
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Multi-Provider Architecture
Цель: развить
ignis-coreиз WiZ-only сервера в ядро, которое умеет работать и с локальными устройствами, и с внешними платформами вроде Яндекса и Tuya, не превращаясь в свалку условностей.Базовый принцип
Нужно разделить:
core— единая доменная модель, API, группы, расписания, аудит, web UI;providers— конкретные источники устройств и команд;transport/protocol— низкоуровневые детали WiZ, Yandex API, Tuya API и т.д.Ключевая идея: не все устройства являются "лампочкой в локальной сети".
WiZ — локальный провайдер.
Yandex Smart Home и Tuya — облачные провайдеры.
Чего делать не надо
WizDriver;discoveryодинаково работает для LAN и cloud;Целевая модель
Нужны три уровня сущностей.
1. Provider
Источник устройств и команд.
Примеры:
wiz_localyandex_cloudtuya_cloudProvider отвечает за:
2. Provider Device
Сырая сущность провайдера.
Пример полей:
provider_nameprovider_device_iddisplay_nameroomcapabilitiesonlineraw_payloadЭто не обязательно то, что напрямую видит пользователь в API.
3. Managed Device
Нормализованная сущность внутри
ignis-core, с которой работают:Пример полей:
id— внутренний стабильный ID, напримерwiz:192.168.0.25илиyandex:abc123;providerprovider_device_idnameroomdevice_typesupported_featuresconnectivity_kind—local/cloudonlinestate_snapshotКонтракт провайдера
Нужен единый интерфейс уровня Python-кода.
Примерно такой:
provider_name() -> strkind() -> "local" | "cloud"list_devices() -> list[ProviderDevice]get_device_state(provider_device_id) -> DeviceStateapply_command(provider_device_id, command) -> CommandResultblink(provider_device_id) -> CommandResult | Unsupportedlist_scenes(provider_device_id) -> list[Scene] | Unsupportedhealthcheck() -> ProviderHealthОпционально:
list_provider_groups()apply_group_command()Но groups лучше держать в ядре, а не размазывать по провайдерам, если нет жёсткой необходимости.
Командная модель
Вместо текущей привязки к WiZ-командам ядро должно оперировать нормализованной моделью.
Пример:
state: bool | Nonebrightness: int | Nonetemp: int | Nonergb: {r,g,b} | Nonescene: str | NoneДальше каждый провайдер сам маппит это:
Discovery и синхронизация
Нужно отказаться от предположения, что discovery = сетевой скан.
Правильно так:
wiz_local: активный LAN discovery;yandex_cloud:sync/import devices;tuya_cloud:sync/import devices.То есть вместо одного
discovery_serviceв будущем должен появиться общий orchestration слой:provider sync managerОн запускает:
Группы
Группы лучше оставить собственными, на уровне
ignis-core.Почему:
При этом допустимо потом добавить:
provider_group_idкак optional mapping,но не делать это основой дизайна.
Аудит и расписания
Аудит и расписания должны работать только через нормализованный слой.
То есть:
ManagedDeviceили группу;device_idиgroup_id.Так мы не привязываем event log к деталям Tuya или Яндекса.
Конфиг и секреты
Нужно вводить provider-level конфиг.
Примеры env:
IGNIS_WIZ_ENABLED=trueIGNIS_YANDEX_ENABLED=falseIGNIS_YANDEX_CLIENT_ID=...IGNIS_YANDEX_CLIENT_SECRET=...IGNIS_TUYA_ENABLED=falseIGNIS_TUYA_ACCESS_ID=...IGNIS_TUYA_ACCESS_SECRET=...IGNIS_TUYA_REGION=euТокены лучше хранить не в
.env, а в БД или отдельном secure storage, если появится OAuth/user binding.API-слой
Снаружи API лучше оставить максимально стабильным.
Желательное поведение:
GET /devicesпо-прежнему отдаёт единый список устройств;POST /control/device/{device_id}не знает о конкретном провайдере;GET /devices/groupsи schedules работают как сейчас.Но стоит добавить новые служебные эндпоинты:
GET /providersPOST /providers/{provider}/syncGET /providers/{provider}/healthPOST /providers/yandex/connectPOST /providers/tuya/connectЕсли появится OAuth или облачная авторизация, без этого будет больно.
Web UI
UI не должен знать детали протокола.
Но ему пригодятся:
local/cloud;syncдля cloud providers.Рекомендуемая структура файлов
Файлы, которые будут затронуты крепко
app/core/state.pyapp/core/discovery.pyapp/api/routes/devices.pyapp/api/routes/control.pyapp/api/routes/system.pyapp/api/schemas.pyapp/models/device.pymain.pystatic/app.jsstatic/index.htmlПочему:
Файлы, которые, скорее всего, тоже придётся менять
app/core/server_info.pyapp/api/routes/stats.pyapp/core/scheduler.pytests/test_p0_security_and_control.pytests/test_p1_discovery.pytests/test_p1_ui_security.pyПочему:
Новые файлы и директории, которые стоит создать
Core abstraction
app/core/providers/base.pyapp/core/providers/registry.pyapp/core/providers/models.pyapp/core/providers/sync_manager.pyProvider implementations
app/providers/wiz_local/provider.pyapp/providers/wiz_local/mapper.pyapp/providers/yandex_cloud/provider.pyapp/providers/yandex_cloud/client.pyapp/providers/yandex_cloud/mapper.pyapp/providers/tuya_cloud/provider.pyapp/providers/tuya_cloud/client.pyapp/providers/tuya_cloud/mapper.pyPersistence for provider accounts/config
app/models/provider_account.pyapp/models/provider_state.pyAPI
app/api/routes/providers.pyTests
tests/test_p0_provider_registry.pytests/test_p0_yandex_provider.pytests/test_p0_tuya_provider.pytests/test_p1_multi_provider_control.pyМиграция по этапам
Этап 1. Внутренний abstraction layer
Сначала без новых интеграций:
Это самый важный этап.
Этап 2. Provider registry
wiz_localчерез registry;devices/control/systemна работу через registry.Этап 3. Cloud provider skeletons
yandex_cloudиtuya_cloudбез полноценного UI;Этап 4. Команды и статус
get_stateиapply_command;Этап 5. UI и provider management
Главный архитектурный критерий
Если после внедрения нового провайдера приходится лезть:
control.py,devices.py,то архитектура всё ещё плохая.
Правильная цель: новый провайдер добавляется в основном через:
Итог
Правильная архитектура для
ignis-core— это не “ещё один драйвер рядом с WiZ”, а:Только так поддержка Яндекса, Tuya и следующих платформ не превратится в набор
if/elseпо всему проекту.