@overvis/hmi-workspace-ui
Цей контент ще не доступний вашою мовою.
Shared React library for the workspace shell, setup UI, forms, tRPC client factory, and dashboard mount. Two hosts consume it:
| Host | Auth / entry |
|---|---|
| Overvis HMI cloud application | Cookie session; mounts createWorkspaceRoutes |
| CLI-scaffolded local apps | LocalWorkspaceApp with apiUrl / apiKey / workspaceId |
Hosts pass credentials and URLs. The package supplies translated UI text.
Local pull workflow:
Pull and scaffold a local workspace.
Contracts: @overvis/hmi-shared.
Requirements
Section titled “Requirements”Build tooling (hmi build) runs on Node.js 22.13+ on the 22.x line or 23.4+. The browser library
has these peer dependencies, in the ranges the package declares: react, react-dom,
@tanstack/react-form, @tanstack/react-query, @tanstack/react-router, @trpc/client,
@trpc/react-query, and @trpc/server. Dashboard builds run in @overvis/hmi-cli (hmi build),
which needs Vite, Tailwind CSS, @vitejs/plugin-react, @tailwindcss/vite, and TypeScript; a
pulled workspace lists them in its package.json. Browser-only consumers do not need them.
LocalWorkspaceApp creates its own router, query, theme, and workspace providers. Its apiUrl is
the API base URL without /trpc. The API accepts browser requests only from its own web app, so a
host proxies /trpc and /static (published dashboards, rendered reports) on its own origin and
passes /. The apiKey is readable by everyone who can open the host. The steps to mount it are
in Embed the workspace UI in a React app.
Package exports
Section titled “Package exports”| Export | Role |
|---|---|
. | Dashboard kit: trpc, live parameters, forms, report runtime, graphics |
./ui | UI primitive catalog and cn |
./host | Application shell: workspace app, layout, pages, chrome, theme, tRPC factory |
./i18n | Locale initialization, selection, and subscribing translation hooks |
./parameter-values | Live-value state and pure projections without React UI |
./dashboard-bindings | Missing dashboard binding checks without the full package entry point |
./styles.css | Tailwind 4 + design tokens, which the host imports once |
./styles-reference.css | Class tokens without preflight or fonts, read by the dashboard build |
Peers: React 19, TanStack Form/Query/Router, tRPC client / react-query / server. Depends on
@overvis/hmi-shared for PublicAppRouter, dashboard contracts, and schemas.
Dashboards import the kit (.), the primitives (./ui), and translation hooks (./i18n), which
the host supplies at run time; ./host is for host applications only. The full list of what a
dashboard can import is in the dashboard host contract.
. (dashboard kit)
Section titled “. (dashboard kit)”- API:
trpc, the typed React Query client bound to the host’s providers. - Live parameters:
ParameterValuesProvider,useParameterValues,useParameterValue,useParameterValueActions,useParameterValueHistory,useParameterValueHistories,useStoredParameterHistory,storedHistoryWindows,withLiveTail,liveTailPoints,useStoredWindowExtremes,rangeExtremes,useDistinctParameterNames,useParameterMetadata,parameterMetadata,isParameterRecorded,isCurrentParameterValue,isParameterValuePending,parameterValueNumber,parameterValueBoolean,parameterValueFlag,parameterValueComparable,parameterValueTime,parameterStatesStaleSince,combineParameterValuesDemands,ParameterLiveControls,ParameterValueDisplay,ParameterBooleanBadge,ParameterDiagnostics,ParameterWriteReviewDialog, andparameterWriteOutcome. - Dashboard pages:
useDashboardNavigation,DashboardLink,DashboardNavigationLink(aDashboardLinkthat takes the navigation as a prop),DashboardNavigateOptions(replace, andkeepScrollto keep the scroll position on another path),DashboardPageTabs,DashboardPageNotFound,dashboardPageAt,dashboardPathMatches,useAnchorScroll, andusePageRotationwithnextPageId.useAnchorScroll(container, anchor, { key })scrolls to the element whose id isanchorand keeps it in view while content above it loads, until the user scrolls or five seconds pass.DashboardPageTabskeeps an optionalsearchon its links; a tab’scurrentmarks the current page where its path alone cannot tell.usePageRotation({ rotateSeconds, currentId, paused, onRotate })callsonRotateeveryrotateSecondsfor wall displays, except whilepaused, while a dialog is open and for a minute after user input. - Drag and drop:
SortableGrid,SortableGroup, andSortableItemorder items within and between groups, such as tiles in the sections of a page.SortableGridtakes the groups as item ID lists and callsonMoveonce on drop with where the item came from and went, andbefore, the order shown when the drag began (the baseline to check the move against); the dropped order stays untilgroupschanges, or until a promise returned byonMoverejects;childrenrenders the groups in their order during a drag.SortableItempasseshandleReffor its drag handle. Keyboard users pick an item up by its handle with Space or Enter, move it with the arrow keys and drop it with Space or Enter; each step is announced withitemLabelandgroupLabel. They are in the import map, so a dashboard that uses them bundles no drag-and-drop library. - Device API:
useDeviceApi(the device operations the viewer may call, withcanCallfor per-key roles),useDeviceApiRead(shared, rate-aware reads ofreadoperations;refreshMsof at leastDEVICE_API_MIN_REFRESH_MS),useDeviceApiAction(calls, usually ofwriteoperations), anddeviceApiErrorText; see device API. - Device dashboards:
DevicePresetLayout,useDevicePresetValues,splitEndpointDiagnostic,useParameterUnit,useDeviceReadings,DevicePresetHeader,DeviceEndpointUnavailable,DeviceCommandButton,DeviceRelayTest,ParameterStateText,ParameterTrendChart,parameterTrendSeriesList,DeviceSettingsSection,DeviceSettingsPage,deviceSettingNumber,deviceSettingChoice,defaultDeviceSettingsWritePlan,DeviceSettingOutcome,ThresholdGauge,WindowGauge,formatQuantity,formatFixedNumber,isUnsupportedParameterValue,isUnsupportedParameterDiagnostic,DevicePanel,DeviceStatusBar,DeviceStatusBadge,DeviceKeyFigure,DevicePhaseTable,DeviceReadingList,DeviceSeriesSwatch, and the vendor-neutralColdRoomCardwithcoldRoomBindingsType,coldRoomParameterIds,coldRoomBoundParameterIds,mapColdRoomBindings(DroppedColdRoomBinding), itscoldRoom*model functions,ColdRoomCommands,coldRoomStatusLabelandcoldRoomCommandLabel. - Reports:
ReportDataProvider,ReportView,ReportWidget,ReportTimeNav,ReportCommentsFooter,useReportSession,ChartRepresentation,TableRepresentation,TimeBucketsRepresentation,HistogramRepresentation,CalendarRepresentation,HourlyMatrixRepresentation,StateDurationRepresentation,SankeyRepresentation, andGeneratedReport,GeneratedReportSection,GeneratedReportPendinganduseFrozenReportParametersfor charts a dashboard generates (report components). - Counters:
useCounterTotals,CounterTotal,CounterChange,CounterBars,CounterPeriodSelect,useCounterPeriod,useResolvedCounterPeriod,useDashboardTimeZone,useClosedMinute,useReadingPeak,counterBucketCadence,counterPeriodLabel,counterComparisonLabel,counterTotalValue,counterSlotUnit,counterShare,formatCounterShare,commonCounterUnit,rankByCounterTotal,CounterTotalsNotice,CounterBindingWarnings,PeriodSettingsFields,counterBindingErrorandnumericBindingError(see Counter totals). - Dashboard actions:
useQuickReportanduseDashboardQuickReportfor temporary history reports,useDashboardConfigurationMutationfor validated read-modify-write configuration updates,useDashboardNamefor the dashboard’s own name in the UI language (null while loading),parameterStaleSinceTextandparameterStaleNotefor the “Stale · time” label of a value kept from an earlier read,DashboardConfigurationRequired,DashboardConfigurationRenameDialog, anduseWorkspace. - Documents:
DocumentViewrenders a document from@overvis/hmi-shared/document. It takesdocument,workspaceId,locale, anddashboardId, the dashboard showing it, which serves the document’s images. It shows the pages as dashboard subpages with their inputs, and values read from parameters, history, devices and the HMI API. It evaluates formulas in the browser and shows ”—” for values not known.DashboardDocument,DocumentLead,DocumentSection,DocumentColumns,DocumentProse,DocumentMetric,DocumentFigure,DocumentTable,DocumentChartandDocumentChartDataTypecompose documents in code; see document components. - Forms:
useAppForm,withForm,withFieldGroup,FormRoot,mutationFormValidators, field helpers (includingParameterPickerFieldandDashboardPickerField),mapTrpcToFormErrors,formOnlyError. - API errors:
apiErrorMessage(translated text for any failed call) andapiErrorCode;parameterRuntimeErrorTextfor the error of one parameter read or write result;formulaDiagnosticText/formulaProblemText(with the place in the formula) andautomationDiagnosticTextfor formula and automation diagnostics. - Graphics:
DashboardGraphics,defineGraphicsSymbol,useGraphicsSymbolViewport,useGraphicsSymbolAction,builtinGraphicsSymbols. - Maps:
readLatLng,useMapsBrowserConfig,useMapsReverseGeocode. The Maps hooks andDashboardLocationPickeracceptdashboardId: nullfor workspace settings (editor/admin/owner access); a dashboard ID retains dashboard access checks and usage attribution. The picker uses a global view when empty;defaultCenteranddefaultZoomoverride that fallback. Saved locations and context markers take precedence. - Alarms:
AlarmListshows a workspace’s active alarms live, unacknowledged first, filtered bysourceIds,keyPrefixorparameterIds(parameters the raising rule or automation reads now); operators and above acknowledge and shelve from it.compactdrops message bodies and source names. Inside the workspace it shares the host’s single alarm subscription. See Alarms. - Fleets and sites:
FleetOverview,FleetMap,FleetAlarmList,SiteStatusBadge,SiteReadings,SiteReadingMetric,SiteReadingValue,PhaseValues,SiteReadingCell,SiteModeBadge,SiteCommandButtons,SiteGroupCommandConfirmation,SiteGroupCommandResults,buildSiteGroupCommand,siteControlMode,EquipmentOperations,SiteEquipmentView,SiteAlarmList,SiteActiveAlarmBadges,SiteEquipmentSetup,SiteItemActions,overdueEquipment,dueDateParameterIds,FleetRoutes,matchSitePath,SitePageHeader,useSiteSetup,newSiteLocation,useSiteEditor,SiteSection,SiteBindingEditor,SiteSlotFields,SitePlacementFields,SiteStarterFields,CommandStepsField,BoardEditor,BoardSettingsFields,FleetOrderEditor,SiteDuplicateDialog,SiteFigure,SiteIndicatorLights,useParameterNames,evaluateSiteStatus,siteStatusParameterIds,siteAttentionParameterIds,activeAlarmsBySeverity,useNow,useIsMobile,useMediaQuery, and the generated-drawing helpers; see Fleet and site blocks. - Assistant page summary:
usePublishDashboardSummary,usePublishSiteSummary,buildFleetSummary,buildSiteSummary,activeSiteAlarms,useReadParameterValuesOnce; see Page summary.
The shadcn/Base UI component catalog, including form controls, navigation, responsive layout,
overlays, messaging, the shared PageTitle, Chart, composed DataTable, DateTimePicker, the
native SVG Sparkline, GaugeBar and Gauge, SvgDiagram, Toaster, useToast,
MessageHistoryProvider, MessageScopeProvider, MessageHistoryBell, and
showNotificationToast, and the cn class-merging utility. Sparkline’s fixed-window mode
buckets readings by time, disconnects sampling gaps above a caller-provided threshold, and supports
a fixed vertical range and reference line. Gauge is a half-circle scale from 0 to max with an
optional marker tick, such as a peak; gaugeScaleMax rounds a value up to 1, 2 or 5 times a power
of ten for its scale. It takes already formatted texts.
./ui also re-exports the Recharts parts that compose with the shadcn ChartContainer:
BarChart, LineChart, AreaChart, ComposedChart, Bar, Line, Area, XAxis, YAxis,
CartesianGrid, ReferenceLine, and Rectangle (for a custom Bar shape), with their prop
types BarProps, BarShapeProps, LineProps, AreaProps, XAxisProps, YAxisProps,
CartesianGridProps, ReferenceLineProps, and RectangleProps. They come from the exact
recharts version pinned in this package’s dependencies, so their props follow that version and
a Recharts upgrade is a change to this public API. Dashboards build charts from these exports; the
dashboard runtime does not serve recharts itself, and Recharts parts outside this list are not
available to dashboards.
NoticeCard is the one problem and status card for dashboards and pages. severity (error,
warning, info) sets a tinted icon badge, an accent edge and the --destructive, --warning or
--info color token; an error is announced as role="alert", the others as role="status".
title is a short headline and description a plain-language explanation; subject names what
the notice concerns (a device) above the title. details ({ label, value }[]) lists technical
facts under a collapsed “Technical details” disclosure (detailsOpen expands it). primaryAction
is the one filled button; secondaryActions are outlined. An action with href or render (such
as a router link) renders as a link; while disabled or pending, it renders as a disabled
button. onRetry adds Retry (with retryLabel to rename it), which leads unless primaryAction
is set; it shows a pending state while retrying is true or while the promise onRetry returns is
unsettled. actions appends custom elements such as a DeviceCommandButton, and children adds
body content under the description. size="compact" fits panels and narrow sections.
<NoticeCard title={copy.loadError} description={copy.loadErrorHint} onRetry={() => query.refetch()} retrying={query.isFetching}/>./host
Section titled “./host”Hosts with their own router pass parseRouterSearch and stringifyRouterSearch from ./host to
it, so dashboard subpage search parameters round-trip. The host provides the maps API keys.
- Workspace:
LocalWorkspaceApp,WorkspaceLayout,WorkspaceHostProvider,useWorkspaceHost,DashboardPage,DashboardNavigationProvider(the dashboard scope ofuseDashboardNavigationfor a dashboard component rendered outsideDashboardPage),ReportPage, setup pages and their route dialogs,NoAccessPage,WorkspaceSuspendedPage(given the suspendedworkspace, it explains each of itssuspensionReasons), error helpers, mobile nav,WorkspacePullInstructions,AuditHistoryPage,CheckpointRevertPreview(the preview of reverting a workspace to a checkpoint, with the on-site MC252 confirmations; itsonChangegets aCheckpointRevertControlwhoserevert()reverts exactly the previewed setup once nothing blocks it, andnullwhile something does).SetupParametersPageandReportPagepublish the current parameter-tree selection and report session (UiPageState) to the page-state store that eachWorkspaceHostProviderowns.DashboardPagepublishes the mounted dashboard: its ID, title, location inside the dashboard, and the summary its current page publishes, ornull. Inside the provider,useUiPageStateReader()returns a stable async reader: it resolves to the live state of the one publishing page, ornullwhen no page or more than one page publishes. Outside aWorkspaceHostProvider, pages publish nothing and the reader always yieldsnull. - Routes:
createWorkspaceRoutes(workspaceRoute, options?)returns the TanStack Router code routes of one workspace; see Workspace routes. - Setup editors: create and edit forms use dialogs:
SetupParameterFormDialog,SetupEndpointCreateDialog,SetupEndpointEditDialog,SetupDashboardCreateDialog,SetupDashboardCreateFromReportDialog,SetupDashboardSettingsDialog, andSetupReportFormDialog. Closing navigates back to the section list;SetupParameterFormDialog,SetupEndpointEditDialog,SetupDashboardSettingsDialog, andSetupReportFormDialogacceptonCloseto replace that.DashboardPageopens settings over the dashboard and stays there after saving or closing.DashboardCopyDialog({ workspaceId, dashboard, onClose })accepts the source dashboard’sidand localizedname, asks for a copy name, and navigates to the copy after creation;onClosedismisses the dialog on cancel or success. - Clients:
createTrpcClientandcreateQueryClient. - Caches:
invalidateWorkspaceCaches(utils, change)marks the cached queries an entity change makes stale, so open pages refetch them.utilsistrpc.useUtils()of the host’s client;changeis{ entity, workspaceId?, id? }withentityone ofworkspace(name, suspension, access),workspaceList,member,cliKey,checkpoint,branding,file,endpoint,edgeProgram,automation(with alarm rules and automation limits),parameter,dashboard(with its drawings),report,reportComment,storage(stored-data usage and retention), orsetup(everything the setup revision covers). WithoutworkspaceIdthe entity refreshes in every workspace; withid,getqueries of other rows stay cached. The package’s own mutations refresh through the same table.invalidateAfterApiCall(utils, queryClient, path)refreshes after a successful call of procedurepathmade on the user’s behalf and reloads open dashboards after setup-wide changes. - Tab icon:
WorkspaceFaviconloads a workspace image with session credentials and restores the host favicon on failure, removal, or unmount. It takes the active workspace’s favicon URL or null. - Chrome / brand:
BrandLink,OvervisLogo,LanguageSwitcher,ModeToggle,ThemeProvider.BrandLinkuses the Overvis HMI wordmark by default; hosts can replace its full and compact content through props when custom branding is needed.WorkspaceBrandLinkselects light/dark image URLs using the resolved theme, at 20 pixels high with proportional width and Overvis fallback on image failure. Undefined URLs hide the brand while loading; null URLs select the default brand.LocalWorkspaceAppreads branding metadata from the scaffold’s/__hmi/brandingendpoint and images from localbranding/files. It reads them again wheneversubscribeBrandingChangesreports a change (the scaffold wires the dev server’s branding event), so unpushed changes appear; without it branding is read once. - Development:
DevFatalErrorBoundaryandinstallDevFatalCrashfor the scaffold’s development error overlay.
Architecture sketch
Section titled “Architecture sketch”Host SPA / LocalWorkspaceApp │ ├─ ThemeProvider, Toaster, MobileNavProvider ├─ createTrpcClient ──► /trpc (cookie or x-api-key) │ └─ WorkspaceHostProvider (cloud | local) │ └─ WorkspaceLayout (host route /workspace/$workspaceId) │ createWorkspaceRoutes: ├─ / → first dashboard or empty state ├─ /d/$dashboardId → DashboardPage ├─ /reports/$presetId → ReportPage ├─ /alarms (active alarms and alarm log; ?tab=history) ├─ /setup/dashboards… (list + create/settings dialogs) ├─ /setup/endpoints… (list + create/edit dialogs) ├─ /setup/endpoints/$endpointId/status (full page) ├─ /setup/parameters… (tree + create/edit dialogs) ├─ /setup/reports… (list + create/edit dialogs) ├─ /setup/automations… (list + full-page editor) ├─ /setup/automations/alarm-rules… (list + form dialogs) ├─ /setup/files (uploads/ catalog) ├─ /setup/storage (stored data and retention) └─ /audit (activity history and checkpoints; ?view=checkpoints, ?kinds=all)The /setup/… and /audit pages are for editors, admins and owners.
Workspace routes
Section titled “Workspace routes”createWorkspaceRoutes(workspaceRoute, options?) from ./host builds the workspace pages as
TanStack Router code routes. They are children of the host’s route at /workspace/$workspaceId,
whose component renders WorkspaceLayout:
const workspaceRoute = createRoute({ getParentRoute: () => rootRoute, path: "workspace/$workspaceId", component: WorkspaceLayoutRoute, // renders <WorkspaceLayout workspaceId={…} />});const routeTree = rootRoute.addChildren([ workspaceRoute.addChildren(createWorkspaceRoutes(workspaceRoute)),]);LocalWorkspaceApp and the cloud application mount the same subtree, so paths, path params, and
search params are identical in both. The route objects keep their types: a host that registers its
router gets typed links to every workspace page, including the ones the package renders.
options.resolveDeviceUiOrigin maps the configured Device UI origin to one the host can open
(development proxies). Search params: endpoint status tab (device-ui; omitted for the default
connection tab) and new parameter branchId.
Host contract
Section titled “Host contract”<WorkspaceHostProvider mode="cloud" userId={user.id} onWorkspaceLoaded={setActiveWorkspaceId}> <WorkspaceLayout workspaceId={workspaceId} /></WorkspaceHostProvider>- Default dashboard entrypoint:
dashboardEntrypointUrl(id)→/static/dashboard/<id>/index.js. - Cloud (and local remote-JS fallback) also attach
dashboardStylesheetUrl(id)→/static/dashboard/<id>/index.css. Local with sources does not (Vite already scanssrc/dashboards/**). createTrpcClient()defaults:url: "/trpc",credentials: "include", no isolated paths.- Transport: queries are batched into GET requests, and a batch is split before its URL passes
8,000 characters, because servers and proxies reject longer request lines. A query whose
URL-encoded input passes 2,000 characters, such as readings for 100 parameters, is sent as POST
with the input in the request body instead. Mutations are always POST. A proxy in front of the
API therefore receives POST requests to
/trpcfor queries as well as mutations. mintDeviceUiTicket(optional): mints the ticket that opens a VPN endpoint’s device web UI through the proxy. Without it the endpoint status page has no Device UI tab.renderSuspendedBilling(optional): renders what the host offers on the page of a workspace suspended because its billing account is below its negative limit, such as the balance and a way to top it up. It gets the workspace (WorkspaceGetOutput).renderNavFooter(optional): renders host content at the foot of the workspace navigation, above the Setup menu, in the sidebar and in the mobile navigation. It receives the workspace ID, whether the sidebar is collapsed to icons, andonNavigate, which closes the mobile navigation after a link is followed.LocalWorkspaceAppdoes not take it.userId(optional): the signed-in user. Preferences kept in the browser, such as favorite and recently inserted graphics symbols, are stored per user. Without it they last until the page reloads.openAssistant(optional): opens the host’s assistant panel. With adrafttext it opens a new chat with that message written in the composer for the user to send, as Ask the assistant on an automation problem does. Without it, pages offer no assistant.
Local (LocalWorkspaceApp)
Section titled “Local (LocalWorkspaceApp)”Props: apiUrl, apiKey, workspaceId, optional getDashboardEntrypointUrl (default
/src/dashboards/<id>/index.tsx).
- In-package TanStack router: the same
createWorkspaceRoutessubtree as the cloud application. - Browser preferences use one fixed local user identity.
- tRPC:
credentials: "omit",getHeaders→x-api-key,client→x-hmi-client: workspace-ui/<version>, andparameter.readValuesStreamas an isolated path. - Dashboards load from the Vite source path; when it is missing (Accept probe), from the remote
/static/dashboard/…build, with a warning. Setup settings load the dashboard module from that same resolved URL. - Endpoint status
?tab=connection|device-uiis validated by the shared route. The Device UI tab needs a host that opens device web UIs (mintDeviceUiTicketonWorkspaceHostProvider), editor/admin/owner access, and a VPN public key.LocalWorkspaceAppdoes not open device web UIs, so it never shows the tab.
Scaffold entry (generated by CLI):
import { LocalWorkspaceApp } from "@overvis/hmi-workspace-ui/host";import { initializeLocale } from "@overvis/hmi-workspace-ui/i18n";import "@overvis/hmi-workspace-ui/styles.css";
await initializeLocale();
<LocalWorkspaceApp apiUrl={…} apiKey={…} workspaceId={…} />Dashboards
Section titled “Dashboards”DashboardPage imports the dashboard’s entry URL and requires a default export React component
accepting DashboardRootProps; the props and exports are in the
dashboard host contract.
Loading is a skeleton. A failed load is an error NoticeCard with Retry. A configuration that
fails validation offers editors, admins and owners a link to the dashboard’s settings. For a built
bundle (…/index.js), the host first reads the sibling hmi-dashboard.json; a missing manifest or
an abi other than DASHBOARD_ABI_VERSION shows “Dashboard needs a rebuild” instead of importing
the module. Local source entries compiled by Vite skip this check. After load, the host validates
configuration against named-export TypeBox dashboardConfigType. A missing export or a failed
check does not mount the root. A successful root is wrapped in a dormant ParameterValuesProvider;
no live demand means no read request or polling timer.
useParameterValues registers parameter cohorts with their cadence, useParameterValue reads one
discriminated state, and useParameterValueActions submits batches. The coordinator deduplicates
due IDs, consumes progressive stream results, prevents overlapping reads, pauses hidden tabs, backs
off failures per ID, preserves stale last-success values, and performs targeted reads after writes.
A nested provider isolates the scheduling of part of a dashboard. A dashboard that registers no
demand makes no live requests. The steps are in
Show live values in a dashboard.
combineParameterValuesDemands joins cohort summaries. ParameterLiveControls,
ParameterValueDisplay, ParameterBooleanBadge, and ParameterDiagnostics render their shared
states. ParameterDiagnostics shows one NoticeCard per grouped diagnostic: the device as its
subject, the error, affected parameter count and register addresses as technical details, and Retry
(pending while the parameters are being read) or, when a Setup change fixes the problem, the
endpoint, parameter or dashboard settings link as the primary action.
DashboardConfigurationRequired shows a dashboard whose parameters are not bound yet without
starting live demand. A dashboard for one device model renders its pages in DevicePresetLayout,
builds them on ParameterStateText, ParameterTrendChart, useParameterUnit,
DeviceCommandButton, and the overview cards (DevicePanel, DeviceKeyFigure,
DevicePhaseTable, DeviceStatusBar), tests its relays with DeviceRelayTest, and edits device
settings with DeviceSettingsSection, its write plans, and its gauges; see
device dashboard components. ParameterWriteReviewDialog
submits one reviewed batch and passes onSettled the failed change keys and each answered change’s
result by key (ok or the runtime error; a change the batch did not answer has none).
parameterRuntimeErrorText(result.error) is the text of a result’s error. The dialog neither
retries nor rolls back. parameterWriteOutcome(result) sorts a write result into done
(acknowledged), no-response (a device or gateway timeout, a unit the gateway could not reach, a
dropped connection, or no result: the write may or may not have taken effect) and failed with its
error. Both write dialogs read the written parameters back afterwards, except
writeOnlyParameterIds, such as command registers a device cannot read; their effect shows only in
separate feedback parameters. ParameterWriteSequenceDialog writes ordered steps and reports the
step that stopped them to onSettled; prepare runs after its checks, right before the first
write, and a message it returns stops the sequence with nothing written; onStart then runs once,
so a caller can record what it must undo before anything reaches the device. Its optional secret
(ParameterWriteSequenceSecret) asks for a password and writes it in a step before the others.
DeviceCommandButton and DeviceSettingsSection take the same value as unlock. A settings
section reads its written settings again and reports those the device did not keep
(DeviceSettingsVerification); its layout can render custom controls
(DeviceSettingCustomOptions, DeviceSettingControlProps) and check rules between settings
(DeviceSettingsValidator). ColdRoomCard is a ready room card for refrigeration controllers,
bound through coldRoomBindingsType; see
cold room card. coldRoomBoundParameterIds
lists every parameter the bindings name, command steps included. mapColdRoomBindings copies
bindings to another controller through a parameter map: it reports each binding it leaves out,
drops a command when any of its steps has no match, and gives no copy without the room probe and
the setpoint. ColdRoomCommands renders the card’s commands on their own, for dashboards that lay
a controller out themselves: Switch on while in stand-by and Stand-by while running, each
through a DeviceCommandButton (which takes an optional size). Every bound command stays mounted
while it is not offered, so a confirmation open when the state changes still reports its result.
SvgDiagram renders a same-origin SVG asset as a dashboard surface. Callers provide stable
data-svg-target bindings with prepared text, classes, accessible labels, and activation handlers.
The component removes executable and external SVG content, scopes IDs, fragment references, and
embedded CSS selectors, and reports missing targets. It preserves the asset’s view box and fits the
drawing into the container. Text bindings replace only the targeted element’s children; other
bindings on container groups keep their artwork. Binding classes take precedence over declarations
in the asset’s embedded stylesheet. Parameter lookup and state interpretation stay in the dashboard
module.
The diagram has a min-h-80 default. A Tailwind minimum height or aspect-ratio class in
className sizes it differently, such as aspect-video min-h-0 for a responsive fixed-aspect
floor plan. The diagram does not add pan or zoom behavior.
In the cloud, and when the local host falls back to a published build, DashboardPage also loads
the dashboard’s stylesheet, scoped to the dashboard element. Publishing dashboard changes through
the assistant reloads the open dashboard without a page refresh. The packages a dashboard shares
with the host are listed in the dashboard host contract.
Report components
Section titled “Report components”Dashboards compose history reports from pieces exported from the package root. The report page itself, with its title, Edit, and back links, is not exported. The steps are in Embedding reports in dashboards.
| Piece | Role |
|---|---|
ReportDataProvider | Holds the session (window, visible parameters) and loads readings. Optional sessionScope keeps a separate remembered window |
ReportView | All layout rows of the preset in a 12-column grid. Reads the preset from the provider |
ReportWidget | One view by its key. Without column, it fills its parent; ReportView passes the layout widths |
ReportTimeNav | From/To, Reset, quick ranges, CSV, Share snapshot, Parameters. Checks the viewer’s role itself |
ReportCommentsFooter | Comment, Sign, and the comment lists. Checks the viewer’s role itself |
useReportSession | Read and set the window and visible parameters from host code |
CSV export and snapshot sharing are available only through ReportTimeNav. The view components
(ChartRepresentation, TableRepresentation, and the others) are also exported; ReportWidget
picks the right one from the recipe. Two providers for the same preset on one page can show
different windows until the page reloads, and they share the remembered window unless
sessionScope is set.
Session. By default, a provider shares the window and visible parameters that the browser
remembers for the report page. With
sessionScope, it keeps a separate memory, so a dashboard tile does not change the window of the
report page. With Lock time range (immutableTimeRange), the remembered window is ignored.
Sizing. Plot height comes from the recipe: height on charts, time bars, and histograms;
tables use maxHeight, or about one screen tall when it is empty. A widget needs a parent with a
definite height. A ReportWidget placed by the host applies no column span. When the same view key
is mounted twice, such as in ReportView and in a separate ReportWidget, a snapshot uses only
the first one.
CSV and snapshots. ReportTimeNav shows CSV and Share snapshot under the same
conditions as the report page: the viewer is an operator, editor, admin, or owner, and the preset
enables them. A snapshot contains the views mounted under that provider (a ReportWidget without
column becomes full width), not the preset’s Setup layout.
Generated presets. A dashboard can run a ReportPreset built in memory. saved={false} keeps
the provider from loading comments. trpc.report.preset.freezeFromLive freezes parameter setups
for any workspace member, up to 100 IDs per call. validateReportPresetRecipe from
@overvis/hmi-shared/report checks a recipe. timeRange={{ from, to }} makes the window the
host’s: the session follows it whenever it changes, From and To are disabled, Reset and
quick ranges are hidden, and the range is not remembered. The window is also the report’s data
range, so daily bars follow it as it advances, including the current day. The window must not be
empty; a calendar period such as the current day is empty until its first minute closes.
Generated reports. These helpers wrap a recipe built in code:
useFrozenReportParameters(workspaceId, parameterIds)freezes the parameters’ current metadata withreport.preset.freezeFromLive, in as many requests as its limit needs;parametersstays null until every request has answered, andretryrequests failed batches again.GeneratedReportruns the preset over a counter period withtimeRangeandsaved={false}. It keeps one session per recipe content, so a recipe rebuilt on every render keeps its session, and shows a note instead of charts while the period is still empty.GeneratedReportSectionrenders one representation with a title and an optional note; wide content scrolls inside it.GeneratedReportPendingstands in while parameters freeze, or explains that they failed with a Retry that callsonRetry.
Fleet and site blocks
Section titled “Fleet and site blocks”Dashboards that watch many similar sites (stations, substations, lighting cabinets, cold rooms)
build on these blocks and keep only their domain: site types, equipment, drawings, and labels.
Configuration schemas and pure helpers come from @overvis/hmi-shared/dashboard (see
shared).
Site status comes only from configured conditions, never from measurements.
evaluateSiteStatus({ activity, alarms }, states) returns one of alarm, warning, offline,
active, inactive, normal, unknown, loading, unconfigured:
| Status | When |
|---|---|
alarm | A critical alarm condition is known to be true |
warning | A warning alarm is known to be true and no critical one |
offline | No feedback is current (missing or stale); lastData is the newest value |
active | Every activity condition is known and at least one is true |
inactive | Every activity condition is known and all are false |
normal | No activity conditions, no alarm true, and every alarm known |
unknown | Some feedback is missing or stale while the rest is current |
loading | Feedback is still being read for the first time |
unconfigured | No activity or alarm conditions are bound |
Stale, missing, or failed feedback is never read as inactive or zero. SiteStatusBadge shows the
status, an activity count, the last data time while offline, and a note when alarm feedback is
unavailable. A fleet dashboard relabels states through labels, such as Running and Stopped for
pumps. Blocks take each site’s evaluated state through stateOf; evaluateSites from
@overvis/hmi-shared/dashboard builds such a lookup for every site.
siteStatusParameterIds(feedback) lists the parameters evaluateSiteStatus reads, for the
overview’s live demand, and siteAttentionParameterIds(feedback, equipment) adds the due dates of
equipment, for what a page summary reads. activeAlarmsBySeverity(alarms, states) keeps the
alarms whose condition is known to be true, critical first and otherwise in their order; an alarm
without a condition, such as an unbound fault slot, is never active, so a dashboard can list every
possible alarm with its own fields (a pump, a trip) and let the helper drop the inactive ones.
Blocks receive the dashboard navigation (useDashboardNavigation()) and live states as props, so
they render without a router, for example in tests:
-
FleetOverviewlists sites as compact tiles (name and type, the status line, then the summary and readings inline; the zone only when not grouped by zone) or a table, with status totals, search, zone, status, and the dashboard’s ownfilters, sorting, zone grouping, empty and no-result states, and paging by 100. Filters live in the URL (q,zone,status,sort, and the key of each of the dashboard’s filters), and site links carry the query back.childrenreceives the filtered sites, for an alarm list or a map. Tables scroll horizontally inside their own area; on phones the table representation shows tiles.titleis optional; the host already shows the dashboard’s name. Optional props add the dashboard’s own content:siteSummary({ label, render(site) }): a block on each card under the status line, and a table column after Status.columns(FleetColumn:id,label,cell(site), optionalsortValue(site)): named values such as energy over a period, after the reading columns in the table and next to the summary readings on cards.sorts(FleetSort:value,label,column,direction): sort options after configured order, name, and urgency, ordering by a column’s sort values; unknown values (null) come last. Avaluethat repeats a built-in or an earlier option is ignored.selection(FleetSelection:selected,onChange,actions(sites)): the dashboard holds the selected site IDs. The overview adds Select, a checkbox on each card and table row, and a sticky bar with the selected count, Select all matching (every filtered site, not only the shown page), Clear, and the dashboard’s actions. Changing a filter (search, zone, status, or a filter of the dashboard’s own, but not the sort) or leaving selection clears it. Nothing is stored in the URL.actionsreceives only selected sites the filters still show. Selection stays on while sites are selected, so a dashboard can put failed sites back into it.
A card is one link to its site. Interactive content a dashboard renders on a card needs
relative z-10to sit above that link. -
FleetMapplaces sites by fixed coordinates or by a geo parameter, colours markers by status, shows a legend of the states on the map, and opens a popup with status, summary readings, and a site link. A stale geo position keeps its last place with a faded marker; a missing one leaves the site off the map.sitePositionParameterIds(position)lists the position parameters it reads.details(site)adds the dashboard’s own content to the popup after the readings, such as period totals. -
FleetAlarmListlists known active alarms with links, critical first, in a panel of fixed height that scrolls beyond three alarms and reads No active alarms only while every alarm state is known (otherwise Some alarm states are unavailable), so the page never shifts as alarms come and go;unknownreplaces the all-clear while some alarm states cannot be evaluated, andpendingkeeps it empty until values arrive. -
FleetOverviewalso publishes a fleet page summary for the assistant: status totals over every site, ignoring the overview filters, then sites most urgent first.alarmsOfandmaintenanceOfadd each site’s active alarms and overdue maintenance. -
SiteReadingsshows labeled readings, as a strip across the page or a compact grid;SiteReadingMetricis one of them (name, optional icon, value), such as on a panel card, andSiteReadingValueis a value alone, or ”—” when the reading is not bound. A stale value stays visible, muted, and marked with a clock (StaleMarker).SiteMaintenanceBadgecounts overdue maintenance dates;dueDate="verification"labels them as verification dates (meters) instead. -
EquipmentOperationsgroups setpoints, mode commands, start and stop, and other commands, and shows the maintenance due date and a history action.historytakesparameterIds,pending, andopen(parameterIds); the callback receives unique IDs in their original order. Null history or empty IDs hide the action. Pending requests disable it, and more than 20 unique parameters disable it with a translated explanation of the count and limit. WithoutparameterIds, as in older bundles, the callback works as before and the limit is not checked.dueDate="verification"labels the due date “Verification due” instead of “Maintenance due”. -
overdueEquipment(equipment, states, now)names the equipment whose due date (itsmaintenanceParameterId) has passed, withunknownwhile a bound date is not current; pass a site and its equipment, or a meter list for verification dates.dueDateParameterIds(equipment)lists those parameters for the live demand. -
SiteActiveAlarmBadgesshows the active alarms of one piece of equipment as compact badges, such as in a panel header. -
SiteAlarmListlists one piece of equipment’s active alarms, and those whose state is unknown, with their severity; withcanEditit lists every alarm with Edit and Remove.SiteEquipmentSetupis the setup row under a piece of equipment: its controls with Edit and Remove, reordering of each list with two or more items, and adding a reading, alarm or control, throughonEdit(kind, item | null),onRemove(kind, item)andonReorder(kind)(SiteEquipmentActions).SiteItemActionsis the Edit and Remove pair they use, for a dashboard’s own setup lists, such asSiteReadingsactions. -
SiteEquipmentViewcomposes them for a site or one piece of equipment:SiteReadings, thenSiteAlarmList,EquipmentOperationsand, whilecanEdit, Edit and Remove on every reading andSiteEquipmentSetup.variant="site"spreads the readings across the page;equipmentis the compact block of a panel. It takes the dashboard’s readingicon, the operations’historyanddueDate, and theSiteEquipmentActions.aside, such as a pump list, sits beside the alarms and operations on wide screens;commandsgoes between the alarms and the operations, andoperationControlslimits the controls the operations offer, for a dashboard that operates some commands throughSiteCommandButtons. -
SiteFigureis a labelled value for adlstrip (sizelarge, medium or small;widespans two columns).SiteIndicatorLightsshows states as a fixed row of lights (SiteLight: icon, label,on,offorunknown, atone, optionaldetailandstale), dim while off or unknown, so the row never shifts as states change.useParameterNamesmaps parameter IDs to their names in the UI language, such as alarm flags named after their parameter. -
SitePageHeaderandSiteNotFoundgive a site page its back link (keeping overview filters), title, status line, and the setup toggle.SiteSectionis a plain page section with a small heading and optional actions. -
FleetRoutesis a fleet dashboard’s root:overviewat/,site(site)at<sitesPath>/<id>, andsubpage(site, path)for pages below a site’s own, such as/sites/<id>/points/<id>, returning null for one that does not exist. A missing site or page showsSiteNotFoundwithnotFoundandbackLabel.matchSitePath(path, sitesPath)splits a path the same way. -
Setup mode of a site page:
newSiteLocation(path, overviewSearch)is a new site’s page withsetup=1and the overview’s query.useSiteSetup({ search, canEdit, pageParams })starts in setup mode from that flag and returnseditingandsetEditingforSitePageHeader, andoverviewSearchfor links back to the overview, without the flag and thepageParamsonly the page uses.useSiteEditor(site)holds the open setup dialog (editor,open,close) andbaseline, the site it opened with, as its conflict baseline. -
PhaseValuesshows phase readings on one line, L1 first (“231.2 · 234.0 · 231.3 V”): one unit when the bound phases share it, otherwise a unit per value, and ”—” for an unbound phase.SiteReadingCellis an overview column built on it, forcolumns: the phases, then the site’s readings of that column (named when there are several), or ”—”. -
Control mode:
siteControlMode({ automatic, local }, states)reads who controls a site from its conditions:localwhile the local selector is on, otherwiseautomaticormanual,unknownwhile that feedback is unavailable, and null when the site reports neither. The mode never changes the status.siteLocalControl(local, states)returnson,off,unknown, orunbound.SiteModeBadgeshows the mode beside the status, anduseSiteControlModeLabellabels it, such as for a mode filter.siteActivity(state)readsactive,inactive, orunknownfrom the activity feedback alone, and null while there is none. -
SiteCommandButtonsshows a site’s or its equipment’s commands as buttons grouped by purpose (switching, mode, other);siteCommandEntry(control, pressed)turns a configured command into one. Each opens the write sequence, so commands of several steps work. The buttons are disabled while the local selector is on and explain why; in automatic or unknown mode a switching command warns that the controller may switch it back; a dialog whose command changed in setup while it was open cannot run.canWritecarries the user’s write access; without it nothing shows. -
Group commands write one command to many selected sites, for
selectionactions:buildSiteGroupCommand({ sites, action, command, localControl, stateOf, modeOf, switching })lists the writes. It includes each site whose command is one write and leaves out, with a reason, sites without a command, with several steps, under local control, or sharing a command parameter with another selected site at a different value. Sites writing the same parameter and value (a shared group register) share one write.switchingcounts sites in automatic or unknown mode for the warnings. More thansiteGroupCommandLimit(100) sites block the batch.sameSiteGroupCommand(reviewed, current)tells whether a list rebuilt from live feedback still matches the reviewed one; changed data notes alone do not count.SiteGroupCommandConfirmationreviews the list with the sites’ state now (currentText) and thetarget, the warnings and the left-out sites; whileoutdatedit offers Update list and cannot be confirmed. Confirming sends one batch.siteGroupCommandResults(write, results)gives each site itsparameterWriteOutcome, andSiteGroupCommandResultsshows them with the error and the site’s live state (liveText).siteGroupRetrySelection(rows)selects the failed and unanswered sites again. Nothing is retried or reversed automatically.
-
SiteBindingEditor,FleetDeleteDialog,BoardEditor,FleetOrderEditor, andSiteDuplicateDialogare setup forms. Each validates its input and callsonSavewith the result; the dashboard applies it insideuseDashboardConfigurationMutationand throwsFleetConfigurationErrorfor conflicts, whichfleetConfigurationFormErrorturns into form errors. For a change made without a form, such as a drag,fleetConfigurationErrorTextgives the same failure as one sentence; a conflict asks to make the change again on the latest configuration, so the dashboard shows that one after the error.useParameterInfo,writeBindingError,geoBindingError, andcounterBindingErrorcheck bound parameters. -
FleetDeleteDialogwithdetailsexplains what else the deletion changes, such as items that move elsewhere. -
SiteBindingEditorwithcommand({ category, name? }) edits a named command slot: a command with a fixed purpose and no setpoint choice;namefills the name of a new command, andtitlereplaces the dialog title. -
SiteDuplicateDialogmatches every binding to the same relative path under a target branch and lists the bindings that will be left out before saving. Withoutzonesit has no zone field. WithonSavethe user picks one branch and names the copy. WithonSaveAllthe user picks one or more branches from a searchable list, gets one copy per branch named after it (the branch’s last segment, or its path when two end alike; there is no name field), sees the left-out bindings per branch, and the dialog passes every copy (SiteDuplicateCopy:name,zoneId,branch,mapping) toonSaveAllat once, for one configuration update. -
Field groups render shared rows in a fleet dashboard’s own forms:
SiteSlotFieldsrenders the reading and condition slots of adefineSiteRolesdeclaration in declaration order: a parameter per slot, a unit for readings (filled from the parameter unless the user typed one), and a comparison and value for conditions. It takesfieldspointing at the rows built by the declaration’sformValue,roles, andlabels.siteSlotErrors(invalid, path)turns theinvalidrows offromForminto field errors. The form schema types aresiteSlotRowsTypefor those rows (siteSlotRows(keys, row)builds them),optionalParameterIdTypefor a single optional binding, andsiteNameTypefor a name.SitePlacementFieldsedits a site’s zone and map position: fixed coordinates, typed or picked on a map, or a geo parameter for a mobile unit.sitePlacementFormValue(site)is its starting value (schemasitePlacementFormType). It takes the board’szonesand, ascontext,sitePlacementContext(sites, siteId).sitePlacementFromForm(value, path, parameterInfo)returns{ placement, errors }, whereplacementholds the site’szoneIdandposition.parseSiteCoordinateaccepts decimal degrees with a dot or comma.SiteStarterFieldsoffers optional pickers for the typical readings, alarms and setpoints of a new site or piece of equipment, each with its label.siteStarterFormValue(starters)is its starting value (schemasiteStarterFormType), andsiteStarterBindings(starters, value, path, parameterInfo)returns{ bindings, errors }(a setpoint needs a writable parameter), thebindingsthatstarterEquipmenttakes.CommandStepsFieldedits a command’s ordered write steps (a parameter and a value each, with Earlier and Later).commandStepsFormValue(steps)is its value,commandStepsErrors(value, path, parameterInfo)checks it (at least one step, writable verified parameters, values), andcommandStepsFromFormreads the steps.SiteBindingEditoruses it for commands.DashboardPickerField(a field component ofuseAppForm) picks one of the workspace’s dashboards;excludeleaves some out, such as the dashboard being configured.
useParameterInfo() returns an async lookup
(parameterId) => Promise<ParameterInfo | "unavailable" | null> that reads the parameter from the
server. ParameterInfo has unit, writable, datetime, geo, the value-type name valueType
and backgroundReadingPeriodMs (null when background readings are off). Null means nothing is
selected (an empty ID). "unavailable" means the lookup failed, and the binding checks block
saving then. The binding checks take its awaited result; sitePlacementFromForm,
siteStarterBindings and commandStepsErrors take the lookup itself and are async too.
const parameterInfo = useParameterInfo();
async function setpointError(parameterId: string) { return writeBindingError(await parameterInfo(parameterId));}useNow(intervalMs) re-renders with the current time in milliseconds every minute by default, for
due dates, ages and countdowns; useNow(null) stops the ticks, such as while no age is on screen.
useIsMobile() is true below Tailwind’s md width, for layouts that switch between a table and
cards; useMediaQuery(query) follows any CSS media query.
Generic strings (search, filters, statuses, editors) come from this package’s catalog in every supported language; dashboards pass their domain labels.
Counter totals
Section titled “Counter totals”Dashboards for meters (energy, heat, water, gas) show the consumption of a counter per period with
a comparison. Periods, comparison windows and classification come from @overvis/hmi-shared
(counter periods); every value is the server’s readings.aggregate
delta. The dashboard never sums or integrates anything.
const { period } = useCounterPeriod({ defaultPeriod: "today" });const counters = useCounterTotals({ workspaceId, counters: [energyId], periods: [period] });
<CounterPeriodSelect defaultPeriod="today" /><CounterTotal total={counters.find(energyId, period)} unit={counters.unit(energyId)} />useCounterTotals({ workspaceId, counters, periods, timeZone? })returnstotals(one{ parameterId, period, current, previous, change }per counter and period),find,unit,isPending,isError, andretry.- Calendar periods use
timeZone, or the browser’s zone when it is omitted. - Each window asks
readings.aggregatefordelta,first,latestandmin, at most 100 pairs per request.readings.timeExtentruns once per counter and is cached for the page. At most four requests of each kind run at once. - When a window ends after the cached extent, a
latestover the last background period plus a minute shows whether readings reach the window end. The extent is requested again only when they don’t, or when a counter cached without readings has started reporting. - A counter with readings recorded on its device is partial in a window that ends after its
deviceLog.completeThrough, unless background readings reach the end. Its metadata is loaded again every minute, as is that of a Modbus counter without background readings, which may start receiving them; when the watermark moves, the counter’s extent and window values are loaded again, so totals complete after the next pull. - Results refresh just after each UTC minute closes. Completed periods are not requested again.
- A counter that is not
numgetsno-datatotals. When a counter’s parameter cannot be read,isErroris true andretryreads it again.
- Calendar periods use
CounterTotalshows the value with unit and a change badge, captioned for the period (for example “vs same time yesterday”). Without readings it shows “No data”, never 0. A counter reset shows “Counter reset” without a value. Partial coverage shows “Since …” or “Until …” and no change. Whiletotalis undefined it shows a skeleton.compactfits a table cell or list row: a smaller value without the change badge.change={false}drops the badge at full size andvalueClassNamerestyles the value, such as a larger headline. Values are grouped and lose decimals as they grow;formatCounterValue(value, unit, locale)formats the same way elsewhere.CounterChangeshows only the change badge of a total, with the comparison as its tooltip or, withcomparison, as a caption; nothing unless both windows have values. It complements a compact total, which has no badge.rising="good"tints a rise green and a fall amber,rising="bad"the reverse, for example for export and for consumption or cost.CounterBarscharts a counter’s increase over a period: per hour up to two days, per calendar day intimeZonebeyond (counterBucketCadence(period)gives the same choice for a chart of your own). Each bar is that bucket clipped to the period and runs from the latest reading before it, the first from the reading the period total starts from, so the bars add up to the total however short an edge bucket is; a reset inside or between buckets draws no bar. It reads closed days (readings.days) or quarter hours (readings.cadence) once per aggregation and only the period’s edges withreadings.aggregate, so a month takes about six requests; where a zone’s days or hours do not start on such a boundary (days in UTC+05:30), it reads each bucket. A rolling period’s first bucket and the bucket still in progress cover only part of their hour or day: they are lighter and left out of the average, which a dashed line marks. Up to 16 bars carry their values, the axis shows dates (YYYY-MM-DD) or hours (HH:mm), and the tooltip gives the bucket start and value. Nothing renders when the period has no increase.useReadingPeak(workspaceId, parameterId, window)is the reading with the largest magnitude in a window[from, to), with its sign and time, such as the peak power of the period (negative when export peaked), or null.useClosedMinute()is the current time, sampled on mount and again just after each minute closes; it is not itself a minute boundary.resolveCounterPeriod(id, useClosedMinute(), timeZone)floors a period to the last closed minute and returns the windowsuseCounterTotalsuses, for example for a chart orreadings.minMaxover the same period.CounterPeriodSelectis a select bound to a URL search key (searchKey, defaultperiod).useCounterPeriodreads the same key with the sameperiodsanddefaultPeriod. An unknown value in the URL selects the default, and choosing the default removes the key.useResolvedCounterPeriod({ timeZone, defaultPeriod, periods, searchKey })resolves the period in the URL that way and refreshes it at each closed minute.useDashboardTimeZone(setting)turns a dashboard’s time zone setting (dashboardTimeZoneTypefrom@overvis/hmi-shared) into a zone: the viewer’s own forbrowser, the workspace’s when the setting is absent.CounterTotalsNoticeshows why totals stay empty after their requests failed, with Retry, as a notice for the totals of a page.counterTotalValue(total)is a total’s value for sorting and bars, null for no data, a reset or while loading.counterSlotUnit(totals, slot)is a bound counter’s unit from its parameter, or the unit saved with the binding.counterShare(part, whole, partUnit, wholeUnit)is a part’s share of its whole over the period, only for whole-period values in the same unit;formatCounterShareshows it as a percentage.commonCounterUnit(units)picks the unit most counters use, andrankByCounterTotal(items, counterOf, totals, periodId, count)ranks items by their counter in that unit, largest first, such as the largest sites of a portfolio.counterBindingError(info)acceptsnumparameters only. For an integer it explains that a calculated parameter must scale the pulses to the metered quantity. Pulse counters are bound through such acalcparameter.numericBindingError(info)does the same for a reading charted or compared over time, such as power for a peak.CounterBindingWarnings({ counters, share })warns in a setup form, without blocking the save, about a counter that is not recorded (its history is recorded only while someone views it) and, withshare, about a counter whose unit differs from its parent’s.PeriodSettingsFieldsedits a board’s default period and the time zone its calendar periods follow: the workspace’s, each viewer’s own, or a named zone. Itsfieldspoint at aperiodSettingsFormValue({ defaultPeriod, timeZone }), andperiodSettingsFromForm(value, path)reads it back and returns a field error for an unknown zone.BoardSettingsFields(withboardSettingsFormValueandboardSettingsFromForm) is the display and zone part ofBoardEditor, for a board dialog with more settings.
A counter reset is flagged, never corrected. Unit conversion is not done; units come from the parameters.
Page summary
Section titled “Page summary”The assistant reads what an open dashboard page shows through the host’s page state. A page
publishes a summary with usePublishDashboardSummary(getSummary). getSummary runs only when the
assistant asks, with the values of the latest render. It may be async, and returns one of two
kinds:
fleet(buildFleetSummary): site count, status totals with the labels the page shows, active alarms critical first, and sites most urgent first with overdue maintenance.FleetOverviewpublishes it.site(buildSiteSummary): one site’s status and label, its active alarms, overdue maintenance, and optionallyfleet, abuildFleetSummaryresult for the other sites. A site page shows only its own values, so it reads the others ingetSummarywithuseReadParameterValuesOnce(). The reader adds a temporary demand to the shared live values and resolves when every value has arrived or failed, or after 8 seconds with the states known then.
A fleet site page publishes both with usePublishSiteSummary, which takes the site, all sites,
the page’s readOnce, the parameterIds one site’s summary reads (such as
siteAttentionParameterIds), evaluate(site, states, now) returning its state, active alarms and
maintenance (SiteSummaryEvaluation), and the page’s statusLabels. Every site is read once, so
the site and the fleet cannot contradict each other.
Each alarm (SiteActiveAlarm) names the equipment that raised it, or null for a site alarm, its
name, and its severity. Maintenance (SiteMaintenanceSummary) lists overdue equipment and whether
some due date is unavailable. Each site reports alarmUnknown and maintenanceUnknown, so
unavailable feedback never reads as an empty, all-clear list; a fleet also counts such sites in
alarmUnknownSites and maintenanceUnknownSites, including sites past the list limit. Lists stop
at dashboardSummaryListLimit (20) entries, and the summary counts the rest. The summary stays
small for any fleet size. Only one page of a dashboard publishes at a time. Outside a mounted
dashboard the hooks do nothing.
Both builders take each site’s state as { status, alarmUnknown } (SiteSummaryState), so a page
with its own status logic, such as map pins, maps it onto the site statuses with the labels it
shows. A device dashboard can publish itself as one site, with its fault flags as
SiteAlarmBindings (a condition with an equipment name, a name, and a severity) for
evaluateSiteStatus and activeSiteAlarms; see
Publish a page summary.
Generated drawings: derivedEntityIds gives each configuration entity stable drawing item IDs,
hashedEntityId(text) names an item by what it connects, such as a pipe keyed by its line and end
ports, validateDrawingLimits rejects drawings beyond the graphics limits, and
reconcileGeneratedDrawing applies a configuration change to a drawing an editor may have
customized, keeping its layout and extra items and placing new items below the artwork. Each end of
a generated connection that the editor left alone follows the configuration, so reordered or moved
equipment is reconnected; an end the editor moved stays where they put it, and a connection whose
end moved loses its custom bends. Symbol properties and generated text the editor left alone also
follow the configuration, such as a meter’s units after a rebinding or a renamed board’s label. A
free end of an existing connection is a point in the layout the editor’s artwork replaced, so it
stays where it is, and an end left on equipment that loses its port keeps its spot on that
equipment. A free end of a new connection, such as a feeder starting on a busbar, stays on the item
it lands on: it moves with new items below the artwork and with an existing one to where the editor
keeps it. A generated connection attached to a port follows its equipment. graphicsSymbolElement,
graphicsShapeElement and graphicsConnector from @overvis/hmi-shared/graphics build a placed
symbol, a plain element and a line for such a drawing (see shared).
Templates
Section titled “Templates”Stamp via dashboard.create({ template }). Template IDs are in
dashboard procedures. The compiler is hmi build
(@overvis/hmi-cli/dashboard-build); see the CLI reference. For a
live dashboard’s configuration, empty state, live values and writes, see
live data.
The build writes unminified ESM plus utilities-only index.css (scan that dashboard’s *.ts /
*.tsx / *.js / *.jsx, tokens from this package’s styles-reference.css as installed in the
project, no preflight) and the hmi-dashboard.json manifest. Vite, its plugins, Tailwind CSS, and
TypeScript load from the first of the project, the working directory, and the CLI’s own
installation that has all of them. The build writes the CSS; dashboards import none. The host
declares two cascade layers for the generated utilities: plain classes use dashboard, below the
host utilities, and variant classes use dashboard-variants, above them, so
grid-cols-2 @min-[30rem]:grid-cols-3 switches even when the host also defines grid-cols-2. The
build also scans this package’s code next to styles-reference.css; a variant class found there
stays in dashboard, so shared UI components keep their interaction states inside the scoped
dashboard stylesheet. Important variants (sm:p-4!) also stay in dashboard, because !important
reverses the layer order. A data-theme="light" subtree stays light in the dark theme. See
dashboard styling. config.ts must export
dashboardConfigType; the build writes config.schema.json.
The package root exports the form scaffold that every UI in Setup, cloud and local hosts, and the
shared components uses to submit fields to the API: useAppForm, FormRoot, SubmitButton,
mutationFormValidators, focusFirstInvalidField, field helpers, and mapTrpcToFormErrors. Its
shape and behavior are in Dashboard UI and forms,
and the steps in Build a form that saves through the API.
| Symbol | Entry | Role |
|---|---|---|
trpc | . | createTRPCReact<PublicAppRouter>() |
createQueryClient | ./host | Defaults: staleTime 30s, retry: false |
createTrpcClient | ./host | Configures requests and subscriptions for the host |
Subscriptions use httpSubscriptionLink (+ EventSourcePolyfill when custom headers are set —
needed for API-key local hosts).
Local hosts pass url, credentials: "omit", and getHeaders for x-api-key. Hosts signed in
with a browser session can use the defaults. Hosts may supply fetch to observe or customize query
and mutation HTTP requests, including isolated streams. Subscription requests continue to use
EventSource.
isolatedPaths lists procedures whose calls each get their own HTTP request, so aborting one call
(for example parameter.readValuesStream) cancels exactly that stream. client: { name, version }
sends x-hmi-client: <name>/<version>; the API answers client_outdated when that version is
older than it supports. localSetup: true makes dashboard.get and graphics.get return the
dashboard configuration and drawings that the scaffold’s /__hmi/setup/dashboards/<id> endpoint
serves from unpushed setup.db rows. A local drawing reports the revision it will have after a
push, so saving over unpushed local changes is refused as a conflict. Without the endpoint the
queries return saved data; when it fails they fail. LocalWorkspaceApp sets it.
trpcContext (from ./host) is the React context trpc uses. A host with its own
createTRPCReact over a larger router of the same API passes it as context, so its hooks share
trpc.Provider’s client and query cache.
UI primitives and styles
Section titled “UI primitives and styles”- Design system: Tailwind 4 + shadcn components, exported from the
./uientry. - The catalog is the Overvis component set, not a synchronized mirror of shadcn. Components may be added, adapted, or removed as the product evolves.
- Hosts import
@overvis/hmi-workspace-ui/styles.cssonce (font: Inter variable). ThemeProviderpersists light/dark/system; exposesresolvedThemeto dashboard mounts.cnutility for class merging.
The package’s translation catalogs support en, pl, de, uk, and ru. The browser checks localStorage, then cookies, browser preferences, and finally English.
Hosts await initializeLocale() before mounting translated components. Additional host catalogs
register their load function with browserLocale.register before initialization. The package loads
catalogs by language and caches them. useTranslation() returns a typed translator and subscribes
to language changes; useLocale() subscribes when a component only needs the locale. t() reads
the active language outside rendering, such as in event handlers or non-React helpers.
await setLocale(locale) loads all registered catalogs before updating components, the document
language, and the saved preference. It does not reload the page. Failed loads retain the current
language. LanguageSwitcher reports failure and lets the operator retry.
Literal keys and placeholder arguments of t() are checked by TypeScript; catalogs need no
generation step.
Workspace pages
Section titled “Workspace pages”The pages createWorkspaceRoutes builds behave as the
workspace pages reference describes; the library adds no host-specific
behavior to them. Their lists, editors, and drawings follow setup changes, renames, suspension, and
access changes in the open workspace without a page reload.
Dates in the pages use YYYY-MM-DD HH:mm:ss; report fields use the report time zone. CSV exports
prefix headers and text cells whose first non-whitespace character is =, +, -, or @ with an
apostrophe before RFC 4180 quoting, so spreadsheets do not run them as formulas. Numeric cells,
including negative values, stay numeric.
Pull instructions UI
Section titled “Pull instructions UI”WorkspacePullInstructions mints workspace.mintPullToken with a cookie session and editor, admin
or owner access, and shows:
pnpm dlx overvis-hmi pull <token># or npm / yarn variantsMounted on workspace settings (cloud) and the empty workspace index — not under Setup.
Non-goals
Section titled “Non-goals”- Authentication, account switching, or the assistant chat panel
- Serving built dashboard assets (API
/static)
See also
Section titled “See also”- shared package — schemas and PublicAppRouter import
- Embedding reports — dashboard host contract for report widgets
- HMI CLI reference
Local action message history
Section titled “Local action message history”useToast() returns a helper with success, info, warning, and error methods that take a
plain-text message. An optional second argument accepts workspaceId, workspaceName, and an
app-relative href. The helper displays a toast and records the same text in browser history.
MessageHistoryProvider identity={userId} keeps the history of one identity; without an identity,
history stays in memory only. MessageHistoryBell shows it.
MessageScopeProvider scope={{ workspaceId, workspaceName }} attaches workspace metadata to
descendant messages. A helper keeps the workspace of the component that returned it, so a
completion message from an asynchronous action records its original workspace. Changing the history
identity detaches pending callbacks and removes visible toasts from the previous identity. The
steps for a custom host are in
Embed the workspace UI in a React app.
The bell keeps the newest 100 messages for seven days, with browser-local timestamps, seen markers, Mark all seen, and Clear. Expired records are also removed for inactive accounts and local workspaces when history refreshes. Storage failure falls back to memory. History and seen state synchronize across tabs without displaying duplicate toasts. Stored messages retain their original display language. Only visible rows in the active Messages panel become seen while the browser tab is visible and focused.
A host may provide notifications (tab content) and notificationUnreadCount to add the server
Notifications tab and badge to the same bell. Tab controls appear only when the host supplies
notification content; the bell opens on that tab while the count is above zero. These do not change
local message retention. showNotificationToast(kind, title, { description, action }) shows a
toast for something the host lists elsewhere, such as a server notification, without recording it
in Messages.
The legacy toast.success/info/warning/error export records history using the identity and
workspace active when called. These four methods preserve Sonner message arguments, options
(including durations and actions), and returned toast IDs. Rich React messages are recorded from
their rendered text. Unlike useToast, it does not keep the context of the action that called it.