Files
Evergreen-Health/Evergreen.Health.Application/Health/Devices/Curtain/CurtainModule.cs
T
2026-10-09 16:46:30 +08:00

77 lines
3.7 KiB
C#

using Evergreen.Health.Application.Health.Devices.Contracts;
using Evergreen.Health.Application.Health.Integrations.HomeAssistant;
using Evergreen.Health.Application.Health.Models;
using Evergreen.Health.Application.Health.Scenes;
using Microsoft.Extensions.Options;
namespace Evergreen.Health.Application.Health.Devices.Curtain;
/// <summary>窗帘开合不是电源开关;本版只提供单独手动控制,不擅自加入舒睡/全关。</summary>
public sealed class CurtainModule : HealthDeviceModule
{
private readonly string _entityId;
public CurtainModule(DeviceActionRunner runner, IOptions<HomeAssistantOptions> options) : base(runner)
{
_entityId = options.Value.Entities.Curtain;
Parameters =
[
new("curtain_action", _entityId, BuildAction, "窗帘动作", Options: ["open", "stop", "close"]),
new("curtain_position", _entityId, BuildPosition, "窗帘开度", 0, 100)
];
}
public override string Key => "curtain";
public override string Category => nameof(HealthDeviceCategory.Curtain);
public override string DisplayName => "窗帘";
public override string PrimaryEntityId => _entityId;
public override IReadOnlyList<DeviceParameterDefinition> Parameters { get; }
private DeviceCommand BuildAction(object? value)
{
var action = DeviceValues.Choice(value, ["open", "stop", "close"], "窗帘动作");
var (service, feature, label) = action switch
{
"open" => ("open_cover", 1, "打开窗帘"), "close" => ("close_cover", 2, "关闭窗帘"),
_ => ("stop_cover", 8, "停止窗帘")
};
return new(label, _entityId, "cover", service)
{
ValidateCurrentState = state => ValidateFeature(state, feature, label),
RetryOnVerificationMismatch = false
};
}
private DeviceCommand BuildPosition(object? value)
{
var position = DeviceValues.Integer(value, 0, 100, "窗帘开度");
return new("设置窗帘开度", _entityId, "cover", "set_cover_position",
new Dictionary<string, object?> { ["position"] = position })
{
ValidateCurrentState = state => ValidateFeature(state, 4, "设置窗帘开度"),
RetryOnVerificationMismatch = false
};
}
private void ValidateFeature(HomeAssistantState state, int feature, string label)
{
if (!_entityId.StartsWith("cover.", StringComparison.OrdinalIgnoreCase) ||
!string.Equals(state.EntityId, _entityId, StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException("窗帘主控实体绑定无效");
var features = state.GetDoubleAttribute("supported_features");
if (features is null || features < 0 || features > int.MaxValue || features != Math.Truncate(features.Value) ||
(((int)features.Value & feature) != feature))
throw new InvalidOperationException($"该窗帘未声明支持{label}");
}
public override Task SetPowerAsync(bool turnOn, CancellationToken cancellationToken)
=> throw new InvalidOperationException("窗帘没有电源开关,请使用打开、停止或关闭控制");
public override Task ExecuteSleepAsync(DeviceSceneContext context, PersonalizedSceneConfig config,
string constitutionCode, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
context.Results.Add(new() { Name = "窗帘场景联动", EntityId = _entityId, Status = "Skipped",
Message = string.IsNullOrWhiteSpace(_entityId) ? "窗帘实体尚未配置" : "本版窗帘只支持手动控制,舒睡不自动改变开合位置" });
return Task.CompletedTask;
}
}