@thinger-io/dashboard-sdk is the package for dashboards built as web apps (React, Angular, Vue or plain TypeScript). The app runs inside a dashboard of the console with the session of the person viewing it, or on its own with a link that carries a token. The function reference on this page is generated from the type declarations of the package.
| Package | Installed from | Version | Source code |
|---|---|---|---|
@thinger-io/dashboard-sdk | npm | 0.3.1 | thinger-io/dashboard-sdk |
- Install: Get started, on this page. To build and publish a first app step by step, follow Build a dashboard app.
- Changelog: Dashboard SDK changelog: every release, with what it adds, changes and fixes.
The oldest server the SDK supports is Thinger.io 7.4, and every function of this version works on it.
Versions 0.x
While the SDK is in 0.x versions, its contract with the console can still change between minor versions. Check the changelog before you upgrade.
Get started
Create a project from a template of the SDK with the CLI, or add the package to an existing app:
npm install @thinger-io/dashboard-sdkThe package exports Thinger, a client that starts when the module is imported. Wait for Thinger.ready() and then call the modules:
import { Thinger, type BucketRecord } from '@thinger-io/dashboard-sdk';
await Thinger.ready();
// Devices of the account, or of the project the dashboard runs in
const devices = await Thinger.devices.list();
console.log(devices.map((device) => device.device));
// The last 6 hours of a Data Bucket, kept current as new data arrives
const stop = Thinger.buckets.subscribe('climate', { from: '6h', items: 500 }, (data, err) => {
if (err) return console.error(err);
const records: BucketRecord[] = data ?? [];
console.log(records.length);
});
// Stop the live updates when the view goes away
stop();climate stands for the ID of one of your Data Buckets. ready() rejects with code: 'no-session' when the page is opened outside the console without a standalone link.
Inside the console
A dashboard of the console can show your app instead of widgets. The console loads it in a sandboxed frame and hands it the session of the person viewing it: the app never handles credentials, and API calls run with that person's permissions and in their current project. Build the app with these rules:
- Use hash routing (
#/devices). The console keeps the route in its own URL, so reloading or sharing the console link reopens the same screen. - Use relative asset URLs (
base: './'in Vite) and no<base>tag. - Reload with
Thinger.reload(), notlocation.reload(): a plain reload gets no session. - Do not use
localStorage,sessionStorageor cookies; the frame has no storage of its own. Keep settings in properties. - Open other screens of the console with
Thinger.navigate(). - The app fills the content area of the console and scrolls inside it: design for that height instead of a fixed one.
- Files of a private file storage need
Thinger.storages.url(); a plain<img src>to them fails. - People who open a shared dashboard may lack some permissions: handle
status: 403errors.
Publish an app
The console loads the app from a file storage of your account:
- Upload the build folder to a public file storage with
index.htmlas its index. The frame cannot send an authorization header when it loads files, so a private storage does not work. - Point the dashboard at the storage folder, with the trailing slash:
/v1/users/YOUR_USER/storages/<storage>/files/. Without the slash the server answers with the file list. - For per-device or per-product dashboards, set the same source on the
dashboardproperty of that device or product.
thinger dashboard publish does the three steps from the build folder; see the CLI reference. Publishing again replaces the files; with --bump, consoles that have the app open reload it at once.
Standalone links
An app published to a public storage also works outside the console, for example embedded in another website. Pass the session in the fragment of the URL:
https://YOUR_SERVER/v1/users/YOUR_USER/storages/<storage>/files/#/<route>&thinger_token=YOUR_TOKEN&thinger_project=<project>| Parameter | Content |
|---|---|
thinger_token | Required. An access token (JWT); the SDK takes the user from it. Here YOUR_TOKEN is an access token of your account. |
thinger_api | Server address, URL-encoded, when the app is not served by the Thinger.io server: https://host[:port], optionally with a path. Without it, the page's own origin. |
thinger_project | Project the app runs in; API calls are scoped to it. |
thinger_device, thinger_product, thinger_type, thinger_group | Fill Thinger.context, as a device, product, asset type or asset group dashboard would. |
The fragment never reaches the server. The SDK reads the thinger_* parameters, removes them from the address bar and keeps them for the browser tab, so a reload keeps the session. There is no token renewal: when the token expires, calls fail with status: 401 and the app needs a new link. thinger dashboard publish --preview prints such links with a read-only token.
Permissions for a read-only token
A standalone link carries a token; give it only what the app needs. For a read-only app, create the token in the project of the dashboard and allow:
| Resource | Actions |
|---|---|
| Device | ListDevices, ReadDeviceConfig, ReadDeviceStatus, ListDeviceProperties, ReadDeviceProperty, ReadDeviceBucket |
| Bucket | ListBuckets, ReadBucket, ListBucketTags |
| Product, Type, Group, Project | List…, Read…Config, List…Properties, Read…Property of each one the app reads |
| Alarm | ListAlarmRules, ReadAlarmRuleConfig, ListAlarmInstances |
| Storage | ReadStorageFiles, for files of private storages |
Add actions such as AccessDeviceResources, SetDeviceProperty or UpdateAlarmInstance only if the app calls device resources, writes properties or changes alarms.
The live functions (buckets.subscribe, devices.subscribeProperty, devices.subscribeState, alarms.subscribe, alarms.rules.subscribe and events.subscribe) receive events over a WebSocket. An access token can subscribe only to the events of the permissions table, each with its read permission: ReadBucket or ReadDeviceBucket for bucket writes, ReadDeviceProperty for property updates, ReadDeviceConfig for device state, and ListAlarmInstances or ListAlarmRules on all alarms for alarm events. When the server refuses a subscription, reads still work, the data does not update live, and the SDK logs a warning in the browser console.
Errors
Every call rejects with a ThingerError. Its code says why; subscriptions pass the error to their callback instead.
code | Meaning | Extra field |
|---|---|---|
http | The server answered with an error status. The message reads HTTP <status> <reason>. | status |
unsupported | The server is older than the function needs. | requires: the minimum server version |
no-session | No session yet: called before ready(), or the page was opened without one. | |
invalid-argument | The call itself is wrong, for example device: true without a context device. | |
unexpected-response | The server answered something the SDK cannot use. |
import { Thinger, ThingerError } from '@thinger-io/dashboard-sdk';
try {
await Thinger.devices.get('YOUR_DEVICE');
} catch (err) {
if (err instanceof ThingerError && err.code === 'http' && err.status === 404) {
console.log('No such device');
}
}Time values
Options such as from and to take a time value:
| Value | Meaning |
|---|---|
| A number | Milliseconds since the epoch. |
A Date | That instant. |
'now' | The current time. |
'<n><unit>' | That long before now. Units: s seconds, m minutes, h hours, d days, w weeks, M months (30 days), y years (365 days). Examples: '30m', '6h', '7d', '1M'. |
Units are case-sensitive (m is minutes, M is months), and combined values such as '1h30m' are not accepted: they fail with code: 'invalid-argument'. Without from and to, a query has no time bound.
Script build
Deprecated
The script build thinger-dashboard.min.js, which defines a global Thinger for pages without a bundler, is deprecated and logs a warning. Install the package from npm and import { Thinger } from '@thinger-io/dashboard-sdk' in every module instead; the migration notes are in the changelog.
Thinger
Thinger is the client the package exports, an instance of ThingerClient. Its modules are listed under Modules.
Thinger.version
readonly version: string;SDK version (semver).
new ThingerClient
constructor(options?: ClientOptions);Thinger.apiBase
get apiBase(): string;Base URL of API calls ('' = the page's origin).
Thinger.apiHost
get apiHost(): string;Informational: the absolute API host the console reported.
Thinger.token
get token(): string | null;Viewer's access token.
Thinger.user
get user(): User | null;{ id } of the viewer.
Thinger.project
get project(): Project | null;Project the dashboard runs in; API calls are scoped to it (?project=).
Thinger.context
get context(): Context;The resource whose dashboard property is shown: { device, product, type, group, project }.
Thinger.dashboard
get dashboard(): DashboardRef | null;{ id, name } of the dashboard (inside the console).
Thinger.serverVersion
get serverVersion(): string | null;Server version (e.g. "7.5.0"), known after ready(); null when it could not be read.
Thinger.ready
ready(): Promise<ThingerClient>;Resolves once the session is there: the console handshake, or the standalone link. It also waits (up to 3 s) for the server version, so supports() is accurate afterwards. Rejects when the page has no session at all.
Thinger.onInit
onInit(fn: (sdk: ThingerClient) => void): void;Call fn(Thinger) on every init (and now, when already initialized).
Thinger.supports
supports(name: string): boolean;Is a function available on this server? name is its path from Thinger ('devices.list', 'alarms.rules.get'). Functions that need a newer server than this one answer false; the rest true. Accurate after ready(); before it, or when the server version cannot be read, only unknown names answer false.
Thinger.request
request<T = any>(path: string, opts?: RequestOptions): Promise<T>;API call: fetch() plus error handling and body parsing. opts.body: any JSON value, or a string, Blob, FormData or ArrayBuffer sent as is. opts.as forces the response type ('json', 'text', 'blob', 'arrayBuffer'); by default parsed JSON for JSON responses, text otherwise, null when empty. opts.project: false skips the project scoping. Throws a ThingerError HTTP <status> <reason> with status.
Thinger.fetch
fetch(path: string, options?: FetchOptions): Promise<Response>;Authenticated fetch(): adds Authorization: Bearer <token> and, in a project, ?project= (the server maps the URL's user to the project owner, so callers never need to know it). Inside the console, a 401 asks it for a new token and retries once. An absolute URL of another server goes out as a plain fetch: the token only travels to the Thinger server.
Thinger.navigate
navigate(target: NavigateTarget): void;Navigate the console: a path or URL, or one of { device }, { product }, { project }, { dashboard }, { url }. Standalone pages go to the URL themselves.
Thinger.reload
reload(): void;Reload the app. Use it instead of location.reload(): inside the console it keeps the key of the frame load, without which the reloaded page gets no token.
Modules
| Module | Description |
|---|---|
Thinger.events | Server events (bucket writes, property updates, alarms…) over one WebSocket shared by every subscription, /v2/users/{user}/events. It reconnects with backoff, refreshing the token first when it has expired. |
Thinger.buckets | Bucket data: read, write, live reads and tags. |
Thinger.devices | Devices: documents, properties, resources (read, call, poll, stream) and their events. |
Thinger.alarms | Alarm instances: list, live list, state changes. |
Thinger.alarms.rules | Alarm rules and their executions. |
Thinger.resources | Any kind of resource, listed or read by id. |
Thinger.properties | Properties of devices, products, projects, groups and types. Also a place for the dashboard's own settings (e.g. a project or product property). |
Thinger.products | Products and their API. |
Thinger.endpoints | Endpoints: email, webhooks, notifications… |
Thinger.storages | Files in file storages. Every call carries the viewer's token, so private storages work too. A plain <img src> to a private file goes without the token: use url(). |
Thinger.projects | Projects the viewer can open. Not scoped to the console's current project. |
Thinger.stats | Account and traffic statistics. |
Thinger.events
Server events (bucket writes, property updates, alarms…) over one WebSocket shared by every subscription, /v2/users/{user}/events. It reconnects with backoff, refreshing the token first when it has expired.
events.subscribe
subscribe<E extends ServerEvent = ServerEvent>(filter: EventFilter, cb: (event: E) => void, onReconnect?: () => void): Unsubscribe;Subscribe to server events. filter is { event, ...filters }, e.g. { event: 'bucket_write', bucket: 'b' }. Events sent while the socket is down are lost: onReconnect() runs each time it comes back after a drop.
Thinger.buckets
Bucket data: read, write, live reads and tags.
buckets.read
read<T = BucketRecord>(bucketId: string, opts?: BucketQuery): Promise<T[]>;Records { ts, ...fields }. opts.device reads the device-scoped view of a product bucket (a device id, or true for the context device): it only needs access to the device.
buckets.write
write(bucketId: string, value: unknown): Promise<unknown>;Write a record to a bucket (one whose data source is the API).
buckets.tags
tags(bucketId: string, opts?: {
device?: string | true | null;
}): Promise<any>;
tags(bucketId: string, tag: string, opts?: {
device?: string | true | null;
}): Promise<any>;Tags of a bucket, or the values seen for one tag (to build filters).
buckets.subscribe
subscribe<T = BucketRecord>(bucketId: string, cb: BucketCallback<T>): Unsubscribe;
subscribe<T = BucketRecord>(bucketId: string, opts: BucketSubscribeOptions, cb: BucketCallback<T>): Unsubscribe;read(bucketId, opts), then the same read kept current as the bucket is written (debounced by opts.debounceMs, 500 ms by default). Only what a write changes is read again; opts.refetch: true re-runs the whole read instead. cb(data, err, { added, reset }).
Thinger.devices
Devices: documents, properties, resources (read, call, poll, stream) and their events.
devices.list
list(query?: DeviceQuery): Promise<Device[]>;Devices, all pages (the API returns 50 per page). query filters: product, asset_type, asset_group, type, name, sort, order, labels ({ key: value }, or [key] for "has label"), limit.
devices.get
get(deviceId: string): Promise<Device>;One device, with its connection status.
devices.locations
locations(opts?: LocationsQuery): Promise<any[]>;Device locations for maps. Without options: every asset with a location. { group } / { type } narrow it to an asset group / type; { connected: true } returns only connected devices.
devices.stats
stats(deviceId: string): Promise<any>;Traffic usage of one device over the last 30 days.
devices.listProperties
listProperties(deviceId: string, query?: PropertyQuery): Promise<Property[]>;Properties of a device, inherited ones included: those carry source (type, group or product). query.id accepts a pattern ('temp*').
devices.getProperty
getProperty<T = any>(deviceId: string, propertyId: string): Promise<Property<T>>;One property: { property, value, source, created, modified, … }.
devices.setProperty
setProperty(deviceId: string, propertyId: string, value: unknown): Promise<any>;Create or update a property.
devices.deleteProperty
deleteProperty(deviceId: string, propertyId: string): Promise<boolean>;Remove a property (true also when it did not exist).
devices.getResource
getResource<T = any>(deviceId: string, resource: string, opts?: {
shadow?: boolean;
}): Promise<T>;Read a device resource. shadow: true returns the last value saved when the device disconnected.
devices.callResource
callResource<T = any>(deviceId: string, resource: string, input?: unknown): Promise<T>;Call a device resource (actuation / RPC) with input as its JSON body: any serializable value. Returns the device's response, or null when it sends none.
devices.pollResource
pollResource<T = any>(deviceId: string, resource: string, intervalMs: number, cb: (data: T | null, err: Error | null) => void): Unsubscribe;Read a resource every intervalMs. cb(data, err) per read.
devices.subscribeProperty
subscribeProperty(deviceId: string, propertyId: string, cb: (event: PropertyUpdateEvent) => void): Unsubscribe;Property update events of one property.
devices.subscribeState
subscribeState(deviceId: string, cb: (event: DeviceStateEvent) => void): Unsubscribe;Connection events; event.state is connected, disconnected, resumed, awake or asleep.
devices.streamResource
streamResource<T = any>(deviceId: string, resource: string, cb: (payload: T | null, err: Error | null, frame: StreamFrame) => void): Unsubscribe;
streamResource<T = any>(deviceId: string, resource: string, intervalMs: number, cb: (payload: T | null, err: Error | null, frame: StreamFrame) => void): Unsubscribe;The device pushes the resource every intervalMs (0 or omitted: when it changes), over the device's resources WebSocket. Intervals go to the device in whole seconds. Subscribers of one resource share its stream, at the GCD of their intervals. When the device is offline, cb gets the error and then the last known value (frame.signal === 'shadow'). Reconnects with backoff.
Thinger.alarms
Alarm instances: list, live list, state changes.
alarms.STATE
readonly STATE: Readonly<{
readonly NONE: 0;
readonly ACTIVATED: 1;
readonly ACKNOWLEDGED: 2;
readonly LATCHED: 3;
readonly SHELVED: 4;
readonly CLEARED: 5;
}>;Same as Alarms.STATE, reachable as Thinger.alarms.STATE.
alarms.SEVERITY
readonly SEVERITY: Readonly<{
readonly NONE: 0;
readonly LOW: 1;
readonly MEDIUM: 2;
readonly HIGH: 3;
readonly CRITICAL: 4;
}>;Same as Alarms.SEVERITY, reachable as Thinger.alarms.SEVERITY.
alarms.list
list(query?: AlarmQuery): Promise<AlarmInstance[]>;Alarm instances. query is sent as query params: exact values, nested fields with dots, an array matching any of its values ({ state: 1 }, { 'alarm.rule': 'r1', 'origin.id': ['d1', 'd2'] }).
alarms.get
get(instanceId: string): Promise<AlarmInstance>;alarms.stats
stats(): Promise<any>;Counts per state / severity.
alarms.subscribe
subscribe(cb: (instances: AlarmInstance[] | null, err: Error | null) => void): Unsubscribe;
subscribe(query: AlarmSubscribeQuery, cb: (instances: AlarmInstance[] | null, err: Error | null) => void): Unsubscribe;list(query) now, and again (debounced by query.debounceMs, 500 ms by default) whenever an instance is activated, normalized, updated (acknowledge, shelve, latch, clear, annotation; from this app or anywhere else), created or deleted. cb(instances, err).
alarms.onActivate
onActivate(cb: (event: ServerEvent) => void): Unsubscribe;Raw alarm_instance_activate events.
alarms.onNormalize
onNormalize(cb: (event: ServerEvent) => void): Unsubscribe;Raw alarm_instance_normalize events.
alarms.onUpdate
onUpdate(cb: (event: ServerEvent) => void): Unsubscribe;Raw alarm_instance_update events (state, annotation… changed).
alarms.update
update(instanceId: string, body: Record<string, unknown>): Promise<any>;Update an instance: usually its state (see STATE), with annotation / reactivation. The server validates transitions (a CLEARED alarm cannot be reactivated). The helpers below only differ in the state they set.
alarms.acknowledge
acknowledge(instanceId: string, opts?: AlarmUpdateOptions): Promise<any>;alarms.shelve
shelve(instanceId: string, opts?: AlarmUpdateOptions): Promise<any>;Shelve (usually with a reactivation timespan).
alarms.latch
latch(instanceId: string, opts?: AlarmUpdateOptions): Promise<any>;Latch (fires again after the configured timeout).
alarms.clear
clear(instanceId: string, opts?: AlarmUpdateOptions): Promise<any>;Clear (resolve). A cleared instance cannot be reactivated.
Thinger.alarms.rules
Alarm rules and their executions.
alarms.rules.list
list(query?: Record<string, unknown>): Promise<any[]>;alarms.rules.get
get(ruleId: string): Promise<any>;alarms.rules.subscribe
subscribe(cb: (event: ServerEvent) => void): Unsubscribe;alarm_rule_execute events (needs the account owner).
alarms.rules.onExecute
onExecute(cb: (event: ServerEvent) => void): Unsubscribe;Same as subscribe(cb).
Thinger.resources
Any kind of resource, listed or read by id.
resources.list
list<T = any>(kind: ResourceKind, query?: ListQuery): Promise<T[]>;Resources of a kind, following pagination. query is sent as filters (labels as in devices.list); query.limit caps how many are returned.
resources.get
get<T = any>(kind: ResourceKind, id: string): Promise<T>;One resource document.
Thinger.properties
Properties of devices, products, projects, groups and types. Also a place for the dashboard's own settings (e.g. a project or product property).
properties.list
list(kind: PropertyKind, id: string, query?: PropertyQuery): Promise<Property[]>;query.id accepts a pattern matching several properties ('temp*').
properties.get
get<T = any>(kind: PropertyKind, id: string, propertyId: string): Promise<Property<T>>;The full document { property, value, created, modified, … }.
properties.set
set(kind: PropertyKind, id: string, propertyId: string, value: unknown): Promise<any>;Create or update a property.
properties.delete
delete(kind: PropertyKind, id: string, propertyId: string): Promise<boolean>;Remove a property (true also when it did not exist).
Thinger.products
Products and their API.
products.list
list(query?: ListQuery): Promise<Product[]>;products.get
get(productId: string): Promise<Product>;products.call
call<T = any>(productId: string, resource: string, input?: unknown): Promise<T>;Call a resource of the product API (the functions of the product profile) with input as its JSON body. It reaches a device only if the resource defines a device_id_resolver.
Thinger.endpoints
Endpoints: email, webhooks, notifications…
endpoints.call
call<T = any>(endpointId: string, payload?: unknown): Promise<T>;Trigger an endpoint with an optional JSON payload.
Thinger.storages
Files in file storages. Every call carries the viewer's token, so private storages work too. A plain <img src> to a private file goes without the token: use url().
storages.list
list(storageId: string, path?: string): Promise<StorageFile[]>;Files of a storage, optionally inside path.
storages.read
read(storageId: string, path: string, opts: {
as: 'blob';
}): Promise<Blob>;
read(storageId: string, path: string, opts: {
as: 'arrayBuffer';
}): Promise<ArrayBuffer>;
read(storageId: string, path: string, opts: {
as: 'text';
}): Promise<string>;
read<T = any>(storageId: string, path: string, opts?: {
as?: 'json';
}): Promise<T>;File contents. opts.as: 'json', 'text', 'blob' or 'arrayBuffer'; by default JSON when the server says so and text otherwise. Use 'blob' / 'arrayBuffer' for binary files (PDF, images…).
storages.url
url(storageId: string, path: string): Promise<string>;A temporary blob: URL with the file, for <img>, <video>, <a href>… Revoke it with URL.revokeObjectURL(url) when no longer needed.
storages.download
download(storageId: string, path: string, filename?: string): Promise<void>;Save the file on the viewer's computer (filename defaults to the file's name).
storages.write
write(storageId: string, path: string, data: unknown): Promise<any>;Create or overwrite a file; folders on the path are created. data: a string, a Blob / File (e.g. from <input type="file">), an ArrayBuffer / typed array, or any other value, stored as JSON.
storages.open
open(storageId: string, path: string): Promise<void>;Open the file in a new browser tab (PDFs, images…), through the console: the sandboxed frame can neither open tabs nor show the browser's PDF viewer. Call it from a click handler, so the browser does not block the new tab.
storages.remove
remove(storageId: string, path: string): Promise<any>;Delete a file, or a folder with everything in it.
storages.publicUrl
publicUrl(storageId: string, path: string): string;Direct URL of a file in a PUBLIC storage, for <img src> or links without the SDK. Private storages need url().
Thinger.projects
Projects the viewer can open. Not scoped to the console's current project.
projects.list
list(): Promise<ProjectRef[]>;The viewer's own projects and the ones shared with them (those carry owner).
projects.get
get(projectId: string): Promise<ProjectRef>;Thinger.stats
Account and traffic statistics.
stats.account
account(): Promise<any>;Account statistics.
stats.usage
usage(deviceId?: string): Promise<any>;Traffic usage over the last 30 days: all devices, or one with deviceId.
Errors and values
ThingerError
declare class ThingerError extends Error { … }Error of every SDK call. API errors read HTTP <status> <reason>, with status set.
ThingerError.code
readonly code: ThingerErrorCode;ThingerError.status
readonly status?: number;HTTP status, for code: 'http'.
ThingerError.requires
readonly requires?: string;Minimum server version, for code: 'unsupported' (e.g. "7.6").
ThingerErrorCode
type ThingerErrorCode = 'http' | 'unsupported' | 'no-session' | 'invalid-argument' | 'unexpected-response';Why a call failed:
http: the server answered with an error status (statusset).unsupported: the server is older than the function needs (requiresset).no-session: no user or token yet (beforeready(), or opened without a session).invalid-argument: the call itself is wrong (e.g.device: truewithout a context device).unexpected-response: the server answered something the SDK cannot use.
ALARM_INSTANCE_EVENTS
declare const ALARM_INSTANCE_EVENTS: readonly ["alarm_instance_activate", "alarm_instance_normalize", "alarm_instance_create", "alarm_instance_update", "alarm_instance_delete"];Server events that change the alarm instance list.
ALARM_SEVERITY
declare const ALARM_SEVERITY: Readonly<{
readonly NONE: 0;
readonly LOW: 1;
readonly MEDIUM: 2;
readonly HIGH: 3;
readonly CRITICAL: 4;
}>;Numeric severity values of the backend ALARM_SEVERITY enum.
ALARM_STATE
declare const ALARM_STATE: Readonly<{
readonly NONE: 0;
readonly ACTIVATED: 1;
readonly ACKNOWLEDGED: 2;
readonly LATCHED: 3;
readonly SHELVED: 4;
readonly CLEARED: 5;
}>;Numeric state values of the backend ALARM_STATE enum.
BASE_SERVER_VERSION
declare const BASE_SERVER_VERSION = "7.4";Oldest server the SDK supports: everything without a declaration works there.
SDK_VERSION
declare const SDK_VERSION = "0.3.1";Thinger
declare const Thinger: ThingerClient;The SDK, ready to use after await Thinger.ready().
parseTime
declare function parseTime(value: Time | null | undefined): number | undefined;A time value as a milliseconds timestamp; durations count back from now.
Types
AlarmInstance
interface AlarmInstance {
instance: string;
name?: string;
severity: AlarmSeverity;
state: AlarmState;
/** alarm.rule is the rule id. */
alarm: {
rule: string;
[key: string]: unknown;
};
/** origin.id is the device. */
origin: {
source: 'device';
id: string;
name?: string;
};
asset_type?: string;
asset_group?: string;
project?: string[];
/** Timestamps in ms. */
activation?: {
detections?: number;
initiated?: number;
completed?: number;
};
normalization?: {
detections?: number;
initiated?: number;
completed?: number;
};
/** Values that triggered it: { "<source>.<field>": value }. */
evaluation?: {
last_values?: Record<string, unknown>;
[key: string]: unknown;
};
annotation?: string;
[key: string]: any;
}AlarmQuery
type AlarmQuery = Record<string, unknown>;Query of alarm instances: exact values, nested fields with dots, an array matching any.
AlarmSeverity
type AlarmSeverity = 0 | 1 | 2 | 3 | 4;AlarmState
type AlarmState = 0 | 1 | 2 | 3 | 4 | 5;AlarmSubscribeQuery
interface AlarmSubscribeQuery extends AlarmQuery {
/** Wait for changes to settle before listing again (default 500 ms). */
debounceMs?: number;
}AlarmUpdateOptions
interface AlarmUpdateOptions {
annotation?: string;
reactivation?: {
type: 'timespan';
timespan: {
value: number;
magnitude: 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year';
};
};
}BucketCallback
type BucketCallback<T = BucketRecord> = (data: T[] | null, err: Error | null, change?: BucketChange<T>) => void;BucketChange
interface BucketChange<T = BucketRecord> {
/** Records new or updated in this call (aggregation windows replace the old one), in `data` order. */
added: T[];
/** `data` comes from a full read: the first call, refetch, or after an error. */
reset: boolean;
}Third argument of the buckets.subscribe callback.
BucketQuery
interface BucketQuery {
/** Device id, or `true` for the device of the dashboard context. */
device?: string | true | null;
from?: Time;
to?: Time;
/** Max 1000 per call; the server returns 50 when not set. */
items?: number;
/** 'desc' (default) or 'asc'. */
sort?: 'asc' | 'desc';
/** Aggregation period: '5m', '1h', '1d', '1w', '1mo', '1y'… */
aggregation?: string;
/** Aggregation function: 'mean', 'max', 'min', 'sum', 'count', 'first', 'last', 'mode'… */
aggregator?: string;
transform?: string;
/** Fields to return, e.g. ['temperature']. */
fields?: string | string[];
/** Group results, e.g. 'device'. */
groupBy?: string | string[];
/** IANA zone where aggregation windows start (default: the viewer's browser zone). */
timeZone?: string;
/** Tag filters: one value, or any of several. */
tags?: Record<string, string | number | Array<string | number>>;
}BucketRecord
interface BucketRecord {
ts: number;
[field: string]: any;
}A bucket record: ts (ms) plus the stored fields.
BucketSubscribeOptions
interface BucketSubscribeOptions extends BucketQuery {
/** Wait for writes to settle before updating (default 500 ms). */
debounceMs?: number;
/** Re-run the whole read on each write instead of reading only what changed. */
refetch?: boolean;
}ClientOptions
interface ClientOptions {
/** The page window; none (server-side rendering) leaves the SDK idle. */
window?: Window | null;
fetch?: typeof fetch;
WebSocket?: typeof WebSocket;
/** Wait at most this long for the server version before `ready()` resolves (ms, default 3000). */
versionTimeoutMs?: number;
/** Inside a frame, reject `ready()` when no `thinger:init` arrives in this time (ms, default 10000). */
handshakeTimeoutMs?: number;
}What the SDK takes from its environment. Defaults: the page's own globals.
Context
interface Context {
device?: string | null;
product?: string | null;
type?: string | null;
group?: string | null;
project?: string | null;
}The resource whose dashboard property is being shown (null or missing when not set).
DashboardRef
interface DashboardRef {
id: string;
name?: string;
}The dashboard being shown.
Device
interface Device {
device: string;
name?: string;
description?: string;
type?: string;
product?: string;
asset_type?: string;
asset_group?: string;
labels?: Record<string, string>;
enabled?: boolean;
connection?: DeviceConnection;
[key: string]: any;
}DeviceConnection
interface DeviceConnection {
active?: boolean;
ts?: number;
location?: {
lat?: number;
lon?: number;
[key: string]: unknown;
};
[key: string]: any;
}DeviceQuery
interface DeviceQuery extends ListQuery {
product?: string;
asset_type?: string;
asset_group?: string;
type?: string;
/** `{ key: value }` for exact matches, or `[key]` for devices that have the label. */
labels?: Record<string, string> | string[];
}DeviceState
type DeviceState = 'connected' | 'disconnected' | 'resumed' | 'awake' | 'asleep';DeviceStateEvent
interface DeviceStateEvent {
event: 'device_state_change';
device: string;
product?: string;
state: DeviceState;
ts?: number;
[key: string]: any;
}EventFilter
interface EventFilter {
event?: string;
[key: string]: any;
}{ event, ...filters }, e.g. { event: 'bucket_write', bucket: 'b' }.
FetchOptions
interface FetchOptions extends RequestInit {
/** false: do not add the console's current `?project=` to this call. */
project?: false;
}Options of fetch().
ListQuery
interface ListQuery {
name?: string;
sort?: string;
order?: 'asc' | 'desc';
/** Maximum number of items; all pages are fetched otherwise. */
limit?: number;
[filter: string]: unknown;
}LocationsQuery
interface LocationsQuery {
group?: string;
type?: string;
connected?: boolean;
}NavigateTarget
type NavigateTarget = string | {
device: string;
} | {
product: string;
} | {
project: string;
} | {
dashboard: string;
} | {
url: string;
};Where navigate() asks the console to go: a path or URL, or one resource.
Product
interface Product {
product: string;
name?: string;
description?: string;
enabled?: boolean;
config?: Record<string, unknown>;
profile?: Record<string, unknown>;
[key: string]: any;
}Project
interface Project {
id: string;
owner?: string;
name?: string;
}Project the dashboard runs in.
ProjectRef
interface ProjectRef {
project: string;
name?: string;
description?: string;
/** Set on projects shared with the viewer: the account that owns them. */
owner?: string;
[key: string]: any;
}Property
interface Property<T = unknown> {
property: string;
name?: string;
description?: string;
value: T;
/**
* Device properties only: the level the value comes from, the first that defines it
* (device → asset type → asset group → product). `listProperties` leaves it out for the
* device's own properties.
*/
source?: PropertySource;
created?: string;
modified?: string;
[key: string]: any;
}PropertyKind
type PropertyKind = 'device' | 'product' | 'project' | 'group' | 'type';PropertyQuery
interface PropertyQuery {
/** Property id, or a pattern matching several ('temp*'). */
id?: string;
}PropertySource
type PropertySource = 'device' | 'type' | 'group' | 'product';Level a device property's value comes from: the first that defines it.
PropertyUpdateEvent
interface PropertyUpdateEvent {
event: 'device_property_update';
device: string;
property: string;
value?: unknown;
ts?: number;
[key: string]: any;
}RequestOptions
interface RequestOptions extends Omit<RequestInit, 'body'> {
/** Any JSON value (serialized automatically), or a string / Blob / FormData / ArrayBuffer sent as is. */
body?: unknown;
/** Force the response type. Default: parsed JSON for JSON responses, text otherwise, null when empty. */
as?: 'json' | 'text' | 'blob' | 'arrayBuffer';
/** false: do not add the console's current `?project=` to this call. */
project?: false;
}Options of request().
ResourceKind
type ResourceKind = 'device' | 'product' | 'bucket' | 'group' | 'type' | 'project' | 'dashboard' | 'endpoint' | 'storage' | 'alarm_rule';ServerEvent
interface ServerEvent {
event?: string;
[key: string]: any;
}A server event (bucket_write, alarm_instance_update…): event plus its fields.
StandaloneParams
type StandaloneParams = Partial<Record<StandaloneParam, string>>;Values of a standalone link.
StorageFile
interface StorageFile {
name: string;
path: string;
size: number;
type: 'file' | 'directory';
[key: string]: any;
}StreamFrame
interface StreamFrame {
/** 'shadow': the last value saved when the device disconnected, sent once while it is offline. */
signal: 'data' | 'error' | 'shadow';
resource?: string;
ts?: number;
error?: string;
payload?: any;
[key: string]: any;
}Envelope of a value delivered by streamResource.
Time
type Time = number | Date | string;Milliseconds timestamp, Date, 'now', or a duration before now ('30m', '6h', '7d', '1M'…).
Unsubscribe
type Unsubscribe = () => void;Returned by every subscribe / poll / stream call: stop it.
User
interface User {
id: string;
}The viewer.