Hooks
React hooks that connect your components to live device data. All hooks require your app to be wrapped in RelayProvider.
useRelayApp()
Access the raw App SDK instance.
import { useRelayApp } from "@relay-x/ui";
const app = useRelayApp(); // RelayAppInstance | null
useRelayConnection()
Access the App SDK instance and connection status.
import { useRelayConnection } from "@relay-x/ui";
const { app, isConnected, error } = useRelayConnection();
| Return | Type | Description |
|---|---|---|
app | object | null | The App SDK instance |
isConnected | boolean | Whether the SDK is connected |
error | string | null | Error message if connection failed (e.g., auth_failed) |
useRelayTimeSeries()
Stream historical and/or real-time telemetry data for a device.
import { useRelayTimeSeries } from "@relay-x/ui";
const { data, isLoading, error } = useRelayTimeSeries({
deviceIdent: "sensor_01",
metrics: ["temperature", "humidity"],
mode: "both",
timeRange: {
start: Date.now() - 3600000,
end: Date.now(),
},
});
Options
| Option | Type | Description |
|---|---|---|
deviceIdent | string | The device identifier |
metrics | string[] | Metrics to subscribe to |
mode | string | "historical", "realtime", or "both". Default: "historical" |
timeRange | object | { start: number, end: number } — timestamps in ms |
maxPoints | number | (optional) Maximum data points to keep. Default: 10000 |
interval | string | (optional) Bucket size as a Flux duration ("30s", "5m", "1h"). Pair with aggregateFn. Historical only |
aggregateFn | string | (optional) Server-side aggregation. One of "mean", "min", "max", "sum", "count", "first", "last", "median", "stddev". Pair with interval. Historical only |
interval and aggregateFn are passed through to the App SDK as interval and aggregate_fn. Both must be set together to produce bucketed results. They have no effect when mode is "realtime", since live points stream as they arrive.
Modes
| Mode | Behavior |
|---|---|
"historical" | Fetches data once for the given time range. No live updates. |
"realtime" | Streams live data only. No historical data. |
"both" | Fetches historical data first, then streams live updates. New data is merged in. |
Return
| Return | Type | Description |
|---|---|---|
data | DataPoint[] | Array of data points with merged metrics |
isLoading | boolean | true while fetching historical data |
error | Error | null | Error if the fetch or stream failed |
useRelayLatest()
Get the most recent value for a single metric. Fetches the latest and subscribes to live updates. Returns a RelayDataPoint that can be passed directly to any component's data prop.
import { useRelayLatest } from "@relay-x/ui";
const latest = useRelayLatest({
deviceIdent: "sensor_01",
metric: "temperature",
timeRange: {
start: Date.now() - 86400000,
end: Date.now(),
},
});
// Pass directly to a component
<NeedleGauge data={latest} min={0} max={100} />
Options
| Option | Type | Description |
|---|---|---|
deviceIdent | string | The device identifier |
metric | string | The metric name |
timeRange | object | { start: number, end: number } — timestamps in ms |
Return (RelayDataPoint)
| Return | Type | Description |
|---|---|---|
value | number | null | The latest metric value |
timestamp | number | null | When the value was recorded (ms) |
isLoading | boolean | true while fetching |
error | Error | null | Error if the fetch or stream failed |
Components that accept a data prop (NeedleGauge, ArcGauge, ProgressBar, StatCard, StatCardWithGraph) expect this exact shape.
useRelayEvents()
Subscribe to historical and/or real-time events for a device.
import { useRelayEvents } from "@relay-x/ui";
const { data, isLoading, error } = useRelayEvents({
deviceIdent: "sensor_01",
eventNames: ["door_opened", "door_closed"],
mode: "both",
timeRange: {
start: Date.now() - 86400000,
end: Date.now(),
},
});
Options
| Option | Type | Description |
|---|---|---|
deviceIdent | string | The device identifier |
eventNames | string[] | Event names to subscribe to |
mode | string | "historical", "realtime", or "both". Default: "historical" |
timeRange | object | { start: number, end: number } |
Return
| Return | Type | Description |
|---|---|---|
data | RelayEvent[] | Events sorted ascending by timestamp |
isLoading | boolean | true while fetching historical data |
error | Error | null | Error if the fetch or stream failed |
Each RelayEvent has name, deviceIdent, value, and timestamp (Unix ms).
useRelayLogs()
Subscribe to historical and/or real-time device logs, with optional level filtering.
import { useRelayLogs } from "@relay-x/ui";
const { data, isLoading, error } = useRelayLogs({
deviceIdent: "sensor_01",
levels: ["warn", "error"],
mode: "realtime",
timeRange: {
start: Date.now() - 3600000,
end: Date.now(),
},
});
Options
| Option | Type | Description |
|---|---|---|
deviceIdent | string | The device identifier |
levels | string[] | (optional) Subset of ["info", "warn", "error"]. Omit for all levels |
mode | string | "historical", "realtime", or "both". Default: "historical" |
timeRange | object | { start: number, end: number } |
Return
| Return | Type | Description |
|---|---|---|
data | RelayLog[] | Logs sorted ascending by timestamp |
isLoading | boolean | true while fetching historical data |
error | Error | null | Error if the fetch or stream failed |
Each RelayLog has deviceIdent, level ("info" \| "warn" \| "error"), message, and timestamp (Unix ms).
useRelayCommands()
Fetch command history for one or more devices. Polls on a configurable interval since there is no live command stream.
import { useRelayCommands } from "@relay-x/ui";
const { data, isLoading, error, refresh } = useRelayCommands({
name: "set_brightness",
deviceIdents: ["sensor_01", "sensor_02"],
timeRange: {
start: Date.now() - 86400000,
end: Date.now(),
},
refreshInterval: 30000,
});
Options
| Option | Type | Description |
|---|---|---|
name | string | The command name |
deviceIdents | string[] | Device identifiers to query |
timeRange | object | { start: number, end: number }. With refreshInterval, end slides to "now" on each tick |
refreshInterval | number | (optional) ms between re-fetches. Omit for a one-shot query |
Return
| Return | Type | Description |
|---|---|---|
data | RelayCommand[] | Commands sorted ascending by timestamp |
isLoading | boolean | true while fetching |
error | Error | null | Error if the fetch failed |
refresh | function | Trigger an immediate re-fetch without waiting for the next tick |
Each RelayCommand has name, deviceIdent, value, and timestamp.
useRelayAlerts()
Org-wide alert event feed. Returns a flat list of fire, resolved, and ack events that you can reduce by incident_id to render an incident-level UI.
import { useRelayAlerts } from "@relay-x/ui";
const { data, isLoading, error } = useRelayAlerts({
mode: "both",
filters: {
deviceIdents: ["sensor_01", "sensor_02"],
},
timeRange: {
start: Date.now() - 86400000,
end: Date.now(),
},
});
Options
| Option | Type | Description |
|---|---|---|
mode | string | "historical", "realtime", or "both". Default: "historical" |
filters.ruleIds | string[] | (optional) Match by rule id |
filters.deviceIdents | string[] | (optional) Match by device identifier |
filters.groupIds | string[] | (optional) Match by logical or hierarchy group. Stream-only — not applied to historical results |
timeRange | object | Required for "historical" and "both". Ignored for "realtime" |
Return
| Return | Type | Description |
|---|---|---|
data | RelayAlertEvent[] | Alert events with snake_case keys |
isLoading | boolean | true while fetching historical data |
error | Error | null | Error if the fetch or stream failed |
Each RelayAlertEvent has state ("fire" \| "resolved" \| "ack"), rule_id, rule_name, rule_type, device_id, device_ident, incident_id, timestamp, and an ack object on ack events. Stream events arrive pre-enriched, so rule_name, rule_type, and device_ident are populated without extra lookups.
In "both" mode, history loads silently to populate the current incident state, then the live stream takes over from now. If you trigger browser notifications or sounds from the data, gate them on event.timestamp > pageLoadTime so the silent reconciliation does not re-trigger alerts that already happened.
fire events keep arriving on the cooldown after an incident is acknowledged — the alerting engine continues publishing them for the audit trail. If you dispatch your own notifications from the alert feed, track ack state per incident_id and skip dispatch while the incident is in acknowledged. See the App SDK alerts reference for the full behavior.
useRelayPresence()
Track whether a device is online or offline.
import { useRelayPresence } from "@relay-x/ui";
const { online, lastEvent, isLoading, error } = useRelayPresence("sensor_01");
Return
| Return | Type | Description |
|---|---|---|
online | boolean | null | true if online, false if offline, null if unknown |
lastEvent | object | null | Last presence event with device_ident and event type |
isLoading | boolean | true while initial presence is being determined |
error | Error | null | Error if the presence subscription failed |