11
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
using Evergreen.Health.Application.Health.Devices.Contracts;
|
||||
using Evergreen.Health.Application.Health.Integrations.HomeAssistant;
|
||||
using Evergreen.Health.Application.Health.Models;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Evergreen.Health.Application.Health.Devices.Lighting;
|
||||
|
||||
/// <summary>标准灯光参数依赖 HA 上报能力,不能将一个型号的色温范围或模式复制给另一盏灯。</summary>
|
||||
public sealed class StandardLightControl : IStandardDeviceControl
|
||||
{
|
||||
public IReadOnlyList<string> ParameterKeys => ["light_brightness", "light_color_temperature", "light_effect"];
|
||||
private static readonly string[] DimmableModes = ["brightness", "color_temp", "hs", "rgb", "rgbw", "rgbww", "xy", "white"];
|
||||
|
||||
public DeviceCommand Build(HomeAssistantDiscoveredDeviceOutput device, HomeAssistantState state,
|
||||
string parameterType, object? value)
|
||||
{
|
||||
var entityId = device.PrimaryControlEntityId ?? "";
|
||||
if (!entityId.StartsWith("light.", StringComparison.Ordinal) ||
|
||||
!device.Entities.Any(entity => entity.EntityId == entityId && entity.Domain == "light"))
|
||||
throw new InvalidOperationException("该设备没有受信任的灯光主控能力");
|
||||
Validate(state, entityId, parameterType, value);
|
||||
var command = parameterType switch
|
||||
{
|
||||
"light_brightness" => Brightness(entityId, value),
|
||||
"light_color_temperature" => new DeviceCommand("设置灯光色温", entityId, "light", "turn_on",
|
||||
new Dictionary<string, object?> { ["color_temp_kelvin"] = DeviceValues.Integer(value, 1, int.MaxValue, "灯光色温") }),
|
||||
"light_effect" => new DeviceCommand("设置灯光模式", entityId, "light", "turn_on",
|
||||
new Dictionary<string, object?> { ["effect"] = DeviceValues.Text(value) }),
|
||||
_ => throw new InvalidOperationException("不支持的标准灯光参数")
|
||||
};
|
||||
return command with { ValidateCurrentState = current => Validate(current, entityId, parameterType, value),
|
||||
RetryOnVerificationMismatch = false };
|
||||
}
|
||||
|
||||
private static DeviceCommand Brightness(string entityId, object? value)
|
||||
{
|
||||
var brightness = DeviceValues.Integer(value, 0, 100, "灯光亮度");
|
||||
return new("设置灯光亮度", entityId, "light", brightness == 0 ? "turn_off" : "turn_on",
|
||||
brightness == 0 ? null : new Dictionary<string, object?> { ["brightness_pct"] = brightness },
|
||||
brightness == 0 ? "off" : null);
|
||||
}
|
||||
|
||||
private static void Validate(HomeAssistantState state, string entityId, string parameterType, object? value)
|
||||
{
|
||||
if (state.EntityId != entityId || state.State is not ("on" or "off"))
|
||||
throw new InvalidOperationException("灯光主控当前离线或状态未确认");
|
||||
var modes = Strings(state, "supported_color_modes");
|
||||
switch (parameterType)
|
||||
{
|
||||
case "light_brightness":
|
||||
if (!modes.Any(mode => DimmableModes.Contains(mode, StringComparer.Ordinal)))
|
||||
throw new InvalidOperationException("这盏灯未声明支持亮度调节");
|
||||
DeviceValues.Integer(value, 0, 100, "灯光亮度");
|
||||
break;
|
||||
case "light_color_temperature":
|
||||
var min = state.GetDoubleAttribute("min_color_temp_kelvin");
|
||||
var max = state.GetDoubleAttribute("max_color_temp_kelvin");
|
||||
if (!modes.Contains("color_temp") || min is null || max is null || min <= 0 || max <= min ||
|
||||
max > int.MaxValue || min != Math.Truncate(min.Value) || max != Math.Truncate(max.Value))
|
||||
throw new InvalidOperationException("这盏灯的色温能力或范围未确认");
|
||||
DeviceValues.Integer(value, (int)min.Value, (int)max.Value, "灯光色温");
|
||||
break;
|
||||
case "light_effect":
|
||||
var features = state.GetDoubleAttribute("supported_features");
|
||||
if (features is null || features < 0 || features > int.MaxValue || features != Math.Truncate(features.Value) ||
|
||||
(((int)features.Value & 4) == 0)) throw new InvalidOperationException("这盏灯未声明支持灯光模式");
|
||||
DeviceValues.Choice(value, Strings(state, "effect_list"), "灯光模式");
|
||||
break;
|
||||
default: throw new InvalidOperationException("不支持的标准灯光参数");
|
||||
}
|
||||
}
|
||||
|
||||
private static string[] Strings(HomeAssistantState state, string key)
|
||||
=> state.Attributes.ValueKind == JsonValueKind.Object && state.Attributes.TryGetProperty(key, out var values) &&
|
||||
values.ValueKind == JsonValueKind.Array
|
||||
? values.EnumerateArray().Where(value => value.ValueKind == JsonValueKind.String)
|
||||
.Select(value => value.GetString()!).Where(value => !string.IsNullOrWhiteSpace(value)).Distinct(StringComparer.Ordinal).ToArray()
|
||||
: [];
|
||||
}
|
||||
Reference in New Issue
Block a user