This is an old revision of the document!


Example Driver Walk-through

The WOCustomDrvr.ts file demonstrates how to write a driver for a device that provides a TCP "socket" based control protocol.

This example driver communicates with Dataton WATCHOUT production software, controlling some basic functions. It's provided only as an example, since Blocks already has full support for controlling WATCHOUT built-in. Using WATCHOUT as an example device has some distinct advantages:

  1. Many of you are already familiar with WATCHOUT.
  2. The production software UI clearly shows what's going on.
  3. It's available as a free download, so anyone can use it to try out the driver.

Open the driver using the Atom editor, and follow along in the code as you read this walk-through.

Import Statements

At the very top of the file, there are import statements, referencing other files and their content. These other files provide definitions of system functions used to communicate with the device, such as the NetworkTCP interface imported from the "system/Network" file. You can navigate to referenced classes or files by command-clicking (Mac) or control-clicking (Windows) these items in the Atom editor. The referenced files contain the relevant declarations, and often also contain comments that further describe the referenced functions.

Not only does this provide relevant documentation. It also provides information for the TypeScript compiler as well as for the Atom editor. The Atom editor uses this information to guide you when writing code, often providing hints as you type, showing available functions, parameters, etc. If you make a mistake, this is often highlighted in red by the Atom editor and/or flagged by the TypeScript compiler.

Driver Class Declaration

Following the import statements you'll find a driver class declaration:

@Meta.driver('NetworkTCP', { port: 3040 })
export class WOCustomDrvr extends Driver<NetworkTCP> {

This declares the driver named WOCustomDrvr. This class name must match the name of the file it is stored in (minus its .ts extension). All names are case sensitive. This class must have the export keyword in order to be accessible from the outside by Blocks. It must extend the Driver base class, which must be parameterized by either the NetworkTCP or NetworkUDP type, indicating the type of socket being used.

The socket type must also be specified as the first (string) parameter to the @Meta.driver decorator, which must be applied to the class. The second parameter is an object, which for a network driver may specify the default port number typically used by the device being controlled. If specified, this port will be automatically entered in the Blocks user interface when selecting this driver, removing the need for the user to figure this out.

Instance Variables

At the top of the class, you typically define any variables used by the driver to keep track of its state. Variables are typed; either explicitly, as the pendingQueries which is of type Dictionary<Query>, or implicitly by assigning them a value.

A driver may be used by multiple devices (for instance, controlling three projectors, all of the same brand and model). In this case, each driver instance will get its own private set of instance variables.

Constructor

The driver must have a constructor function, which must take a single parameter of the type specified as part of the class declaration. In this example, this parameter is the NetworkTCP object, subsequently used to communicate with the TCP socket.

This parameter must be passed to the Driver base class using the super keyword, as shown.

Event Subscriptions

The constructor is a good place for one-time initializations, such as event subscriptions. The example driver subscribes to the 'connect' and 'textReceived' events. These event subscriptions are similar to how addEventListener is used in web browsers to listen to DOM events. You can find all available events described in the Network.ts file. Jump directly to the relevant declaration by command/control-clicking the subscribe call.

When the subscribed-to event occurs, the function body following the lambda ⇒ operator will be invoked. Either do what needs to be done here, or call a function defined elsewhere in the driver.

Properties

A key concept of a driver is to encapsulate the intricate details of a device-specific communications protocol, and instead expose the device's functionality as relevant properties. For a projector, relevant properties may be:

  • Power on/off
  • Current input selection

The power property of a projector, just like the connected property of the example driver is an on/off state, naturally represented by a boolean data type.

Property Accessors

A property is internally represented by one or two accessor functions. These use the keywords get and set, followed by the name of the property. The set function will be called when the property is set, and the get function will be called when its current value is requested.

Some properties may not be controllable, but could still make sense to read out. For a projector, a "lamp failure" indication fits the bill here. In this case, you only need to provide the get function, since it makes no sense to set this state from outside. This makes the property read-only, and you will not be able to assign a button to set it from the outside.

In some cases, you may want to create a property that can be set from inside the driver itself, but should be read-only from the outside. In this case, provide a set function and then mark it explicitly as re-only using the second parameter to the @Meta.property decorator. This is used for the connected property in the example driver, since the driver uses Blocks autoConnect feature (this is requested in the constructor function).

Property Decorators

To expose a property in the Blocks user interface, mark it using the @Meta.property decorator. This allows you to provide a short text, describing the property, that will be shown in the Blocks user interface when accessing the property. The second parameter of the @Meta.property decorator can be set to true to explicitly mark the property as read-only, as mentioned above

For a numeric property, you can also use min and max decorators, as shown for the input property in the example driver, specifying the minimum and maximum numeric value the property may take.