Admin write-CRUD, Home Assistant connector config, wiring audit
ci / build-test (push) Successful in 1m19s
ci / build-test (push) Successful in 1m19s
Three requested phases.
1) Admin section (SDD §8.7) — MudBlazor inline-dialog CRUD, consistent pattern,
delete guards, snackbar feedback, shared Confirm helper:
- Energy types: create/edit/delete (blocks delete when meters reference it).
- Meters: create/edit/delete; recomputes consumption when mode/baseline
changes (NormalizationService over a fresh factory context, in a tx);
delete cascades data (consumption+readings are Restrict → removed first).
- A meter's ingest sources: manage on the meter-detail Sources tab
(add/edit/delete MQTT/Tasmota/HA sources with typed config).
- Tariffs: full CRUD (scope/component/value/validity).
- Cost categories: CRUD + member management (meter or energy-type members).
- Connectors: ingestion_endpoint CRUD (MQTT broker + Home Assistant);
secrets referenced by env-var name only, never stored.
- Settings: read-only effective-config view (settings are env-driven and
reproducible, so an editable form would change nothing — kept honest).
PV role is now editable on meters (MeterMeta.SetRole can clear a role).
2) Read Home Assistant — extracted a shared public HaEndpointConfig (was a
private record in the worker), added HaConnectionTester (powers the connector
"Test connection": checks base URL + env-resolved token, optionally reads one
entity). Configuring an HA connector + an HA source on a meter drives the
existing REST-poll worker end to end. (WebSocket push stays a future
optimization; REST poll already reads HA.)
3) Wiring/placeholder audit — swept every OnClick/Href: all handlers are real,
all internal links resolve to real routes, no TODO/stub/placeholder code.
Fixed one genuine gap: MainLayout had no drawer toggle, so the nav was
unreachable on narrow screens — added a hamburger button.
Tests: +6 (MeterMeta.SetRole role-removal; HaConnectionTester fail-closed
guard branches with a throwing HttpClientFactory proving no network on bad
config); render test now covers all admin routes. 69 Core + 45 Integration =
114 green. Live-verified in Docker: all admin pages 200, drawer toggle present,
Settings shows real effective config.
Claude-Session: https://claude.ai/code/session_01Lz2RqAsnQhetqWNoCDfexK
This commit is contained in:
@@ -14,13 +14,11 @@ public sealed record EventRow(DateTimeOffset Time, MeterEventType Type, double?
|
||||
/// <summary>A tariff applicable to the meter (own / energy-type / global scope), for the timeline.</summary>
|
||||
public sealed record TariffRow(TariffScope Scope, int? ScopeId, TariffComponent Component, double Value, string Unit, DateOnly ValidFrom, DateOnly? ValidTo);
|
||||
|
||||
/// <summary>Source binding + live status (last-seen / last value / last status).</summary>
|
||||
public sealed record SourceRow(SourceType Type, bool IsEnabled, DateTimeOffset? LastSeenAt, double? LastValue, string? LastStatus, string Config);
|
||||
|
||||
/// <summary>
|
||||
/// The meter-detail read model (SDD §8.6): identity, register span, totals, recent raw readings
|
||||
/// and normalized consumption (measured-vs-estimated markers via quality), source status, the
|
||||
/// applicable tariff timeline, and lifecycle events (swaps/deliveries/corrections).
|
||||
/// and normalized consumption (measured-vs-estimated markers via quality), the applicable tariff
|
||||
/// timeline, and lifecycle events (swaps/deliveries/corrections). Source management loads the
|
||||
/// source entities directly (they are editable), so it is not part of this read model.
|
||||
/// </summary>
|
||||
public sealed record MeterDetailView(
|
||||
int Id,
|
||||
@@ -46,4 +44,4 @@ public sealed record MeterDetailView(
|
||||
IReadOnlyList<ConsumptionDetailRow> RecentConsumption,
|
||||
IReadOnlyList<EventRow> Events,
|
||||
IReadOnlyList<TariffRow> Tariffs,
|
||||
IReadOnlyList<SourceRow> Sources);
|
||||
int SourceCount);
|
||||
|
||||
Reference in New Issue
Block a user