@page "/" @using InverterMon.Shared.Models @using System.Text.Json @implements IDisposable Dashboard @if (status is not null) {
@if (status?.GridUsageWatts > 100) {
Grid Usage
@status?.GridUsageWatts
W
}
Output Load
@status?.LoadWatts
W
@RoundToWholeNumber(status?.OutputVoltage)
V
@(status?.WorkingMode)
@RoundToOneDecimal(status?.LoadCurrent)
A
@status?.PVInputWatt
W
@RoundToWholeNumber(status?.PVInputVoltage)
V
@RoundToOneDecimal(status?.PVInputCurrent)
A
Charging
@status?.BatteryChargeWatts
W
@RoundToOneDecimal(status?.BatteryChargeCurrent)
A
Voltage
@RoundToOneDecimal(status?.BatteryVoltage)
V
@GetCRate() C
Discharging
@status?.BatteryDischargeWatts
W
@status?.BatteryDischargeCurrent
A
} @code{ private static event Action? onStatusUpdated; private static event Action? onStatusRetrievalError; private static InverterStatus? status; protected override void OnInitialized() { onStatusUpdated += UpdateState; onStatusRetrievalError += NullifyStatus; } private void NullifyStatus() { status = null; StateHasChanged(); } private void UpdateState(InverterStatus? s) { status = s; StateHasChanged(); } public void Dispose() { onStatusUpdated -= UpdateState; onStatusRetrievalError -= NullifyStatus; } private static double RoundToWholeNumber(double? val) => Math.Round(val ?? 0, 0); private static double RoundToOneDecimal(double? val) => Math.Round(val ?? 0, 1); public static async Task StartStatusStreaming(string basePath) { //note: only reason we have a full-time stream download is because there's a bug in // blazor-wasm that doesn't close the fetch http requests when streaming is involved. // and it leads to a new stream download being created everytime a page is initialized. // which leads to a memory leak/ connection exhaustion. using var client = new HttpClient(); client.BaseAddress = new(basePath); client.Timeout = TimeSpan.FromSeconds(5); var retryDelay = 1000; while (true) { try { using var request = new HttpRequestMessage(HttpMethod.Get, "api/status"); request.SetBrowserResponseStreamingEnabled(true); using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); response.EnsureSuccessStatusCode(); using var stream = await response.Content.ReadAsStreamAsync(); await foreach (var s in JsonSerializer.DeserializeAsyncEnumerable( stream, new JsonSerializerOptions { PropertyNameCaseInsensitive = true, DefaultBufferSize = 64 })) { onStatusUpdated?.Invoke(s); retryDelay = 1000; } } catch (Exception) { onStatusRetrievalError?.Invoke(); await Task.Delay(retryDelay); retryDelay += 500; } } } private static double GetCRate() { if (status?.BatteryChargeCRate > 0) return Math.Round(status.BatteryChargeCRate, 2); if (status?.BatteryDischargeCRate > 0) return Math.Round(status.BatteryDischargeCRate, 2); return 0; } }