Skip to content
wwitman edited this page Feb 4, 2016 · 9 revisions

Device class

This class models a device. You define in this class device states and allowable transitions, and map transitions to JavaScript functions that control the actual device state. For example, you could model a light switch with two states: on and off.

What you need to know

When Zetta finds a device on-line that matches the type (or other attribute) of a device class, Zetta instantiates the Device. It then generates a REST API that lets clients interact with the device.

Usage

You can make Zetta aware of a Device by adding it to the Zetta server directly, as shown below:

// server.js
var zetta = require('zetta');
var MyDevice = require('./device.js');

  zetta()
    .name('My Server')
    .use(MyDevice)
    .listen(1337);
  });

You can also add a Device directly to a Scout class, as shown in the Scout class reference.

Sample implementation

When developing a Device, follow this general pattern:

  1. Add required modules. You must require the Device class itself. The util module provides useful functions for doing inheritance.
  2. Construct a Device object.
  3. Implement the init() function.
// device.js
var Device = require('zetta').Device;
var util = require('util');

var StateMachineDevice = module.exports = function() {
     Device.call(this);
    }

util.inherits(StateMachineDevice, Device);


StateMachineDevice.prototype.init = function(config) {
      
      // Set up the state machine 
      config
        .type('state_machine')
        .state('off')
        .name("State Machine Device");

      config
        // Define the transitions allowed by the state machine
        .when('off', {allow: ['turn-on']})
        .when('on', {allow: ['turn-off']})

        // Map the transitions to JavaScript methods
        .map('turn-off', this.turnOff)
        .map('turn-on', this.turnOn)
    }


    StateMachineDevice.prototype.turnOff = function(cb) {
      this.state = 'off';
      cb();
    }

    StateMachineDevice.prototype.turnOn = function(cb) {
      this.state = 'on';
      cb();
    }

Properties

Device.state

The current state of the Device object.

Methods

Device.init(config)

The init() function is called once, and initializes the initial state of the Device. It also is where you define the devices allowable transitions and map transitions to JavaScript functions.

Arguments

  • config A DeviceConfig object that lets you to set up your state machine, name the object, give it a type, and define transitions and mappings.

Example

BeagleBoneLedDevice.prototype.init = function(config) {
      
      // Set up the state machine 
      config
        .type('beaglebone_led')
        .state('off')
        .name('BeagleBone LED Device: ' + this.assignedPin);

      config
        // Define the transitions allowed by the state machine
        .when('off', {allow: ['turn-on']})
        .when('on', {allow: ['turn-off']})

        // Map the transitions to JavaScript methods
        .map('turn-off', this.turnOff)
        .map('turn-on', this.turnOn)
    }

config.type(string)

Specify the device type. The type can be used in device queries to find the device.

config.state(string)

Set initial state of the device.

config.name(string)

Specify a name for the device. The name is useful for clients that need to crawl the device REST API.

config.when(state, options)

Allows you to set what transitions are available in a particular state. This method conditionally sets available transitions for your state machine based on the state property.

Arguments

  • state - (string) A valid state for the device.
  • options - An object with a property called allow where you can define the available transitions for the state.

Example

DeviceDriver.prototype.init = function(config) {
  config
    .when('on', {allow: ['off']})
};

config.map(transition, func, [options])

Maps transitions to JavaScript functions. Whenever a transition is called on a state machine the corresponding function will be executed.

Arguments

  • transition - (string) The name of the transition.
  • func - (function) - A valid function on the Device object.
  • options - (array of objects) Defines input arguments to the transition functions.

Example

DeviceDriver.prototype.init = function(config) {
  config
    .map('on', this.turnOn);
    .map('strobe', this.strobe, [{name:'amount', type:'number'}])
};

DeviceDriver.prototype.turnOn = function(cb) {
  // do something
};

DeviceDriver.prototype.strobe = function(amount, cb ){
  // do something
};

config.stream(name, func, [options])

