using Evergreen.Health.Application.Health.Devices.Contracts; using Evergreen.Health.Application.Health.Integrations.HomeAssistant; using Evergreen.Health.Application.Health.Models; using Evergreen.Health.Application.Health.Scenes; namespace Evergreen.Health.Application.Health.Devices; /// 共用归属校验和路由;标准能力处理普通参数,专属模块保留自己的执行与场景逻辑。 public sealed class DeviceParameterController { private readonly DeviceModuleRegistry _modules; private readonly DeviceActionRunner _runner; private readonly Dictionary _standard = new(StringComparer.OrdinalIgnoreCase); public DeviceParameterController(DeviceModuleRegistry modules, DeviceActionRunner runner, IEnumerable standard) { (_modules, _runner) = (modules, runner); foreach (var adapter in standard) foreach (var key in adapter.ParameterKeys) if (!_standard.TryAdd(key, adapter)) throw new InvalidOperationException($"标准设备参数重复:{key}"); } public async Task ExecuteAsync(HomeAssistantDiscoveredDeviceOutput device, IReadOnlyDictionary states, string parameterType, object? value, string? requestedEntityId, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); IHealthDeviceModule? module; DeviceCommand command; if (_standard.TryGetValue(parameterType, out var adapter)) { var primary = device.PrimaryControlEntityId; if (string.IsNullOrWhiteSpace(primary) || !device.Entities.Any(entity => string.Equals(entity.EntityId, primary, StringComparison.OrdinalIgnoreCase)) || !states.TryGetValue(primary, out var state)) throw new InvalidOperationException("设备主控实体未确认"); command = adapter.Build(device, state, parameterType, value); module = _modules.ForEntities(device.Entities.Select(entity => entity.EntityId)); // 附属灯不能借用另一主控的执行钩子;仅复用该实例所属的专属模块。 if (module is not null && !string.Equals(module.PrimaryEntityId, command.EntityId, StringComparison.OrdinalIgnoreCase)) module = null; } else { module = _modules.ForParameter(parameterType); command = module.BuildParameterCommand(parameterType, value); } if (string.IsNullOrWhiteSpace(command.EntityId)) throw new InvalidOperationException("该参数尚未配置 Home Assistant 实体"); if (!string.IsNullOrWhiteSpace(requestedEntityId) && !string.Equals(requestedEntityId, command.EntityId, StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException("请求实体与该参数的受信任实体不匹配"); if (!device.Entities.Any(entity => string.Equals(entity.EntityId, command.EntityId, StringComparison.OrdinalIgnoreCase))) throw new InvalidOperationException("该参数不属于指定设备"); if (module?.ParameterUsesIndependentGateway(command) != true && (!states.TryGetValue(command.EntityId, out var current) || !DeviceActionRunner.IsAvailable(current))) throw new InvalidOperationException("目标参数实体当前离线或不可用"); if (module is not null) await module.ExecuteParameterAsync(command, cancellationToken); else { await _runner.SendAsync(command, cancellationToken); if (command.ExpectedState is not null) await _runner.VerifyParameterAsync(command, cancellationToken); } return command; } }