77 lines
3.7 KiB
C#
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;
|
|
}
|
|
}
|