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; /// 管理明确配置的场景灯;共享单灯渐变实现,但不共享亮度、计时或取消状态。 public sealed class HomeAssistantLightTransitionService : IHostedService, ILightTransitionRuntime { private readonly LightTransitionWorker _primary; private readonly Dictionary _lights = new(StringComparer.OrdinalIgnoreCase); public HomeAssistantLightTransitionService(IHomeAssistantGateway gateway, LightOffDispatcher lightOff, IOptions options, ILogger 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 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 StartSleepAsync(int kelvin, CancellationToken token) { EnsurePrimaryConfigured(); await _primary.StartSleepAsync(kelvin, token); return GetStatus(); } public async Task StartWakeAsync(CancellationToken token) { EnsurePrimaryConfigured(); await _primary.StartWakeAsync(token); return GetStatus(); } public async Task 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 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); } }