ThinkingHome.DeviceModel
Ядро хаба: нейтральная модель устройств и хост. Эта страница — путь с кодом: собственный процесс, драйвер устройства и управление им из C#. Если нужен готовый хаб без программирования — начните с быстрого старта.
Установка
dotnet new console -o MyHome
cd MyHome
dotnet add package ThinkingHome.DeviceModel
dotnet add package ThinkingHome.DeviceModel.FluentApiПервый пакет — ядро: модель устройств и хост. Второй — API для управления устройствами из кода.
Устройство: реализация IDevice
Драйвер реализует интерфейс IDevice: описывает устройство, отдаёт его состояние, исполняет команды и сообщает об изменениях. Создайте файл Lamp.cs:
using ThinkingHome.DeviceModel;
using ThinkingHome.DeviceModel.Capabilities;
using ThinkingHome.DeviceModel.Commands;
using ThinkingHome.DeviceModel.State;
public sealed class Lamp(string id, string title) : IDevice
{
private bool isOn;
public string Id => id;
// через это событие драйвер сообщает хабу, что состояние изменилось
public event Action<StateChange>? Changed;
// описание устройства: что это за прибор и чем в нём можно управлять
public DeviceDescriptor Describe() => new()
{
Id = id,
Title = title,
Endpoints =
[
new Endpoint
{
Id = 0,
Type = DeviceType.OnOffLight,
Capabilities = [new OnOffCapability { Instance = OnOffCapability.InstanceName }],
},
],
};
// текущее состояние устройства
public Task<DeviceSnapshot> QueryAsync(CancellationToken ct = default)
=> Task.FromResult(new DeviceSnapshot
{
DeviceId = id,
Values = [new OnOffState { Instance = OnOffCapability.InstanceName, Value = isOn }],
});
// исполнение команды: здесь драйвер обратился бы к прибору
public Task<CommandOutcome> ExecuteAsync(DeviceCommand command, CancellationToken ct = default)
{
if (command is not OnOffCommand cmd)
{
return Task.FromResult(CommandOutcome.Unsupported);
}
isOn = cmd.Value;
Console.WriteLine($"[{title}] {(isOn ? "включена" : "выключена")}");
Changed?.Invoke(new StateChange
{
DeviceId = id,
Value = new OnOffState { Instance = OnOffCapability.InstanceName, Value = isOn },
});
return Task.FromResult(CommandOutcome.Done);
}
}Устройство описано в терминах модели: тип OnOffLight — лампа с одним выключателем, способность OnOff — то, чем в ней можно управлять. Команду драйвер разбирает сопоставлением с образцом: OnOffCommand он понимает, остальные отклоняет результатом Unsupported.
Хост: регистрация и управление
Замените содержимое Program.cs:
using ThinkingHome.DeviceModel;
using ThinkingHome.DeviceModel.FluentApi;
// хаб и подключённое к нему устройство
var host = new DeviceHost();
host.Register(new Lamp("lamp-1", "Лампа в коридоре"));
var lamp = host.Device("lamp-1");
// подписка на изменения состояния
using var subscription = lamp.OnChanged(change => Console.WriteLine($"хаб получил отчёт: {change.Value}"));
// команда и чтение состояния
await lamp.OnOff().TurnOnAsync();
Console.WriteLine($"лампа включена: {await lamp.OnOff().GetAsync()}");
await lamp.OnOff().TurnOffAsync();
Console.WriteLine($"лампа включена: {await lamp.OnOff().GetAsync()}");Запуск
dotnet run[Лампа в коридоре] включена
хаб получил отчёт: OnOffState { EndpointId = 0, Instance = on_off, Value = True }
лампа включена: True
[Лампа в коридоре] выключена
хаб получил отчёт: OnOffState { EndpointId = 0, Instance = on_off, Value = False }
лампа включена: FalseВ выводе видны все три вида обращений к устройству: команда дошла до драйвера и он напечатал строку, отчёт об изменении вернулся подписчику, а запрос состояния отдал текущее значение.
Что дальше
- Нейтральная модель — из чего складывается описание устройства и почему оно устроено именно так;
- FluentApi — типизированные хендлы, использованные в примере: команды, чтение, подписки;
- Справочник API — все типы ядра.