-
Notifications
You must be signed in to change notification settings - Fork 114
LED Device
In this tutorial we'll walk through the steps to create a very basic Zetta device. When you're finished, you'll understand the basic coding pattern used to create any Zetta device.
A Zetta device is software that models state and behavior for a physical device, such as an LED, a photocell, a robot arm, a car, or anything else you want to be able to control with a web API.
##About the device programming interface
Zetta device drivers are written in Node.js (JavaScript). For some device platforms, such as Beaglebone Black and Edison, Node.js modules are easy to obtain and allow you to program your Zetta device directly and intuitively. For example, the BoneScript module provides a Node.js interface to Beagle devices, such as BeagleBone Black.
To find a list of existing drivers that are on NPM, you can use the Zetta module search site. For more information on finding existing drivers that are in the public domain, see Searching for Zetta modules.
You'll have the best experience with this tutorial if you have a REST client like Postman or Advanced REST Client to try out the Zetta-generated REST APIs. These kinds of REST clients render responses with links you can click, making it easy to exercise a REST API. If you want to use cURL, you can try a utility, like the python JSON formatter that allow you to output readable results. For example:
curl http://localhost:1337 | python -m json.tool
Let's start with a new Zetta project. The best thing to do is follow the simple instructions in the first tutorial topic: Hello, Zetta!
Here's a shorthand list of the steps:
-
mkdir myproject
-
cd myproject
-
npm init
(and press return several times to create a default project) -
npm install zetta --save
-
Create a file called
index.js
and add this code to it and save it:
var zetta = require("zetta");
var led = require("zetta-led-mock-driver");
zetta()
.name("Device Tutorial")
.listen(1337, function(err) {
console.log("Zetta server is running on port 1337");
});
-
node index.js
-
If you see this output in the terminal, you're good to go:
Oct-23-2015 10:38:41 [server] Server (Device Tutorial) Device Tutorial listening on http://127.0.0.1:1337
Zetta is running at http://127.0.0.1:1337
Now that we have a functioning Zetta server, let's create the simplest possible device possible and configure the Zetta server to find the device. As you'll see, this basic pattern requires only a few lines of code. From this starting point, we can build a more interesting device.
-
cd to the Zetta project you created, as explained above.
-
Create a new file called
device.js
. -
Copy this code into the file and save it:
var util = require('util');
var Device = require('zetta').Device;
var Dev = module.exports = function DeviceDriver() {
Device.call(this);
}
util.inherits(Dev, Device);
Dev.prototype.init = function(config) {
config
.type('fake-device')
};
- Open the
index.js
file and add this line at the top:
var DEVICE = require("./device.js");
- Now, use the fake device by calling
.use(DEVICE)
on thezetta()
object. Your server code should look like this:
var zetta = require("zetta");
var DEVICE = require("./device.js");
zetta()
.name("Device Tutorial")
.use(DEVICE)
.listen(1337, function(err) {
console.log("Zetta server is running on port 1337");
});
- Start the server: node index.js. You will see this output in the terminal:
Sep-10-2015 15:27:10 [scout] Device (fake-device) 1d93c7b1-aa00-488f-b738-a9116f33efb7 was discovered
Sep-10-2015 15:27:10 [server] Server (Device Tutorial) Device Tutorial listening on http://127.0.0.1:1337
With just a few lines of code, you have a running Zetta server that has discovered the simple device you wrote. The file device.js
illustrates the basic pattern you'll see for all Zetta devices:
-
Require the
util
andzetta
npm modules. -
Export a function that inherits from
zetta().Device
-
Write an init() function that takes a DeviceConfig object. As you'll see, there are many more methods you can call on the DeviceConfig object for setting device state, configuring state transitions, mapping state transitions to functions, streaming data, and more.
So far, our device doesn't do anything, but we'll add code to fix that next. We'll configure a mock device that blinks a mock LED. Once you understand how the mock device operates, wiring up a real device, such as an LED that blinks via Beaglebone Black hardware, is fairly easy. We'll show you how to do that in another tutorial.
- In a text editor, open
device.js
:
var util = require('util');
var Device = require('zetta').Device;
var Dev = module.exports = function DeviceDriver() {
Device.call(this);
}
util.inherits(Dev, Device);
Dev.prototype.init = function(config) {
config
.type(fake-device)
};
-
Let's make a few changes and call some new methods on the
DeviceConfig
object:-
Change the type from
fake-device
toled
. -
Set the initial state of the device to off:
.state('off')
-
Give the device a name:
.name(led)
-
Tell the device what transitions it allows for a particular state. In the case of an LED, when the state is "off", allow it to transition to "on", and vice versa. We express these transitions as follows:
.when('off', { allow: ['turn-on'] }) .when('on', { allow: ['turn-off'] })
As we'll see, these transitions will show up as "actions" in the device API.
-
Now, we map these allowable transitions to functions that control the device. For now, we'll set these map function references to null and take a quick look at how the API has changed. Then, we'll write the map function code in the next section.
Note: All of the methods for the
DeviceConfig
class are documented in the Device reference. -
Make sure your code looks like this:
var util = require('util'); var Device = require('zetta-device'); var LED = module.exports = function(led) { Device.call(this); }; util.inherits(LED, Device); LED.prototype.init = function(config) { config .type('led') .state('off') .name('LED') .when('off', { allow: ['turn-on'] }) .when('on', { allow: ['turn-off'] }) .map('turn-on', null) .map('turn-off', null); };
-
-
Save the file.
-
Start the server:
node index.js
-
In a REST client tool such as Postman or Advanced REST Client, hit the server URL:
http://localhost:1337
-
Find the servers link and hit it:
http://localhost:1337/servers/zetta-led-demo
-
Now, find the href link in the
led
device entity. Follow this link. It takes you to the API for the device, which includes the available actions the device can take. It should look something like this:http://localhost:1337/servers/zetta-led-demo/devices/6fe9f109-9778-41be-9de9-c6e46b652cce
Notice the properties attribute indicates the current state is
off
. And the available action that can be performed on this device isturn-on
.{ "class": [ "device", "led" ], "properties": { "id": "6fe9f109-9778-41be-9de9-c6e46b652cce", "name": "led", "type": "led", "state": "off" }, "actions": [ { "class": [ "transition" ], "name": "turn-on", "method": "POST", "href": "http://localhost:1337/servers/Device%20Tutorial/devices/6fe9f109-9778-41be-9de9-c6e46b652cce", "fields": [ { "name": "action", "type": "hidden", "value": "turn-on" } ] } ]
Next, we'll implement the methods for turning the LED on and off. Then, we can use the API to execute those methods.
-
Open
device.js
in an editor. -
At the bottom of the file, add these two methods:
Dev.prototype.turnOn = function(cb) { this.state = 'on'; cb(); }; Dev.prototype.turnOff = function(cb) { this.state = 'off'; cb(); };
-
Now, edit the
map()
methods, replacing null with these method names: ..map('turn-on', this.turnOn) .map('turn-off', this.turnOff)
-
Save
device.js
and restart the Zetta server. -
Go back to your REST client and walk back through the links, starting with
http://localhost:1337
, then the servers linkhttp://localhost:1337/servers/zetta-led-demo
, and then the link for the device,http://localhost:1337/servers/zetta-led-demo/devices/6fe9f109-9778-41be-9de9-c6e46b652cce
. -
You can see in the response, that the current state is
off
, and the transition actionturn-on
is available. To execute this action, you need to form a POST request. Here's how you can make the request in cURL (although your actual device ID will be different):curl -i -X POST http://127.0.0.1:1337/servers/Hello%20Zetta/devices/e6f5b480-e96e-4fdc-8718-91aeb0234c99 -d ‘action=turn-on’
-
Check out the response. Notice that the transition has changed from
turn-on
toturn-off
:
"actions": [
{
"class": [
"transition"
],
"name": "turn-off",
"method": "POST",
"href": "http://localhost:1337/servers/Device%20Tutorial/devices/6fe9f109-9778-41be-9de9-c6e46b652cce",
"fields": [
{
"name": "action",
"type": "hidden",
"value": "turn-off"
}
]
}
]
####Quick Summary
In this part, we walked through the steps for wiring up a mock device that switches state on and off. We saw how you can interact with the device through it's RESTful Web API. Client app developers can code apps that call this API to control the device.
Check out How Drivers Work, a short tutorial that explains how to create a simple LED driver for the Beaglebone Black.
Need help? Visit the Zetta Discuss List !
Need help? Visit the Zetta Discuss List ! |
---|
About Zetta
Videos and webcasts
- NEW! Building with Zetta
Tutorials
- NEW! Zetta tutorial series
- Quick start
- Configure a simple device
- Build a mock LED device
- Use the browser client
- Deploy a Zetta server to Heroku
Understanding Zetta
Writing Zetta drivers
- Finding Zetta device drivers
- Create a device driver from starter code
- More coming soon...
Using streams
Reference