using Evergreen.Health.Application.Health.Integrations.HomeAssistant; using Evergreen.Health.Application.Health.Scenes; using Evergreen.Health.Application.Health.Sleep; using Microsoft.Extensions.Options; namespace Evergreen.Health.Application.Health.Devices.Dehumidifier; public sealed class BedPresenceDehumidifierStartAction(DehumidifierModule dehumidifier, DeviceActionRunner runner, IOptions options, TimeProvider clock) : IBedPresenceAction { private HomeAssistantOptions Configuration => options.Value; public string Key => "dehumidifier-humidity-start"; public string Description => "湿度偏高时开启除湿机"; public string ConditionLabel => $"室内相对湿度超过 {Configuration.SleepScene.TargetHumidity}% 且除湿机关机时,发送开机指令。"; public bool SupportsTarget(string entityId) => entityId == dehumidifier.PrimaryEntityId && DehumidifierHumidityCondition.SupportsTarget(Configuration, entityId); public async Task ExecuteAsync(string entityId, CancellationToken token) { if (!SupportsTarget(entityId)) return "target-not-configured"; var command = dehumidifier.BuildStartCommand() with { ExpectedState = "on", RetryOnVerificationMismatch = false }; var reason = await runner.SendIfAsync(command, states => GetSkipReason(states, entityId), token); if (reason is not null) return reason; try { await runner.VerifyParameterAsync(command, token); return "readback-confirmed"; } catch (InvalidOperationException) { return "sent-readback-unconfirmed"; } } private string? GetSkipReason(IReadOnlyList states, string entityId) { var reason = DehumidifierHumidityCondition.SkipReason(states, Configuration, clock.GetUtcNow()); if (reason is not null) return reason; var state = states.FirstOrDefault(item => string.Equals(item.EntityId, entityId, StringComparison.OrdinalIgnoreCase)); if (state is null || !DeviceActionRunner.IsAvailable(state)) return "dehumidifier-unavailable"; return state.State switch { "off" => null, "on" => "dehumidifier-already-on", _ => "dehumidifier-state-not-off" }; } }