using Evergreen.Health.Application.Health.Integrations.HomeAssistant; using Evergreen.Health.Application.Health.Policies; using Evergreen.Health.Application.Health.Scenes; using Microsoft.Extensions.Options; using System.Globalization; namespace Evergreen.Health.Application.Health.Devices.Environment; public sealed class IndoorEnvironmentReader { private readonly HomeAssistantEntityOptions _entities; private readonly TimeProvider _time; public IndoorEnvironmentReader(IOptions options, TimeProvider time) => (_entities, _time) = (options.Value.Entities, time); public IndoorTemperatureSample Read(IReadOnlyDictionary states) => ReadState(string.IsNullOrWhiteSpace(_entities.Temperature) ? null : states.GetValueOrDefault(_entities.Temperature)); public IndoorTemperatureSample Read(IReadOnlyList states) => ReadState(states.LastOrDefault(state => string.Equals(state.EntityId, _entities.Temperature, StringComparison.OrdinalIgnoreCase))); private IndoorTemperatureSample ReadState(HomeAssistantState? state) { if (state is null || !DeviceActionRunner.IsAvailable(state)) return new(null, null, false, "室内温度实体未配置或不可用") { Issue = IndoorTemperatureIssue.Unavailable }; var observedAt = state.LastReported ?? state.LastUpdated; var now = _time.GetUtcNow(); if (observedAt is null || now - observedAt > TimeSpan.FromMinutes(30) || observedAt > now.AddMinutes(1)) return new(null, observedAt, false, "室内温度数据缺少有效时间或已过期") { Issue = IndoorTemperatureIssue.Stale }; if (!double.TryParse(state.State, NumberStyles.Float, CultureInfo.InvariantCulture, out var celsius) || !double.IsFinite(celsius)) return new(null, observedAt, false, "室内温度数据无效") { Issue = IndoorTemperatureIssue.Invalid }; var unit = state.GetStringAttribute("unit_of_measurement"); if (unit is "°F" or "F") celsius = (celsius - 32) * 5 / 9; else if (!string.IsNullOrWhiteSpace(unit) && unit is not ("°C" or "C" or "℃")) return new(null, observedAt, false, "室内温度单位未识别") { Issue = IndoorTemperatureIssue.Invalid }; if (celsius is < -20 or > 60) return new(null, observedAt, false, "室内温度超出有效范围") { Issue = IndoorTemperatureIssue.Invalid }; return new(celsius, observedAt, true, "室内温度数据有效"); } }