wirelesstags - JavaScript API for the Wireless Sensor Tags platform
Aims to provide a well-structured API to the Wireless Sensor Tag platform by interfacing with its JSON Web Servive API. It is primarily intended, designed, and tested for server-side use through NodeJS. (However, making it usable within a browser is a future goal, and corresponding contributions are welcome too.)
Installation and setup
$ npm install wirelesstags
The library (specifically, the platform.connect()
method, see
below) will need authentication information. The library supports two
default ways to pick up this information:
- A file
$HOME/.wirelesstags
in JSON format, with the necessary authentication information (currently keysusername
andpassword
). This file should obviously be readable only by the user running the app. - Environment variables
WIRELESSTAG_API_USER
andWIRELESSTAG_API_PASSWORD
, if set, will override whatever is found in the default options file.
It is strongly recommended to create a separate account as a "limited user" for using this library rather than your main account(s) at Wirelesstag.com. This makes it easy to change the password or delete the account altogether if the password happens to leak out, and allows controlling which tag managers and tags are visible to the account. Note also that wirelesstag.com stores your password in clear text (you can verify by recovering it), and hence never use a password there that you use anywhere else.
Usage
Principle objects
The principle object hierarchy is the following:
WirelessTagPlatform
: Top-level object representing the cloud interface to the platform. Emitsdiscover
events forWirelessTagManager
instances upon callingplatform.discoverTagManagers()
.WirelessTagManager
: Object representing a Tag Manager. Discovered through the platform object. Emitsdiscover
events for Wireless Tags associated with the tag manager upon callingtagManager.discoverTags()
.WirelessTag
: Object representing a Wireless Tag. Discovered through the tag manager object. Tags have sensor capabilities, which can be queried usingtag.hasHumiditySensor()
etc methods, or as an array of strings viatag.sensorCapabilities()
. Sensor objects can be discovered by callingtag.discoverSensors()
, which emits adiscover
event for each newly found sensor. They can be iterated over usingtag.eachSensor()
, which takes a callback.WirelessTagSensor
: Object abstracting a sensor that is part of a Wireless Tag. Sensor objects are of a type (sensor.sensorType
), and usually have areading
and aneventState
property. They can be armed or disarmed (sensor.arm()
andsensor.disarm()
), and their monitoring and notification configuration is available as properties of the object returned bysensor.monitoringConfig()
.
Initialize platform, connect to cloud, and discover tag managers
The platform object can be created using its constructor, or using the
static method WirelessTagPlatform.create()
.
var WirelessTagPlatform = require('wirelesstags');
// Passing a config object is optional. Default for apiBaseURI is
// https://www.mytaglist.com
var platform = new WirelessTagPlatform({ apiBaseURI: 'https://my.wirelesstag.com' });
When using the static create()
method, it will try to load
configuration options from ~/.wirelesstags
, or from the environment:
var WirelessTagPlatform = require('wirelesstags');
var platform = WirelessTagPlatform.create();
Platform instances emit a connect
event after successful
connections, and a discover
event for each tag manager object. The
connect()
and discoverTagManagers()
methods also return promises,
the latter with an array of tag manager objects.
Define or obtain connection options:
var opts = { username: 'foo@bar.com', password: 'supersecret' };
// or load from default configuration file or environment variables:
opts = WirelessTagPlatform.loadConfig();
Connect and discover tag managers using events handlers
platform.on('connect', () => {
console.log("connected to Wireless Tag cloud");
platform.discoverTagManagers();
});
platform.on('discover', (tagManager) => {
console.log("found tag manager", tagManager.name, tagManager.mac);
});
// once the listeners are set up we can connect
platform.connect(opts)
Connect and discover tag managers using returned promises
platform.connect(opts).then(() => {
return platform.discoverTagManagers();
}).then((tagManagers) => {
tagManagers.forEach((tagManager) => {
console.log("found tag manager", tagManager.name, tagManager.mac);
});
});
Discovering tags and their sensors
The tag manager object emits discover
events for each tag associated
with it after starting discovery with tagManager.discoverTags()
. In
the same way, tag objects emit discover
events for each of their
newly found sensors after initiating discovery with
tag.discoverSensors()
.
As before, the discovery methods also return promises of arrays of
tags and sensors, respectively. Either approach can be used. The
tag.discoverSensors()
method always promises an array of all its
sensors, whereas it emits discover
events only for newly found
sensors. Once found, subsequent tag.discoverSensors()
calls will
promise the same sensor objects. In contrast, the other discovery
methods always emit the same number of events as there are number of
elements in the promised array of discovered objects, and the objects
(in the promised array, or passed to listeners) are always new
objects. That is, tags cache their sensors (and indeed tags in
practice don't lose sensors), but tag managers and platform objects
don't cache their tags and tag managers, respectively (and indeed in
practice tags can be dynamically associated with or disassociated from
tag managers, as well as tag managers from accounts).
Discovering tags and sensors using event handlers
tagManager.on('discover', (tag) => {
console.log("Tag", tag.name, "(slaveId=" + tag.slaveId + ")", tag.uuid);
console.log(".. last updated", tag.lastUpdated());
tag.on('discover', (sensor) => {
console.log("..", sensor.sensorType, "of", sensor.wirelessTag.name);
console.log(" reading:", sensor.reading);
console.log(" state:", sensor.eventState);
console.log(" armed:", sensor.isArmed());
});
tag.discoverSensors();
});
tagManager.discoverTags();
Discovering tags and sensors using returned promises
tagManager.discoverTags().then((tags) => {
tags.forEach((tag) => {
console.log("Tag", tag.name, "(slaveId=" + tag.slaveId + ")", tag.uuid);
});
return Promise.all(tags.map((tag) => { return tag.discoverSensors(); }));
}).then((sensorLists) => {
sensorLists.forEach((sensors) => {
var tag = sensors[0].wirelessTag;
console.log("Sensors of tag", tag.name, tag.uuid);
sensors.forEach((sensor) => {
console.log("..", sensor.sensorType, "sensor");
console.log(" reading:", sensor.reading);
console.log(" state:", sensor.eventState);
console.log(" armed:", sensor.isArmed());
});
});
});
Accessing sensors through tag object
Once the promise returned from tag.discoverSensors()
is fulfilled,
sensor objects can also be accessed through the methods of the tag
object (because tag objects cache their sensors, and in practice
sensors can't be associated with or disassociated from a tag
dynamically). Either use tag.eachSensor()
with a callback that
accepts a sensor object, or use each sensor's individual accessor
method, which are all named zzzzSensor()
, where zzzz is the type of
sensor, for example tag.lightSensor()
. The method
tag.sensorCapabilities()
returns an array of strings denoting the
possible zzzz values.
Updating sensor values and tag information
Updating the tag object's data also updates its sensors. tag.update()
updates a tag object's data from the cloud, and tag.liveUpdate()
requests
that the tag immediately post its latest information.
To regularly update a tag object's data whenver the tag posts its latest
information, a loop can be started with tag.startUpdateLoop()
, and
stopped by tag.stopUpdateLoop()
.
Promises versus Callbacks
The library attempts to support both returning Promises from API-calling asynchronous functions, and the traditional callback mechanism.
Callbacks are generally called with an error as the first argument if
one occurred, and as the second argument with an object that has keys
object
(the object in which the operation was performed), and
value
(the resulting value from that operation) if the operation had
a result that is not saving or updating a property value of the
object. For example, in platform.isConnected(cb)
, cb
will be
called with { object: platform, value: false }
if the instance
wasn't yet connected.
It is considered a bad idea, and not supported (even if it happens to work) to mix passing callbacks and using the returned Promises.
Note that callback behaviour is not currently tested as part of the test suite, so there could be bugs.
Full documentation
This library should be considered beta. Full API documentation is only starting to come into place, and remains lacking for most of its functionality:
- Online API documentation for version v0.5.3
- See the
examples/
directory for tutorial scripts that give basic, but fully working demonstrations of how the library can be used.
Use at your own peril, and consider looking at the (meanwhile farily comprehensive) tests for guidance.
How to support
Aside from reporting issues and contributing pull requests, if you plan to buy from Wireless Sensor Tag, you might consider using this link to their website. If more than 10 people do so, and some end up buying, I stand to receive a discount on a past purchase of mine, which will allow me to buy other types of tags in the future and support those too.
License
Available under the MIT License.