1
This commit is contained in:
+51
-4
@@ -5,7 +5,7 @@ using System.Text.Json;
|
||||
|
||||
namespace Evergreen.Health.Application.Health.Integrations.HomeAssistant;
|
||||
|
||||
public sealed class HomeAssistantClient
|
||||
public sealed class HomeAssistantClient : IHomeAssistantGateway
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly HomeAssistantOptions _options;
|
||||
@@ -20,9 +20,47 @@ public sealed class HomeAssistantClient
|
||||
public async Task<IReadOnlyList<HomeAssistantState>> GetStatesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var request = CreateRequest(HttpMethod.Get, "api/states");
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken);
|
||||
using var response = await SendAsync(request, cancellationToken);
|
||||
await EnsureSuccessAsync(response, cancellationToken);
|
||||
return await response.Content.ReadFromJsonAsync<List<HomeAssistantState>>(cancellationToken: cancellationToken) ?? [];
|
||||
var states = await response.Content.ReadFromJsonAsync<List<HomeAssistantState>>(cancellationToken: cancellationToken) ?? [];
|
||||
await EnrichTemperatureUnitAsync(states, cancellationToken);
|
||||
return states;
|
||||
}
|
||||
|
||||
// 原生 climate 状态通常省略温度单位,它使用 HA 全局单位系统;不能把缺失直接当作摄氏。
|
||||
private async Task EnrichTemperatureUnitAsync(List<HomeAssistantState> states, CancellationToken cancellationToken)
|
||||
{
|
||||
var targets = states.Where(state => state.EntityId.StartsWith("climate.", StringComparison.OrdinalIgnoreCase) &&
|
||||
state.Attributes.ValueKind == JsonValueKind.Object &&
|
||||
!state.Attributes.TryGetProperty("temperature_unit", out _)).ToArray();
|
||||
if (targets.Length == 0) return;
|
||||
using var bounded = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
bounded.CancelAfter(TimeSpan.FromSeconds(2));
|
||||
try
|
||||
{
|
||||
using var request = CreateRequest(HttpMethod.Get, "api/config");
|
||||
using var response = await SendAsync(request, bounded.Token);
|
||||
await EnsureSuccessAsync(response, bounded.Token);
|
||||
using var config = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync(bounded.Token),
|
||||
cancellationToken: bounded.Token);
|
||||
if (!config.RootElement.TryGetProperty("unit_system", out var units) ||
|
||||
units.ValueKind != JsonValueKind.Object || !units.TryGetProperty("temperature", out var temperature) ||
|
||||
temperature.ValueKind != JsonValueKind.String) return;
|
||||
var unit = temperature.GetString();
|
||||
if (unit is not ("°C" or "°F")) return;
|
||||
foreach (var state in targets)
|
||||
{
|
||||
var attributes = state.Attributes.EnumerateObject().ToDictionary(property => property.Name,
|
||||
property => property.Value.Clone(), StringComparer.Ordinal);
|
||||
attributes["temperature_unit"] = JsonSerializer.SerializeToElement(unit);
|
||||
state.Attributes = JsonSerializer.SerializeToElement(attributes);
|
||||
}
|
||||
}
|
||||
catch (Exception) when (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
// 单位接口不可读时仍保留其他真实状态;空调温度功能按单位未知禁用,不猜测。
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
public async Task CallServiceAsync(
|
||||
@@ -33,10 +71,19 @@ public sealed class HomeAssistantClient
|
||||
{
|
||||
using var request = CreateRequest(HttpMethod.Post, $"api/services/{domain}/{service}");
|
||||
request.Content = JsonContent.Create(data);
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken);
|
||||
using var response = await SendAsync(request, cancellationToken);
|
||||
await EnsureSuccessAsync(response, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
try { return await _httpClient.SendAsync(request, cancellationToken); }
|
||||
catch (OperationCanceledException exception) when (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
throw new TimeoutException("Home Assistant 请求超时,未确认操作完成", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private HttpRequestMessage CreateRequest(HttpMethod method, string path)
|
||||
{
|
||||
if (!_options.Enabled)
|
||||
|
||||
Reference in New Issue
Block a user