50 lines
2.4 KiB
C#
50 lines
2.4 KiB
C#
using Evergreen.Health.Application.Health.Devices.Contracts;
|
|
using Evergreen.Health.Application.Health.Integrations.HomeAssistant;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace Evergreen.Health.Application.Health.Scenes;
|
|
|
|
public interface IDeviceActionSafetyRule
|
|
{
|
|
Task ValidateAsync(DeviceCommand command, CancellationToken cancellationToken);
|
|
void Accepted(DeviceCommand command, DateTimeOffset now);
|
|
}
|
|
|
|
public sealed class SceneLightSafetyState
|
|
{
|
|
private readonly object _lock = new();
|
|
private DateTimeOffset? _offAt;
|
|
public DateTimeOffset? OffAt { get { lock (_lock) return _offAt; } }
|
|
public void MarkOff(DateTimeOffset now) { lock (_lock) _offAt = now; }
|
|
public void MarkOn() { lock (_lock) _offAt = null; }
|
|
}
|
|
|
|
/// <summary>只依赖“启动按摩”的动作意图,不知道床垫实体。防止灯灭后较晚的启动指令再开启按摩。</summary>
|
|
public sealed class LightMassageSafetyRule : IDeviceActionSafetyRule
|
|
{
|
|
private readonly IHomeAssistantGateway _gateway;
|
|
private readonly string _lightEntity;
|
|
private readonly SceneLightSafetyState _state;
|
|
public LightMassageSafetyRule(IHomeAssistantGateway gateway, IOptions<HomeAssistantOptions> options,
|
|
SceneLightSafetyState state) => (_gateway, _lightEntity, _state) = (gateway, options.Value.Entities.AmbientLight, state);
|
|
|
|
public async Task ValidateAsync(DeviceCommand command, CancellationToken cancellationToken)
|
|
{
|
|
if (command.Intent != DeviceCommandIntent.StartMassage || string.IsNullOrWhiteSpace(_lightEntity)) return;
|
|
var light = (await _gateway.GetStatesAsync(cancellationToken)).FirstOrDefault(item =>
|
|
string.Equals(item.EntityId, _lightEntity, StringComparison.OrdinalIgnoreCase));
|
|
var offAt = _state.OffAt;
|
|
if (light is null || !DeviceActionRunner.IsAvailable(light) ||
|
|
!string.Equals(light.State, "on", StringComparison.OrdinalIgnoreCase) ||
|
|
light.GetDoubleAttribute("brightness") is <= 0 ||
|
|
(offAt is not null && (light.LastUpdated is null || light.LastUpdated <= offAt)))
|
|
throw new InvalidOperationException("卧室灯光已关闭或状态未确认,不启动床垫按摩");
|
|
}
|
|
|
|
public void Accepted(DeviceCommand command, DateTimeOffset now)
|
|
{
|
|
if (command.Intent == DeviceCommandIntent.TurnRelaxationLightOff) _state.MarkOff(now);
|
|
else if (command.Intent == DeviceCommandIntent.TurnRelaxationLightOn) _state.MarkOn();
|
|
}
|
|
}
|