Skip to content

Dashboard internals ​

The dashboard is a grid of panels: solar, a combined battery panel (state of charge plus charge/discharge power), grid, and home consumption. The title sits top centre with the outdoor temperature top left and the gear top right; the connection state and last-updated time sit along the bottom, with a link to the source on GitHub in the lower right. Battery and grid power carry a direction arrow beside the value, drawn in a darker shade of the metric's colour so the direction reads separately from the number: up for discharging or exporting, down for charging or importing, nothing when idle. Tapping the title reloads the app, which is the quick fix when an iOS home-screen instance has gone stale. Tap any panel for a fullscreen readout suitable for a wall display or iPad; the app icon sits top centre there too and reloads on tap. Clicking the reading itself cycles its layout (a plain figure and a compact icon-and-value glyph, where battery and grid power swap their icon for the direction arrow while flowing); clicking around it returns to the dashboard. On the battery panel the left half opens the charge reading and the right half opens the power flow. The charge reading is a percentage by default; switching the charge display in Settings → Battery shows it instead as stored energy in kWh (state of charge times the usable capacity, falling back to the rated register), to one decimal place. The colour, battery icon, and fill bar stay tied to the state of charge in either mode, so only the headline number and its unit change, and the same switch carries into the panel's fullscreen view.

Turning on Show Smart Port on the dashboard (Settings → Smart Port; it only takes effect while Smart Port loads are detected) layers the gateway's controlled loads over the home panel. The combined draw sits directly above the home total at the same size in a dimmed shade: dimmed home-white while drawing, and a darker grey at zero, so neither state can be mistaken for the ordinary idle colour on a zero reading. The panel title reads Home • Smart Port, and the home fullscreen's click-cycle continues past the two standard layouts into a pair of views per detected load, a titled readout carrying the load's name and its Drawing/Idle state, then a plug-icon glyph, so a wall display can be parked on any single load. Below 768px the panel header condenses so both titles hold one line: the words "Smart Port" drop away leaving Home • and the plug icon, and the cost label (when the tariff estimate shares the panel) shortens to "Today's Estimate" or "Hourly Estimate".

The battery estimate ​

When the battery has been moving steadily in one direction, an estimate line fades in under the charge bar: "Full in 1h 45m ~3:05 pm" while charging (right-aligned, under the power side) or "Empty in 3h 25m ~9:05 am" while discharging (left-aligned, under the percentage side). It takes the most recent unbroken run of same-direction samples (battery power beyond a ±100 W noise floor) and shows "Estimating…" while that run is still building. Once the usable capacity is known (the Settings → Battery value, or the rated register it falls back to), the projection comes from the battery's power flow rather than its state of charge: charge or discharge watts over capacity give a percent-per-hour rate, weighted toward the most recent readings with a 90-second time constant, so a step in power moves the estimate within a minute or two instead of waiting for the slow charge trend to catch up; that lag was the old version's main weakness, since a 0.1%-resolution state of charge takes minutes to reveal a rate change. With no capacity to scale by, it falls back to fitting a line to the state of charge over the run, and only then waits for 0.35% of charge travel so it doesn't guess from quantisation noise. Either way the line settles once the run spans a minute with at least five samples. Runs break on opposite-direction samples and on gaps larger than three times the recent sample cadence, so a poll-schedule change or a disconnect can't splice two unrelated trends together. Projections beyond 24 hours are hidden, times are rounded to five minutes so the numbers don't churn with every poll, and the line lives in a reserved slot that holds its height whether it carries the projection, an "Estimating…" note, or nothing, so the layout never shifts. When the battery is simply holding, neither charging nor discharging past the noise floor, the slot stays empty rather than labelling the state. Setting a reserve charge in Settings → Battery counts the discharge estimate down to that floor instead of zero and labels it "Reserve in …", since the system won't drain below it. The reserve defaults to off until you set it; /api/snapshot still carries the energy still to flow as energyToGoKwh for anything reading the API directly.

Responsive layout ​

The layout adapts from desktop down to a phone, where the panels stack into a single column sized to fill the screen: the four rows divide the height with the battery panel weighted a little larger, the values size down a step, the page margins, panel padding, and grid gaps each tighten a notch, and on a short screen the panels and readings compact further so all four still fit without scrolling. A phone held in landscape drops the battery bar to give the panels room. In the other direction, on landscape screens 1024px and wider the values and their arrows scale fluidly with the viewport, capped so the widest reading still fits its panel (the battery panel, holding two readings, caps a little earlier, and also shrinks to fit at narrow two-column widths). A reading-size slider (50–150%, default 100%, with a reset to default) multiplies that large-screen growth without touching phone sizes.