Lets you set up streaming data out of Zetta.

Arguments

  • name - (string) The name of the stream.
  • func - (function) - A callback function that is executed to provide a user with a stream.
  • options - Allows a user to define the type of stream to be created object or binary

Example

DeviceDriver.prototype.init = function(config) {
  config
    .stream('value', this.streamValue);
};

DeviceDriver.prototype.streamValue = function(stream) {
  setInterval(function(){
    stream.write(Math.random());
  }, 3000);
}

config.monitor(name)

Stream a property from your device instance out of Zetta. Zetta monitors the property for changes, and if they occur publishes an event down the stream.

Argument

  • name - (string) The name of the property to monitor.

Example

function DeviceDriver() {
  this.color = 0;
}

DeviceDriver.prototype.init = function(config) {
  config
    .monitor('color');
}

config.remoteFetch(handler)

Takes a handler that is called when the device properties are returned to the API or Zetta to Zetta protocols. You can use this handler to configure what is sent to the API. name, id and type are automatically appended the object returned. Any properties with a leading underscore will be filtered. For example the following device will only return blah and other properties to the API along with name, id and type.

By default all properties on the device are returned to the API except properties with a leading underscore and properties with the type functions.

Argument

  • handler - (function) Called when the device properties are returned to the API or Zetta to Zetta protocols.

Example

function DeviceDriver() {
  this.authToken = 'abc123';
  this.blah = 25;
}

DeviceDriver.prototype.init = function(config) {
  config
    .name('some device')
    .monitor('blah')
    .remoteFetch(function() {
      return {
        blah: this.blah,
        other: 'some other property'
      };
    });
}

config.remoteUpdate(handler)

By default when a PUT is made to a device resource on the API zetta updates the device by updating the internal properties to the payload supplied in the request and removing any that did not exist.

Argument

  • handler - (function) A handler function that can be used to alter the default functionality. You can manually select which properties can be updated remotely and provide extra validation.

Example

function DeviceDriver() {
  this.macAddress = '00-14-22-01-23-45';
  this.location = 'Kitchen';
}

DeviceDriver.prototype.init = function(config) {
  config
    .name('Light Bulb One')
    .remoteUpdate(function(properties, cb) {
      var self = this;
      // ensure remote update does not change macAddress
      delete properties['macAddress'];

      // update existing properties
      Object.keys(properties).forEach(function(key) {
        self[key] = properties[key];
      });

      // must manually call save to store in db.
      this.save(cb);
    });
}

config.remoteDestroy(handler)

By default when a DELETE is made to a device resource on the API zetta destroys the device. It will no longer appear in the Zetta HTTP API.

Argument

  • handler - (function) A handler function that can be used to alter the default functionality. You can manually set whether or not device drivers of this type can be destroyed in the API.

Example

function DeviceDriver() {
  this.macAddress = '00-14-22-01-23-45';
  this.location = 'Kitchen';
}

DeviceDriver.prototype.init = function(config) {
  config
    .name('Light Bulb One')
    .remoteDestroy(function(err, cb) {
      if(err) {
        console.log(err);
        //Error occurred don't delete. 
        return cb(err);
      } else {
        //can destroy
        cb(null, true);
        //cannot destroy
        cb(null, false);
      }
    });
}

Device.destroy()

Removes a device driver from the Zetta internals. It will no longer show up in the API, or be usable in apps. It will cause the device to emit the destroy event.

Arguments

None.

Example

function foo(led) {
  //led is an instance of a Zetta.Device
  //This will destroy the device.
  led.destroy();
}

Device.call(name, [arguments], [cb])

Calls a transition on your state machine.

Arguments

  • name - (string) The name of the transition to call.
  • arguments - Any number of arguments that need to be passed to the transition call.
  • callback - (function) An optional callback that is executed when the transition completes.
function foo(led) {
  //led is an instance of a Zetta.Device
  //The transition 'turn-on' is called
  led.call('turn-on');
}
Clone this wiki locally