Lox Nexus

Abstract Class: Actuator<P, Params, V>

A command going to the device (switch a plug, set a dimmer). Write one actuator class per kind of command and list it in the plugin's static actuators. Each configured actuator is one instance; run() is called with the value converted to accepts, and its outcome becomes a result message on the bus. The class declares itself with static fields (see ActuatorClass; only id is required).

Like a sensor, an actuator is created when its signal is added, disposed when it is removed or the plugin stops, and recreated when its params are edited. Right after a (re)start the host may call run() with a value the user forced earlier, so run() must cope with being called as soon as start() has resolved.

Example

Switches the smart plug on or off; a wire turns 1/0 from a Loxone output into true/false first:

export class SwitchActuator extends Actuator<SmartPlugPlugin, Record<string, never>, ValueOf<"boolean">> {
  static readonly id = "switch"
  static readonly label = "Switch"
  static readonly accepts = "boolean"
  // someone pressing a button five times only needs the last press to reach the plug
  static readonly concurrency = "latest"
  async run(value: ValueOf<"boolean">) {
    await this.plugin.client.switch(value.value)
  }
}

Extends

Type Parameters

P

P extends Plugin = Plugin

Params

Params = unknown

V

V extends Value = Value

Constructors

Constructor

new Actuator<P, Params, V>(plugin, params, signal?): Actuator<P, Params, V>

params are validated against the static params schema before construction. signal names the configured signal this actuator is (for panels and log lines); a subclass that declares its own constructor must pass it on to super().

Parameters

plugin

P

params

Params

signal?

SignalInfo = ...

Returns

Actuator<P, Params, V>

Overrides

Disposable.constructor

Properties

MAX_TIMER_MS

readonly static MAX_TIMER_MS: 2147483647 = 2_147_483_647

the largest delay setTimeout/setInterval honour; Node turns anything longer into 1 ms

Inherited from

Disposable.MAX_TIMER_MS


plugin

readonly plugin: P


params

readonly params: Params


signal

readonly signal: SignalInfo

Accessors

isDisposed

Get Signature

get isDisposed(): boolean

true from the moment dispose() starts, not when it finishes

Returns

boolean

Inherited from

Disposable.isDisposed


clock

Get Signature

get clock(): Clock

the plugin's clock: this.clock.now() instead of Date.now(); every() and after() run on it

Returns

Clock

Methods

dispose()

dispose(): Promise<void>

Releases everything registered with this object. Plugin code seldom needs it: the runtime disposes plugins, sensors and actuators, and own() disposes children. Call it yourself for an object you created without own(), or to end a child early.

Runs all teardowns in reverse registration order and keeps going when one fails. Idempotent: every call returns the same promise, so a second caller also waits for async teardowns.

Returns

Promise<void>

Throws

AggregateError collecting every teardown error, after all teardowns have run

Inherited from

Disposable.dispose


onDispose()

protected onDispose(teardown): void

Registers a callback that runs on dispose(), for cleanup the helpers don't cover (closing a socket, flushing a buffer). Async callbacks are awaited one after the other, last registered first, so a flush can use what was registered before it. Keep the total under the five seconds a stop has.

Parameters

teardown

Teardown

Returns

void

Throws

Error if already disposed

Inherited from

Disposable.onDispose


useClock()

protected useClock(clock): void

Sets the clock for every() and after() calls from now on, unless one is set already; without one they run on the system clock. Timers started before keep the clock they started on. Plugin, Sensor and Actuator set it from the plugin's context.

Parameters

clock

Clock | undefined

Returns

void

Inherited from

Disposable.useClock


currentClock()

protected currentClock(): Clock

The clock this object runs on: every() and after() use it, and a Disposable that isn't a plugin, sensor or actuator reads the time from it (this.currentClock().now()).

Returns

Clock

Inherited from

Disposable.currentClock


own()

protected own<T>(resource): T

ties the lifetime of a child resource to this object; returns it so construction and registration fit in one expression. A Disposable child without a clock of its own runs on this one's, looked up when a timer starts, so a chain of owners (plugin → client → poller) shares the plugin's clock. Timers a child starts in its constructor, before it is owned, run on the system clock.

Type Parameters

T

T extends DisposableLike

Parameters

resource

T

Returns

T

Inherited from

Disposable.own


every()

protected every(ms, fn): () => void

runs fn every ms milliseconds until disposed or until the returned cancel function is called. The first run is after ms, not at once; call fn yourself first if you want an immediate run. Runs are not queued: a slow async fn can overlap its next run, so guard it yourself if needed.

Parameters

ms

number

fn

() => void

Returns

() => void

Throws

RangeError unless 0 < ms <= MAX_TIMER_MS

Inherited from

Disposable.every


after()

protected after(ms, fn): () => void

runs fn once after ms milliseconds unless disposed before; returns a cancel function. Both firing and cancelling forget the teardown, so repeated use does not accumulate entries. Delays beyond MAX_TIMER_MS are scheduled in steps, so they don't fire early.

The delay counts elapsed time, not wall-clock time: a clock step (an NTP sync after boot) is not noticed. For a deadline given as a wall-clock time, wait in shorter slices and re-check the time each time.

Parameters

ms

number

fn

() => void

Returns

() => void

Throws

RangeError for negative or non-finite delays

Inherited from

Disposable.after


listen()

protected listen(emitter, event, listener): void

Subscribes listener to event of an EventEmitter (or anything with on/off) and removes it again on dispose(), so a long-lived emitter such as the plugin's poller does not keep a disposed sensor alive. Prefer it to calling emitter.on yourself.

Parameters

emitter

Listenable

event

string | symbol

listener

Listener

Returns

void

Inherited from

Disposable.listen


start()

start(): void | Promise<void>

Optional setup, called once after construction and before the first run(); the host waits for it (at most 30 seconds). A rejection makes adding the signal fail and disposes the actuator.

Returns

void | Promise<void>


run()

abstract run(value, command?): void | Promise<void>

Performs the command; throwing (or rejecting) reports a failed result. Emits and logs made while it runs carry the command's cause id, including those from async work started here (promises, timers, listeners created inside run()), so the inspector shows them as its effect. Commands are serialized per concurrency; run() never overlaps itself. Put the real work in here and await it: the result (and its duration) is reported when the promise settles. The host marks a command as failed after 30 seconds without an answer, but cannot stop run(). Throw an Error with a message a user can read; it is shown as the failure reason.

command.replayed is true when the host delivers a value again rather than a new command: an active force after the plugin (re)started. An actuator whose command does something drastic (restart a device, open a door) should ignore replays. Older hosts never set it.

Parameters

value

V

command?

ActuatorCommand

Returns

void | Promise<void>


fail()

protected fail(message): void

Reports that the actuator can't work at the moment, e.g. the device it controls was removed; the message shows on the signal and the instance's error topic until recover(). Commands still reach run(), which should refuse them with a readable error while the cause lasts. For an outage of the whole device use plugin.status.set("degraded", …) instead. Repeating the same message sends nothing. Works from start() on, not in the constructor.

Parameters

message

string

Returns

void


recover()

protected recover(): void

Clears a failure reported with fail(). Cheap to call often: it only tells the host after a fail().

Returns

void