Switchboard manual ⚡ Web flasher

Viewports

A viewport is a colour wall-mounted e-ink display, for example the reTerminal E1002 kitchen panel or a sign beside a meeting room door, running Switchboard Viewport. It pairs like a remote but registers as "type": "viewport". The device only draws. Its whole UI is a viewport layout (a “dashboard” in the API and data folder). It’s built on the Layouts page, before or after any display exists, and assigned to one or more displays on their Viewports page. A display without a layout shows “not set up”. Viewports saved before layouts were separate move into a layout of their own automatically at startup.

Building a viewport layout
The kitchen panel's layout in the builder: its screens and timing on the left; the picked screen's name, icon and template above its live preview, and each column's sections below.

The builder

Open a viewport layout from Layouts. On the left:

On the right, the picked screen. Its name, icon (its mark in the display’s footer) and template (Sidebar, Two, Three or Single columns) sit in one row just above its live preview. Under the preview, each column is a list of its sections, headed with its width on the 800-pixel panel (Sidebar · 250 px). Drag a section to reorder it within its column; its ⋯ menu moves it to another column, duplicates or removes it. Add a section opens the list of types. A meeting room or room finder screen has its own settings under the preview instead.

A section's settings in the drawer
Click a section and its settings open in a drawer at the side; the preview outlines it. Esc closes the drawer.

The layout’s column also has Thresholds (when values change colour: battery critical and low, an imminent departure, recent motion, the climate tolerance) and Start from or import, the displays using the layout, and the other layouts. Save keeps your changes, and displays pick them up at their next refresh; Discard throws them away.

As JSON. Any section can also be edited as JSON: its settings drawer has a JSON tab beside Settings. Edit as JSON in the layout’s side column edits the whole layout. Apply checks it on the server (unknown keys are dropped, missing ones get their defaults) and puts it in the builder; Save keeps it. Every key is described in the Viewport layout reference.

Status icons and “now” items can follow an entity’s heating state (@heating: calling, idle or off) instead of its state; see the heating state.

What a layout holds

Meeting room
Meeting room
Room finder
Room finder

Conditional sections. Any section can show only some of the time: Now playing only while one of its players is playing, or any section only while an entity matches (e.g. the alarm panel section only while armed). A hidden section is simply left out, so the rest of its column closes up; a calendar in the same column shrinks to fewer lines (Lines while a conditional section shows, default 2) to make room while it’s there. While a conditional section shows, the display wakes every few minutes (3 by default) to keep it current, and goes back to its normal refresh once it’s gone — a viewport is on battery, so a section appears at the next wake after its condition starts, not the instant it does. The server does all of this: the display only draws what it’s sent.

The layout builder shows a live 800×480 preview of each screen, in the panel’s six colours and with its own colour weather art, from Home Assistant’s current state and including unsaved changes. Start from or import loads the kitchen panel’s defaults or a meeting-room sign, or imports an existing panel’s own settings.

The server does all the evaluating. GET /api/viewports/me/state returns every screen’s finished values: colour indices (0 white, 1 black, 2 red, 3 yellow, 4 green, 5 blue), which icon to draw, alert sentences, times, countdowns and graph buckets. So the device needs no Home Assistant template sensors and no rules of its own. Per refresh, the server makes one GET /api/states for every entity. It adds only what the screens need beyond that — weather forecasts, calendar events, and one history request per energy graph — all in parallel. Calendar links are read by the server itself (each at most every 2 minutes), so a layout made only of meeting rooms on calendar links needs no Home Assistant at all.

Energy graph data. A series can be a power sensor (averaged per bar) or an energy meter (differenced per bar). Each bar of use is split by source: grid import first (it’s metered), then metered battery discharge, then solar up to what it produced. Anything left is counted as battery, so a house with a battery but no battery sensor still adds up. The forecast is read from an entity attribute holding an hourly or half-hourly list, as Solcast (detailedForecast) and Open-Meteo Solar Forecast provide.

Refreshes. Each screen carries its own ETag, and ?screen=<id> answers a bodyless 304 when that screen is unchanged, so the device can skip its 15–20 s panel refresh. refreshInSec (and the X-Refresh-In header) says when to wake next: the refresh interval, or sooner when a meeting starts or ends (less the layout’s Change the sign minutes, with the screen sent as it will be then), and the quiet-hours interval overnight and at weekends (quiet in the body, X-Quiet: 1). The bundle’s layout leaves out calendar links: they’re secrets, and the display never needs them. The bundle lists every icon the screens can show, so the device can fetch them once from /api/icons/mdi/<name> and cache them.

Live and passive content. Most of what a device shows is passive: it only changes when someone presses a button, so it’s drawn from cache and fetched on each wake. Some content is live: a player that’s playing, blinds that are moving, departures counting down.

A viewport's page
A display's own page: its health and the layout it shows.

Health. Any paired device — remote or viewport — can send X-Battery, X-Temperature, X-Humidity, X-RSSI, X-Firmware and X-Board headers on its requests; the Home page and the device’s page show them, warn on a low battery or weak signal, and learn how many days the battery has left (Battery life). Both firmwares send them on every request to the server.