Files
Evergreen-Health/Evergreen.Health.Application/Health/Integrations/HomeAssistant/HomeAssistantLightTransitionService.cs
T
2026-11-11 16:11:41 +08:00

86 lines
4.0 KiB
C#

using Evergreen.Health.Application.Health.Models;
using Evergreen.Health.Application.Health.Scenes;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Evergreen.Health.Application.Health.Integrations.HomeAssistant;
/// <summary>管理明确配置的场景灯;共享单灯渐变实现,但不共享亮度、计时或取消状态。</summary>
public sealed class HomeAssistantLightTransitionService : IHostedService, ILightTransitionRuntime
{
private readonly LightTransitionWorker _primary;
private readonly Dictionary<string, LightTransitionWorker> _lights = new(StringComparer.OrdinalIgnoreCase);
public HomeAssistantLightTransitionService(IHomeAssistantGateway gateway, LightOffDispatcher lightOff,
IOptions<HomeAssistantOptions> options, ILogger<HomeAssistantLightTransitionService> logger,
SceneLightSafetyState safety, TimeProvider time)
{
_primary = new(gateway, lightOff, options, logger, safety, time);
var primaryId = options.Value.Entities.AmbientLight;
if (!string.IsNullOrWhiteSpace(primaryId)) _lights.Add(primaryId, _primary);
foreach (var light in options.Value.AdditionalSleepLights)
{
if (string.IsNullOrWhiteSpace(light.Key) || string.IsNullOrWhiteSpace(light.Name) ||
!light.EntityId.StartsWith("light.", StringComparison.Ordinal))
throw new InvalidOperationException("额外场景灯需要有效的模块键、名称和 light 实体");
if (!_lights.TryAdd(light.EntityId, new(gateway, lightOff, options, logger, safety, time, light)))
throw new InvalidOperationException($"场景灯实体重复配置:{light.EntityId}");
}
}
public ILightTransitionRuntime ForEntity(string entityId)
=> ResolveLight(entityId);
public Task<HomeAssistantLightTransitionOutput?> StartSleepIfIdleAsync(string entityId, int kelvin, CancellationToken token)
=> ResolveLight(entityId).StartSleepIfIdleAsync(kelvin, token);
private LightTransitionWorker ResolveLight(string entityId)
=> !string.IsNullOrWhiteSpace(entityId) && _lights.TryGetValue(entityId, out var light) ? light
: throw new InvalidOperationException("该灯未配置为场景灯");
public HomeAssistantLightTransitionOutput GetStatus()
{
var output = _primary.GetStatus();
output.Lights = _lights.Values.Select(light => light.GetStatus()).ToList();
return output;
}
// 旧助眠/晨起接口只作用于原卧室灯;额外灯由各自场景模块调用。
public async Task<HomeAssistantLightTransitionOutput> StartSleepAsync(int kelvin, CancellationToken token)
{
EnsurePrimaryConfigured();
await _primary.StartSleepAsync(kelvin, token);
return GetStatus();
}
public async Task<HomeAssistantLightTransitionOutput> StartWakeAsync(CancellationToken token)
{
EnsurePrimaryConfigured();
await _primary.StartWakeAsync(token);
return GetStatus();
}
public async Task<HomeAssistantLightTransitionOutput> CancelPrimaryAsync(CancellationToken token)
{
EnsurePrimaryConfigured();
await _primary.CancelAsync(token);
return GetStatus();
}
private void EnsurePrimaryConfigured()
{
if (string.IsNullOrWhiteSpace(_primary.GetStatus().EntityId)) throw new InvalidOperationException("卧室灯光未配置");
}
// 一键关闭/场景替换必须取消全部灯的计时任务,防止后续 tick 重新点亮灯。
public async Task<HomeAssistantLightTransitionOutput> CancelAsync(CancellationToken token)
{
foreach (var light in _lights.Values) await light.CancelAsync(token);
return GetStatus();
}
public Task StartAsync(CancellationToken token) => Task.CompletedTask;
public async Task StopAsync(CancellationToken token)
{
foreach (var light in _lights.Values) await light.StopAsync(token);
}
}