/** * DsmrPanel — shows the latest parsed DSMR telegram (the source of our metering data). * * This is the "ground truth" view: it fetches GET /api/energy/dsmr/latest and * renders the full parsed frame as a key/value table, so you can confirm at a * glance whether DSMR ingest is actually landing data (without reading the DB). * * - Auto-refresh toggle (default on) so new telegrams appear without a manual reload. * - Loading / error / empty states; "empty" explains the likely cause. * - Null phase values are shown as "—" (kept verbatim in the payload). */ import { useState } from 'react' import { Stack, Group, Text, Loader, Center, Alert, Paper, Table, ScrollArea, Switch, Divider, Badge, } from '@mantine/core' import { useDsmrLatest } from './hooks' import { formatLocalDateTime } from '../utils/datetime' /** Default auto-refresh interval (ms) — DSMR ingest stores ~one row per 10 s. */ const AUTO_REFRESH_INTERVAL_MS = 10_000 function formatValue(value: unknown): string { if (value === null || value === undefined) return '—' if (typeof value === 'object') return JSON.stringify(value) return String(value) } export function DsmrPanel() { const [autoRefresh, setAutoRefresh] = useState(true) const latestQuery = useDsmrLatest( autoRefresh ? { refetchIntervalMs: AUTO_REFRESH_INTERVAL_MS } : undefined, ) return (
Latest DSMR reading The most recent parsed telegram persisted to dsmr_reading.
setAutoRefresh(e.currentTarget.checked)} size="sm" data-testid="dsmr-auto-refresh-switch" />
) } interface DsmrContentProps { isLoading: boolean isError: boolean data: | { found: boolean; recorded_at?: string | null; payload?: Record | null } | undefined } function DsmrContent({ isLoading, isError, data }: DsmrContentProps) { if (isLoading) { return (
) } if (isError || !data) { return ( Failed to load the latest DSMR reading. ) } if (!data.found || !data.payload) { return ( No DSMR data yet. Enable DSMR ingest in Config, make sure MQTT is connected, and confirm the DSMR Reader is publishing to the configured topic (default dsmr/json). Rows are stored about once every 10 seconds. ) } const payload = data.payload const keys = Object.keys(payload).sort() return ( {keys.length} fields {data.recorded_at && ( recorded at {formatLocalDateTime(data.recorded_at)} )} Field Value {keys.map((key) => ( {key} {formatValue(payload[key])} ))}
) }