This commit is contained in:
fuenmao
2026-11-11 16:11:41 +08:00
parent 3ca90c4cf3
commit 61e25175db
39 changed files with 2176 additions and 31 deletions
@@ -30,8 +30,13 @@ public sealed class HomeAssistantLightTransitionService : IHostedService, ILight
}
public ILightTransitionRuntime ForEntity(string entityId)
=> _lights.TryGetValue(entityId, out var light) ? light
: entityId == _primary.GetStatus().EntityId ? _primary
=> 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()
@@ -44,31 +49,37 @@ public sealed class HomeAssistantLightTransitionService : IHostedService, ILight
// 旧助眠/晨起接口只作用于原卧室灯;额外灯由各自场景模块调用。
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);
if (_lights.Count == 0) await _primary.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);
if (_lights.Count == 0) await _primary.StopAsync(token);
}
}
@@ -10,11 +10,20 @@ public sealed class HomeAssistantOptions : IConfigurableOptions
public int TimeoutSeconds { get; set; } = 15;
// 匹配已退役设备的明确实体绑定,不按名称或品牌隐藏设备。
public List<string> ExcludedDeviceEntityIds { get; set; } = [];
public bool OnlyConfiguredDevices { get; set; }
public List<DisplayOnlyDeviceOptions> DisplayOnlyDevices { get; set; } = [];
public HomeAssistantEntityOptions Entities { get; set; } = new();
public HomeAssistantSleepSceneOptions SleepScene { get; set; } = new();
public List<SceneLightOptions> AdditionalSleepLights { get; set; } = [];
}
public sealed class DisplayOnlyDeviceOptions
{
public string EntityId { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string Category { get; set; } = "Other";
}
/// <summary>明确加入舒睡/全关的额外灯具;未配置的灯仍仅提供单独控制。</summary>
public sealed class SceneLightOptions
{
@@ -71,10 +71,18 @@ internal sealed class LightTransitionWorker : ILightTransitionRuntime
}
public async Task<HomeAssistantLightTransitionOutput> StartSleepAsync(int colorTemperatureKelvin, CancellationToken cancellationToken)
=> (await StartSleepCoreAsync(colorTemperatureKelvin, onlyIfIdle: false, cancellationToken))!;
internal Task<HomeAssistantLightTransitionOutput?> StartSleepIfIdleAsync(int colorTemperatureKelvin, CancellationToken cancellationToken)
=> StartSleepCoreAsync(colorTemperatureKelvin, onlyIfIdle: true, cancellationToken);
private async Task<HomeAssistantLightTransitionOutput?> StartSleepCoreAsync(int colorTemperatureKelvin, bool onlyIfIdle,
CancellationToken cancellationToken)
{
await _operationLock.WaitAsync(cancellationToken);
try
{
if (onlyIfIdle && GetStatus().Running) return null;
await StopCurrentAsync();
var light = await ReadAvailableLightAsync(cancellationToken);
var data = BuildSleepStartData(light, colorTemperatureKelvin);