Watching signals (exporters)
Read this when your plugin should receive values (an exporter) instead of offering them.
Normally a plugin only offers signals. A watching plugin does the reverse: it receives the values
of signals the user chooses, Loxone signals, other plugins' sensors and actuators, anything on the
bus. That is how an exporter (InfluxDB, MQTT, a CSV file) works. Declare it and implement watched():
import { Plugin, type WatchedValue } from "@lox-nexus/plugin-sdk"
export default class InfluxPlugin extends Plugin<InfluxConfig> {
static readonly watches = { label: "Exported signals", description: "Values of these signals are written to InfluxDB" }
override async watched(values: readonly WatchedValue[], dropped: number) {
// oldest first; this.watch.signal(id) has the signal's label, topic, type and owner
for (const value of values) {
const signal = this.watch.signal(value.signalId)
if (signal) this.buffer.add(signal, value)
}
if (dropped) this.log.warn(`${dropped} values were dropped while the export was behind`)
}
override watchChanged() {
// optional: this.watch.signals() is the new list (chosen, left out, renamed)
}
}
watchesis the declaration (label1 to 100 characters,descriptionup to 500). Without it the plugin is never given values, even if it implementswatched().- The user decides what the plugin sees, on the instance's page: all signals or chosen ones, whole Miniservers or plugin instances, single signals. The plugin's config says how it exports, never what. Until the user chooses, the plugin gets nothing.
this.watchdescribes the chosen signals:signals()is the list andsignal(id)one entry withid,topic,label,valueTypeandowner. It is empty until the first list arrives, which happens afterstart(). A topic changes when the signal or its owner is renamed.watchChanged()is called first, with the list ready inthis.watch, then on every change (chosen, left out, renamed). It is optional to override.watched(values, dropped)gets batches of up to 1000 values, oldest first. The first batch holds the current value of each chosen signal (initial: true; actuators have none unless forced), and a signal added to the choice later also starts with its current value. After that come the changes as the bus shows them: forced values are markedforced, and commands to actuators havekind: "command".tsis epoch milliseconds.- Callbacks never overlap, and a batch always meets the list it was sent with. On shutdown, a
running
watched()finishes before the plugin is disposed. - Delivery is paced by the plugin. The next batch comes once
watched()returns (or its promise settles), and Nexus sends at most two batches ahead. While a plugin is slower than the values arrive, Nexus keeps a bounded queue (20 000 values) and drops the oldest;droppedsays how many since the last batch. A throw inwatched()is logged and doesn't stop the feed. Keep your own buffer bounded too. - Trust. A watching plugin sees values of other systems, for example alarm states. The install review and the instance page say so. Secrets never travel on the bus.
- Testing.
host.watchSignals([...])sets the chosen signals, andawait host.pushValues([...])delivers a batch and resolves whenwatched()finished. See Testing.