using System.Text.Json;
using System.Text.Json.Serialization;
using MeterVault.Infrastructure.Security;
namespace MeterVault.Infrastructure.Ingestion;
///
/// The parsed JSON for a Home Assistant
/// connection (SDD §6.2). The long-lived token is never held here as plaintext (SDD §6.4): either
/// names an environment variable resolved at runtime, or
/// holds it encrypted under the app's data-protection key ring.
///
public sealed record HaEndpointConfig
{
private static readonly JsonSerializerOptions Options = new()
{
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
/// Base URL of the Home Assistant instance, e.g. http://homeassistant.local:8123.
public string? BaseUrl { get; init; }
/// Name of the environment variable holding the long-lived access token.
public string? TokenEnv { get; init; }
///
/// The long-lived access token, encrypted by . Set when
/// the operator typed the token into the admin UI instead of naming an environment variable.
///
public string? TokenEnc { get; init; }
///
/// When true, a persistent WebSocket subscription pushes state changes in real time
/// (); when false (default) the REST poll worker
/// samples on each source's interval. An endpoint is handled by exactly one of the two.
///
public bool UseWebSocket { get; init; }
public static HaEndpointConfig Parse(string? json)
{
if (string.IsNullOrWhiteSpace(json))
{
return new HaEndpointConfig();
}
try
{
return JsonSerializer.Deserialize(json, Options) ?? new HaEndpointConfig();
}
catch (JsonException)
{
return new HaEndpointConfig();
}
}
public string ToJson() => JsonSerializer.Serialize(this, Options);
///
/// Resolves the token: the encrypted value when one was entered directly, otherwise the
/// referenced environment variable. Null when neither yields anything.
///
public string? ResolveToken(SecretProtector? protector = null) =>
EndpointSecret.Resolve(TokenEnc, TokenEnv, protector);
}