1
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
using Evergreen.Health.Application.Health.Integrations.HomeAssistant;
|
||||
|
||||
namespace Evergreen.Health.Application.Health.Devices.Contracts;
|
||||
|
||||
public enum DeviceCommandIntent { Normal, StartMassage, TurnRelaxationLightOn, TurnRelaxationLightOff }
|
||||
|
||||
/// <summary>实体由后端模块提供,不接受客户端任意实体或任意服务名。</summary>
|
||||
public sealed record DeviceCommand(string Name, string EntityId, string Domain, string Service,
|
||||
IReadOnlyDictionary<string, object?>? Data = null, string? ExpectedState = null)
|
||||
{
|
||||
public DeviceCommandIntent Intent { get; init; } = DeviceCommandIntent.Normal;
|
||||
// 新设备的能力校验由模块提供,所有发送路径(含场景关闭)都不可绕过。
|
||||
public Action<HomeAssistantState>? ValidateCurrentState { get; init; }
|
||||
// 不为空时 ExpectedState 表示此属性的期望值,而不是实体的顶层状态。
|
||||
public string? ExpectedAttribute { get; init; }
|
||||
public bool RetryOnVerificationMismatch { get; init; } = true;
|
||||
public Dictionary<string, object?> ToServiceData()
|
||||
{
|
||||
var result = Data is null ? new Dictionary<string, object?>() : new Dictionary<string, object?>(Data);
|
||||
result["entity_id"] = EntityId;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record DeviceParameterDefinition(string Key, string EntityId,
|
||||
Func<object?, DeviceCommand> Build, string Label,
|
||||
int? Minimum = null, int? Maximum = null, IReadOnlyList<string>? Options = null);
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Evergreen.Health.Application.Health.Devices.Contracts;
|
||||
|
||||
public static class DeviceValues
|
||||
{
|
||||
public static string Text(object? value)
|
||||
=> value is JsonElement element
|
||||
? (element.ValueKind == JsonValueKind.String ? element.GetString() ?? "" : element.ToString()).Trim()
|
||||
: Convert.ToString(value, CultureInfo.InvariantCulture)?.Trim() ?? "";
|
||||
|
||||
public static int Integer(object? value, int minimum, int maximum, string label)
|
||||
{
|
||||
if (!int.TryParse(Text(value), NumberStyles.Integer, CultureInfo.InvariantCulture, out var number) ||
|
||||
number < minimum || number > maximum)
|
||||
throw new InvalidOperationException($"{label}应在 {minimum} 到 {maximum} 之间");
|
||||
return number;
|
||||
}
|
||||
|
||||
public static bool Boolean(object? value)
|
||||
{
|
||||
if (bool.TryParse(Text(value), out var result)) return result;
|
||||
throw new InvalidOperationException("开关参数无效");
|
||||
}
|
||||
|
||||
public static string Choice(object? value, IReadOnlyList<string> options, string label)
|
||||
{
|
||||
var text = Text(value);
|
||||
if (!options.Contains(text, StringComparer.Ordinal))
|
||||
throw new InvalidOperationException($"{label}选项无效");
|
||||
return text;
|
||||
}
|
||||
|
||||
public static DeviceParameterDefinition Switch(string key, string entity, string domain, string label)
|
||||
=> new(key, entity, value => new(label, entity, domain,
|
||||
Boolean(value) ? "turn_on" : "turn_off"), label);
|
||||
|
||||
public static DeviceParameterDefinition Number(string key, string entity, int min, int max, string label)
|
||||
=> new(key, entity, value => new(label, entity, "number", "set_value",
|
||||
new Dictionary<string, object?> { ["value"] = Integer(value, min, max, label) }), label, min, max);
|
||||
|
||||
public static DeviceParameterDefinition Select(string key, string entity, IReadOnlyList<string> options, string label,
|
||||
bool verify = false, DeviceCommandIntent intent = DeviceCommandIntent.Normal)
|
||||
=> new(key, entity, value =>
|
||||
{
|
||||
var option = Choice(value, options, label);
|
||||
return new(label, entity, "select", "select_option",
|
||||
new Dictionary<string, object?> { ["option"] = option }, verify ? option : null)
|
||||
{ Intent = option == "无" ? DeviceCommandIntent.Normal : intent };
|
||||
}, label, Options: options);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Evergreen.Health.Application.Health.Integrations.HomeAssistant;
|
||||
using Evergreen.Health.Application.Health.Models;
|
||||
using Evergreen.Health.Application.Health.Scenes;
|
||||
|
||||
namespace Evergreen.Health.Application.Health.Devices.Contracts;
|
||||
|
||||
/// <summary>新增家具实现此契约并注册即可;场景和 API 不枚举具体家具类型。</summary>
|
||||
public interface IHealthDeviceModule
|
||||
{
|
||||
string Key { get; }
|
||||
string Category { get; }
|
||||
string DisplayName { get; }
|
||||
string PrimaryEntityId { get; }
|
||||
bool PowerUsesIndependentGateway { get; }
|
||||
IReadOnlyList<DeviceParameterDefinition> Parameters { get; }
|
||||
IEnumerable<string> EntityIds { get; }
|
||||
DeviceCommand BuildParameterCommand(string parameterType, object? value);
|
||||
Task ExecuteParameterAsync(DeviceCommand command, CancellationToken cancellationToken);
|
||||
bool ParameterUsesIndependentGateway(DeviceCommand command);
|
||||
Task SetPowerAsync(bool turnOn, CancellationToken cancellationToken);
|
||||
Task ExecuteSleepAsync(DeviceSceneContext context, PersonalizedSceneConfig config,
|
||||
string constitutionCode, CancellationToken cancellationToken);
|
||||
IEnumerable<DeviceCommand> GetShutdownCommands();
|
||||
HomeAssistantScenePresetItem? BuildPreview(PersonalizedSceneConfig config, string constitutionCode);
|
||||
}
|
||||
Reference in New Issue
Block a user