The Trends switch in the lower left swaps the grid for a chart of every metric on one time axis at its own bookmarkable /trends URL: the four power flows as lines with battery charge as a green area behind them, and a legend above the chart that totals each metric across the selected window: kWh for the four flows (signed: import/charge positive, export/discharge negative; a window averaging under 50 W reads a plain 0.00), the net change for battery percent, each in its own metric colour so the rows stay distinct, led by a chip naming the window (e.g. "1h totals"), ordered to match the panels (battery percent and power, home, solar, grid). Hovering or dragging the chart switches that chip to a clock at the scrubbed time and the legend to that instant's live values, where grid and battery power show the same dimmed direction arrow as the dashboard and a zero reading greys out. A computed day/night sky backdrop tints the plot by sun altitude across the visible window using the weather location's coordinates (no external API calls), with sunrise and sunset labelled in the footer axis at the exact horizon-crossing times. With Smart Port showing on the dashboard, a Smart Port line joins the chart and legend after home, in home's dimmed shade, carrying the combined draw of the detected loads; it tallies, scrubs, and solos like the rest, and samples recorded before the bridge tracked it simply leave a gap.

Hover over the chart with a mouse, or press and drag on a touch screen, to scrub back through the window: a cursor line snaps to the nearest sample and the clock, the legend, and the header temperature switch to the values at that moment until you let go. The temperature is recorded with each sample, so older history from before this was tracked scrubs as —. Tap a legend reading while all lines are showing to solo it; tap the only line left to bring them all back; otherwise taps toggle lines one at a time. Pills in the top right set the window. The fixed short set runs from the last minute to the last 24 hours; wider pills (2 days through 90) appear as the stored history grows past each one, so the widest pill tracks how far back your retention window actually reaches. A window at or under 24 hours at the live edge is sliced straight from the in-memory buffer and slides as new polls land; anything wider, or panned into the past, is fetched as a server-downsampled slice (/api/history with a bucket interval), so a multi-day window stays a couple of thousand points rather than a hundred thousand. Below the chart, earlier/later controls and a Live button walk the window back through retention a window-width at a time and snap back to now; on a device with a keyboard, ← and → pan while ↑ and ↓ change the zoom. Either way the chart breaks the line across polling gaps rather than interpolating and downsamples with min/max buckets so spikes stay visible. Windows too short for the active polling rate grey out (a one-minute window is no use on a one-minute poll schedule) and tapping one explains why; if the schedule slows while you're on a short window, the view steps up to the nearest usable one. The switch, the chosen window, and any hidden lines are all remembered in the browser, so opening the plain dashboard URL lands back on trends if that's where you left it.

How much history the chart can reach back over is set under Settings → History as a retention window in days (default 7); the same section downloads the stored readings as CSV or JSON, at full resolution or thinned to one row per minute, five minutes, or fifteen, and shows the held sample count and span. The download buttons fetch GET /api/history/export and hand the response to the browser as a blob: object URL rather than linking straight at the endpoint: a chrome-less home-screen PWA ignores both the download attribute and the server's Content-Disposition: attachment on a top-level navigation, so an <a href> would replace the whole app with an in-place file preview and strand the user with no back button, whereas a blob download routes through the dismissable preview sheet that returns to the app. A minimum visible delay keeps the button's "Preparing…" state on screen long enough to read on instant exports, matching the gateway and weather tests. The readings persist in a SQLite database (history.db) written one row per poll, so they survive restarts without the write cost of rewriting a snapshot file.

Colours and theming ​

Colours follow one rule across the dashboard, the fullscreen readouts, and the chart: each metric owns a hue (solar amber, home white, grid blue, battery power violet, battery charge green) and the two signed flows shade that hue by direction, brighter when supplying the home (light cyan grid import, light fuchsia battery discharge) and deeper when absorbing surplus (deep blue grid export, deep violet battery charge). Each chart line switches shade as it crosses the zero line, so you can read direction straight off the chart without a tooltip.

That's only the default palette. Settings is split into sections: the Dashboard section renames the dashboard (the title in the header and the browser tab; blank restores the default), sets the power unit (kW with a chosen 0–3 decimal places, or whole watts) used for every power reading across the dashboard, fullscreen, and trends, and scales the metric values on large screens with a reading-size slider (50–150%, default 100%, with a reset); the Battery section holds the usable capacity and reserve charge that sharpen the time-to-empty estimate; and the Theme section themes the whole colour system. The header temperature carries its °C or °F per the weather units. Pick a preset (Default, Ember, Ocean, Mono) or edit any of the colour slots with a swatch picker or hex field: the solar ramp's low/mid/high stops, the battery level's healthy/low/critical thresholds, the import/export and charge/discharge pairs, home, the trend-line accent, and the idle grey. A preview strip above the pickers shows sample readings and direction arrows in the working palette before you save, and editing any slot flips the theme to Custom. Like the rest of settings it lives in settings.json on the server, so every device pointed at the bridge sees the same title and palette, and the direction arrows re-derive their darker shade from whatever you pick.

URLs ​

Every view has its own URL (/trends, /metric/solar, /metric/battery-percent, /settings, and so on), so you can bookmark a single metric or point a kiosk straight at it.

Weather ​

The header temperature is the current outdoor temperature for your location, pulled from Open-Meteo. The first run resolves your location once from the server's public IP (a coarse, city-level lookup via ip-api.com); set LATITUDE/LONGITUDE to pin it, or turn it off in settings. It needs outbound internet but is independent of the gateway.

MIT licensed. Not affiliated with or endorsed by Sigenergy, Apple, Google, or Cloudflare.