@lox-nexus/plugin-sdk/wire
The host/worker protocol: the messages between Nexus and a plugin worker, validated with zod on both sides, and the shapes they carry (catalog, panel, HTTP request and response). Most plugins only need the types; the messages are for tools and hosts.
Interfaces
PluginWorkerData
workerData the host passes to every plugin worker, so a module can tell it was started by Nexus
Properties
loxNexus
loxNexus:
number
Type Aliases
HostFeature
HostFeature = typeof
HOST_FEATURES[keyof typeofHOST_FEATURES]
SignalSpec
SignalSpec =
z.infer<typeofSignalSpecSchema>
HostMessage
HostMessage =
z.infer<typeofHostMessageSchema>
ActionAnswer
ActionAnswer =
z.infer<typeofActionAnswerSchema>
JsonSchema
JsonSchema =
Record<string,unknown>
A JSON Schema document, as produced by zod's toJSONSchema.
CapabilityDescription
CapabilityDescription =
z.infer<typeofCapabilityDescriptionSchema>
ActionDescription
ActionDescription =
z.infer<typeofActionDescriptionSchema>
PluginDescription
PluginDescription =
z.infer<typeofPluginDescriptionSchema>
PluginHealth
PluginHealth =
z.infer<typeofPluginHealthSchema>
LogLevel
LogLevel =
z.infer<typeofLogLevelSchema>
WorkerMessage
WorkerMessage =
z.infer<typeofWorkerMessageSchema>
Variables
HOST_FEATURES
constHOST_FEATURES:object
What a host understands beyond wire version 1, sent in init (hostFeatures). Plugins don't
check these: the runtime does, and falls back to what older hosts understand. A host that sends
none is one from before them.
Type Declaration
panelV2
readonlypanelV2:"panel.v2"="panel.v2"
tiles, notices, cells with tones and times, controls on key-value items and switch or instant buttons
panelPush
readonlypanelPush:"panel.push"="panel.push"
the panel.changed message (Plugin.panelChanged)
actionResult
readonlyactionResult:"action.result"="action.result"
action answers with a panel part
actionProgress
readonlyactionProgress:"action.progress"="action.progress"
the action.progress message (Action.progress)
WIRE_VERSION
constWIRE_VERSION:1=1
bumped on any incompatible change; a worker announces its version in hello
SignalSpecSchema
constSignalSpecSchema:ZodObject<{id:ZodString;kind:ZodEnum<{sensor:"sensor";actuator:"actuator"; }>;capability:ZodString;params:ZodUnknown;label:ZodOptional<ZodString>; },$strip>
A configured sensor or actuator, as the host knows it.
HttpRequestSchema
constHttpRequestSchema:ZodObject<{method:ZodString;path:ZodString;query:ZodRecord<ZodString,ZodString>;headers:ZodRecord<ZodString,ZodString>;body:ZodString; },$strip>
An HTTP request to the plugin's webhook routes; path is below /hook/
HostMessageSchema
constHostMessageSchema:ZodDiscriminatedUnion<[ZodObject<{type:ZodLiteral<"init">;instanceId:ZodString;slug:ZodOptional<ZodString>;config:ZodUnknown;store:ZodRecord<ZodString,ZodUnknown>;timeZone:ZodOptional<ZodString>;logLevel:ZodOptional<ZodEnum<{error:"error";warn:"warn";info:"info";debug:"debug"; }>>;configVersion:ZodOptional<ZodInt>;hostFeatures:ZodOptional<ZodArray<ZodString>>; },$strip>,ZodObject<{type:ZodLiteral<"describe">;requestId:ZodInt; },$strip>,ZodObject<{type:ZodLiteral<"signal.add">;requestId:ZodInt;signal:ZodObject<{id:ZodString;kind:ZodEnum<{sensor:"sensor";actuator:"actuator"; }>;capability:ZodString;params:ZodUnknown;label:ZodOptional<ZodString>; },$strip>; },$strip>],"type">
Messages from the host to a plugin worker. Requests carry a requestId and are answered with
one response; the others are notifications.
ToneSchema
constToneSchema:ZodEnum<{error:"error";ok:"ok";warn:"warn";info:"info";muted:"muted"; }>
How a value reads at a glance: "ok" green, "warn" amber, "error" red, "info" blue, "muted" grey. The UI shows it with a dot and the colour, never by colour alone.
CellSchema
constCellSchema:ZodUnion<readonly [ZodString,ZodObject<{text:ZodOptional<ZodString>;tone:ZodOptional<ZodEnum<{error:"error";ok:"ok";warn:"warn";info:"info";muted:"muted"; }>>;until:ZodOptional<ZodNumber>;since:ZodOptional<ZodNumber>;at:ZodOptional<ZodNumber>; },$strip>]>
A panel value: plain text, or text with a tone, and a time the browser counts down to (until,
"in 12:30"), up from (since, "for 3 min", how long a state lasts) or shows as a moment (at,
"12 s ago", e.g. the last poll) while the page is open, with the exact time on hover; times are
epoch ms. A host without the "panel.v2" feature gets the text (a time as an absolute one) instead.
RowActionSchema
constRowActionSchema:ZodObject<{action:ZodString;label:ZodOptional<ZodString>;input:ZodOptional<ZodRecord<ZodString,ZodUnknown>>;instant:ZodOptional<ZodBoolean>;switch:ZodOptional<ZodObject<{checked:ZodBoolean; },$strip>>; },$strip>
A button for an action, on a table row, a key-value item or a section's heading; it opens the
action's dialog with input filled in (its keys must be fields of the action's input schema).
instant: runs at once without the dialog (after the action's confirm, if it has one), for
actions whose input the button fills in completely. switch: shown as a switch in the state
checked; flipping it runs the action with input at once (after the action's confirm, too),
so put the new state in input.
PanelPartSchema
constPanelPartSchema:ZodDiscriminatedUnion<[ZodObject<{type:ZodLiteral<"keyValue">;items:ZodArray<ZodObject<{label:ZodString;value:ZodUnion<readonly [ZodString,ZodObject<{text: ...;tone: ...;until: ...;since: ...;at: ...; },$strip>]>;control:ZodOptional<ZodObject<{action:ZodString;label:ZodOptional<...>;input:ZodOptional<...>;instant:ZodOptional<...>;switch:ZodOptional<...>; },$strip>>;catalog:ZodOptional<ZodString>; },$strip>>; },$strip>,ZodObject<{type:ZodLiteral<"table">;columns:ZodArray<ZodString>;rows:ZodArray<ZodArray<ZodUnion<readonly [ZodString,ZodObject<{text:ZodOptional<...>;tone:ZodOptional<...>;until:ZodOptional<...>;since:ZodOptional<...>;at:ZodOptional<...>; },$strip>]>>>;rowActions:ZodOptional<ZodArray<ZodArray<ZodObject<{action:ZodString;label:ZodOptional<ZodString>;input:ZodOptional<ZodRecord<..., ...>>;instant:ZodOptional<ZodBoolean>;switch:ZodOptional<ZodObject<..., ...>>; },$strip>>>>;rowCatalog:ZodOptional<ZodArray<ZodNullable<ZodString>>>; },$strip>,ZodObject<{type:ZodLiteral<"json">;data:ZodUnknown; },$strip>],"type">
One part of a panel; a section holds one of these.
PanelSchema
constPanelSchema:ZodDiscriminatedUnion<[ZodObject<{type:ZodLiteral<"keyValue">;items:ZodArray<ZodObject<{label:ZodString;value:ZodUnion<readonly [ZodString,ZodObject<{text: ...;tone: ...;until: ...;since: ...;at: ...; },$strip>]>;control:ZodOptional<ZodObject<{action:ZodString;label:ZodOptional<...>;input:ZodOptional<...>;instant:ZodOptional<...>;switch:ZodOptional<...>; },$strip>>;catalog:ZodOptional<ZodString>; },$strip>>; },$strip>,ZodObject<{type:ZodLiteral<"table">;columns:ZodArray<ZodString>;rows:ZodArray<ZodArray<ZodUnion<readonly [ZodString,ZodObject<{text:ZodOptional<...>;tone:ZodOptional<...>;until:ZodOptional<...>;since:ZodOptional<...>;at:ZodOptional<...>; },$strip>]>>>;rowActions:ZodOptional<ZodArray<ZodArray<ZodObject<{action:ZodString;label:ZodOptional<ZodString>;input:ZodOptional<ZodRecord<..., ...>>;instant:ZodOptional<ZodBoolean>;switch:ZodOptional<ZodObject<..., ...>>; },$strip>>>>;rowCatalog:ZodOptional<ZodArray<ZodNullable<ZodString>>>; },$strip>,ZodObject<{type:ZodLiteral<"json">;data:ZodUnknown; },$strip>],"type">
A declarative status view a plugin shows on its page, returned by Plugin.panel(); plugins ship no UI code:
{ type: "keyValue", items: [{ label: "Firmware", value: "1.34" }, { label: "Inverter", value: { text: "running", tone: "ok" } }] }
{ type: "table", columns: ["Meter", "Power"], rows: [["Grid", "412 W"]], rowActions: [[{ action: "reset", label: "Reset" }]] }
{ type: "tiles", tiles: [{ label: "PV", value: 3200, unit: "W" }, { label: "Battery", value: 82, unit: "%", min: 0, max: 100 }] }
{ type: "notice", tone: "warn", title: "Not paired yet", text: "Scan the code in the Alexa app" }
{ type: "qr", data: "MT:Y.K90…", caption: "Scan in the Alexa app" }
{ type: "json", data: { anything: "JSON-serialisable" } }
{ type: "sections", sections: [{ title: "Inverter", panel: { type: "keyValue", items } }, { title: "Meters", panel: { type: "table", columns, rows } }] }
Values are cells: text, or text with a tone and a live countdown (see Cell). Buttons (see
RowAction) go on table rows (rowActions), key-value items (control) and section headings
(actions). A row or item that stands for something in the catalog (a client, a device) can
point at its catalog node (rowCatalog, an item's catalog) for a "Signals" button that adds
its signals directly. Hosts without the "panel.v2" feature get tiles and notices as key-value lists,
cells as text and no item controls; the runtime converts them. Answers must be JSON: format
BigInt and dates as text. Never put secrets in a panel.
ActionAnswerSchema
constActionAnswerSchema:ZodUnion<readonly [ZodString,ZodObject<{message:ZodOptional<ZodString>;panel:ZodOptional<ZodDiscriminatedUnion<[ZodObject<{type:ZodLiteral<"keyValue">;items:ZodArray<ZodObject<..., ...>>; },$strip>,ZodObject<{type:ZodLiteral<"table">;columns:ZodArray<ZodString>;rows:ZodArray<ZodArray<...>>;rowActions:ZodOptional<ZodArray<...>>;rowCatalog:ZodOptional<ZodArray<...>>; },$strip>,ZodObject<{type:ZodLiteral<"json">;data:ZodUnknown; },$strip>],"type">>; },$strip>]>
What an action answers: a text, or (on hosts with the "action.result" feature) a message with a panel part shown under it, e.g. the QR code of a pairing window. Older hosts get the message.
CapabilityDescriptionSchema
constCapabilityDescriptionSchema:ZodObject<{id:ZodString;label:ZodString;description:ZodOptional<ZodString>;params:ZodRecord<ZodString,ZodUnknown>;valueType:ZodUnion<readonly [ZodEnum<{string:"string";number:"number";boolean:"boolean";null:"null";singleChannel:"singleChannel";rgbw:"rgbw";tunableWhite:"tunableWhite";t5:"t5"; }>,ZodLiteral<"any">]>;unit:ZodCatch<ZodOptional<ZodString>>;decimals:ZodCatch<ZodOptional<ZodInt>>;staleAfterMin:ZodCatch<ZodOptional<ZodInt>>; },$strip>
A sensor or actuator a plugin offers, ready to be configured with params.
ActionDescriptionSchema
constActionDescriptionSchema:ZodObject<{id:ZodString;label:ZodString;description:ZodOptional<ZodString>;input:ZodRecord<ZodString,ZodUnknown>;confirm:ZodOptional<ZodString>;placement:ZodOptional<ZodEnum<{panel:"panel";toolbar:"toolbar"; }>>; },$strip>
An action a user can start from the instance page (static actions).
PluginDescriptionSchema
constPluginDescriptionSchema:ZodObject<{config:ZodRecord<ZodString,ZodUnknown>;sensors:ZodArray<ZodObject<{id:ZodString;label:ZodString;description:ZodOptional<ZodString>;params:ZodRecord<ZodString,ZodUnknown>;valueType:ZodUnion<readonly [ZodEnum<{string:"string";number:"number";boolean:"boolean";null:"null";singleChannel:"singleChannel";rgbw:"rgbw";tunableWhite:"tunableWhite";t5:"t5"; }>,ZodLiteral<"any">]>;unit:ZodCatch<ZodOptional<ZodString>>;decimals:ZodCatch<ZodOptional<ZodInt>>;staleAfterMin:ZodCatch<ZodOptional<ZodInt>>; },$strip>>;actuators:ZodArray<ZodObject<{id:ZodString;label:ZodString;description:ZodOptional<ZodString>;params:ZodRecord<ZodString,ZodUnknown>;valueType:ZodUnion<readonly [ZodEnum<{string:"string";number:"number";boolean:"boolean";null:"null";singleChannel:"singleChannel";rgbw:"rgbw";tunableWhite:"tunableWhite";t5:"t5"; }>,ZodLiteral<"any">]>;unit:ZodCatch<ZodOptional<ZodString>>;decimals:ZodCatch<ZodOptional<ZodInt>>;staleAfterMin:ZodCatch<ZodOptional<ZodInt>>; },$strip>>;watches:ZodOptional<ZodObject<{label:ZodString;description:ZodOptional<ZodString>; },$strip>>;actions:ZodOptional<ZodArray<ZodObject<{id:ZodString;label:ZodString;description:ZodOptional<ZodString>;input:ZodRecord<ZodString,ZodUnknown>;confirm:ZodOptional<ZodString>;placement:ZodOptional<ZodEnum<{panel:"panel";toolbar:"toolbar"; }>>; },$strip>>>;testable:ZodOptional<ZodLiteral<true>>;panelPush:ZodOptional<ZodLiteral<true>>;discoverable:ZodOptional<ZodArray<ZodString>>; },$strip>
The answer to describe: everything a UI needs to configure a plugin without loading it.
CatalogEntrySchema
constCatalogEntrySchema:ZodObject<{kind:ZodEnum<{sensor:"sensor";actuator:"actuator"; }>;capability:ZodString;params:ZodUnknown;label:ZodString;value:ZodCatch<ZodOptional<ZodType<Value,unknown,$ZodTypeInternals<Value,unknown>>>>;unit:ZodCatch<ZodOptional<ZodString>>; },$strip>
A ready-to-add sensor or actuator in a catalog: the capability with its params filled in.
CatalogNodeSchema
constCatalogNodeSchema:z.ZodType<CatalogNode>
DiscoveredSchema
constDiscoveredSchema:ZodArray<ZodObject<{label:ZodString;value:ZodString;detail:ZodOptional<ZodString>; },$strip>>
What static discover found for a config field, e.g. Home Assistant servers on the network:
value is what the field gets when the user picks it, label and detail what the list shows.
HttpResponseSchema
constHttpResponseSchema:ZodObject<{status:ZodInt;headers:ZodDefault<ZodRecord<ZodString,ZodString>>;body:ZodDefault<ZodString>; },$strip>
What a webhook handler answers (see PluginHttp.route); body is utf-8 text, headers and body default to empty.
WatchDeclarationSchema
constWatchDeclarationSchema:ZodObject<{label:ZodString;description:ZodOptional<ZodString>; },$strip>
What a plugin that watches signals declares (static readonly watches): shown to the user where
they choose the signals it receives.
WatchedSignalSchema
constWatchedSignalSchema:ZodObject<{id:ZodString;topic:ZodString;label:ZodString;valueType:ZodUnion<readonly [ZodEnum<{string:"string";number:"number";boolean:"boolean";null:"null";singleChannel:"singleChannel";rgbw:"rgbw";tunableWhite:"tunableWhite";t5:"t5"; }>,ZodLiteral<"any">]>;owner:ZodOptional<ZodObject<{kind:ZodEnum<{miniserver:"miniserver";plugin:"plugin"; }>;id:ZodString; },$strip>>;unit:ZodOptional<ZodString>;decimals:ZodOptional<ZodInt>; },$strip>
A signal the user chose for a watching plugin: any signal of Nexus, a Miniserver's or another plugin's.
WatchedValueSchema
constWatchedValueSchema:ZodObject<{signalId:ZodString;ts:ZodNumber;kind:ZodEnum<{state:"state";command:"command"; }>;value:ZodType<Value,unknown,$ZodTypeInternals<Value,unknown>>;forced:ZodOptional<ZodLiteral<true>>;initial:ZodOptional<ZodLiteral<true>>; },$strip>
One value of a watched signal, as subscribers of the bus saw it.
PluginHealthSchema
constPluginHealthSchema:ZodEnum<{running:"running";degraded:"degraded"; }>
The status a plugin reports for itself; the host adds its own lifecycle states around it.
LogLevelSchema
constLogLevelSchema:ZodEnum<{error:"error";warn:"warn";info:"info";debug:"debug"; }>
log levels in increasing severity; the host decides which ones it keeps
HelloSchema
constHelloSchema:ZodObject<{type:ZodLiteral<"hello">;version:ZodInt;features:ZodOptional<ZodArray<ZodString>>; },$strip>
The worker's first message: the wire version it was built with, and the additions to that version it understands, so the host sends them only to workers that do ("log.level"). Frozen across wire versions (ADR 0019): a host reads it before it knows which version's messages follow.
WorkerMessageSchema
constWorkerMessageSchema:ZodDiscriminatedUnion<[ZodObject<{type:ZodLiteral<"hello">;version:ZodInt;features:ZodOptional<ZodArray<ZodString>>; },$strip>,ZodObject<{type:ZodLiteral<"ready">; },$strip>,ZodObject<{type:ZodLiteral<"failed">;error:ZodString; },$strip>],"type">
Messages from a plugin worker to the host. The host validates every one of them: a worker that sends something else is treated as broken.
References
HttpRequest
Re-exports HttpRequest
CatalogEntry
Re-exports CatalogEntry
CatalogNode
Re-exports CatalogNode
Discovered
Re-exports Discovered
HttpResponse
Re-exports HttpResponse
Cell
Re-exports Cell
Panel
Re-exports Panel
PanelPart
Re-exports PanelPart
RowAction
Re-exports RowAction
Tone
Re-exports Tone
WatchDeclaration
Re-exports WatchDeclaration
WatchedSignal
Re-exports WatchedSignal
WatchedValue
Re-exports WatchedValue