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; /// 窗帘开合不是电源开关;本版只提供单独手动控制,不擅自加入舒睡/全关。 public sealed class CurtainModule : HealthDeviceModule { private readonly string _entityId; public CurtainModule(DeviceActionRunner runner, IOptions 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 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 { ["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; } }