node package manager
Easy collaboration. Discover, share, and reuse code in your team. Create a free org »

stream-dev-tools

Getting started

Install the latest version available on NPM:

npm install stream-dev-tools --save

Then:

var VectorWatch = require('stream-dev-tools');

Streams

How it works

Streams are text based content that is displayed on the Vector Watch. From a technical point of view, the system works as follows. The watch exchanges data with the phone application (available on iOS, Android and Windows Phone), the app exchanges data with our cloud and our cloud exchanges data with a stream node. Streams Flow.

The communication between Vector Cloud and a Stream Node is handled using HTTP protocol. The Stream Node listens on a HTTP endpoint to receive Vector events and it calls HTTP endpoints on the Vector Cloud in order to push realtime data to the Vector Watch.

How to use it

In order to host a stream, you need the API token generated for your developer account and a streamUID which you can create here.

var vectorStream = VectorWatch.createStreamNode({
    token: '***',
    streamUID: '***'
});

Listen to requestConfig events, triggered when someone drops the stream onto an empty stream slot, creating a stream instance:

vectorStream.requestConfig = function(resolve, reject) {
    resolve({
        renderOptions: {
            MySetting: {
                type: 'GRID_LAYOUT',
                dataType: 'STATIC'
            }
        },
        settings: {
            MySetting: [
                { name: 'Option #1', value: 1 },
                { name: 'Option #2', value: 2 }
            ]
        },
        defaults: {
            MySetting: { value: 1 }
        }
    });
};

Listen to registerSettings events, triggered when someone configures a stream instance:

vectorStream.registerSettings = function(resolve, reject, settings) {
    if (settings.MySetting1 == 1) {
        resolve('Selected Option #1');
    } else if (settings.MySetting1 == 2) {
        resolve('Selected Option #2');
    } else {
        reject(new Error('Invalid option selected.'));
    }
};

Apps

How it works

How to use it

API Reference

Summary

VectorWatch

Static Methods

StreamNode

Methods
Events

AppNode

Methods
Events

## VectorWatch : Object #### Methods

## StreamNode : Object #### Properties

  • stateStorage
  • authStorage

Methods

This method will put the 'data' String for the given channel(state) in the cache object and send it 
to the cloud after the delay has passed.
After that all the users that listen to that channel will see the new information.
Example:
stream.push({channelLabel:"....", settings1:{name:"...", value:""}}, "Info to be shown", 1);
This method will flush the cache and send all the information to the cloud.
This method will inform the mobile app that the user's authentification has expired
Example:
stream.authTokensForStateExpired({channelLabel:"....", __auth:{....},settings1:{name:"...", value:""}});
Get all the settings stored in the DB. On success the resolve(settingsArray) method is called, 
otherwise the reject(error) method.
The developer can access the returned array in the resolve(settingsArray) callback, as a parameter.
Example:
stream.retrieveSettings(function (allSettingsObject) {
    //Do something on success 
}, function(){
    //Do something on failure 
});
Get the authentification information for the given 'state'
Delete al settings from the DB.
Returns the configurated expressJS app
Inserts/Updates the authentification information in the db/memory

Events (overridable methods)

/** This method is called in order to retrieve all the settings(name, order, display option) when 
an user adds the stream to a watch-face.
 * Call the resolve() method with a Config Object as a parameter for success or the reject() method 
 with an error message.
 */
stream.requestConfig = function (resolve, reject, authTokens) {
    resolve({
        "renderOptions": {
            "DataTypeSettings": {
                "type": "GRID_LAYOUT",
                "dataType": "STATIC",
                "order": 0
            },
            "DisplaySettings0": {
                "type": "INPUT_LIST",
                "dataType": "DYNAMIC",
                "order": 1,
                "asYouType": true,
                "minChars": 3
            }
        },
        "settings": {
            "DataTypeSettings": [
                {
                    "name": "Counter",
                    "value": 1
                },
                {
                    "name": "Last Update",
                    "value": 2
                }
            ]
        },
        "defaults": {
            "DataTypeSettings": {
                "name": "Counter",
                "value": 1
            }
        }
    });
};
/** This method is called in order to retrieve all the options for a particular setting(given by the settingName parameter).
 *  Call the resolve() method with an array of SettingValue objects for success or the reject() method with an error message.
 *  The state parameter holds all the previous settings
 */
stream.requestOptions = function (resolve, reject, settingName, searchTerm, state, authTokens) {
    resolve([{name:"Option0"},{name:"Options1"}]);
 
};
/** This method is called every time a user selects the desired settings.
 * The DB(if not in dev mode) is automatically updated.
 * When implementing this method the developer must call the 'resolve' function parameter 
 after he retrieves/generates the data.
 * */
stream.registerSettings = function (resolve, reject, settings) {
    resolve("Welcome!");
};
/** This method is called every time a user removes the stream from a watch-face.
 * */
stream.unregisterSettings = function (resolve, reject, settings) {
    resolve();
};

## AppNode : Object

TODO

## State : Object #### Properties

  • __auth Object (for internal use only)
  • channelLabel String (optional)
  • {setting name} SettingValue (there is a property for each setting)

Example:

{
    "__auth": {
        "code": "...",
        "state": "..."
    },
    "MySetting1": { "value": "2" },
    "MySetting2": { "value": "1" },
    "channelLabel": "..."
}

## Config : Object This object tells the phone application what are the stream settings and how to display them

Example

{
    "renderOptions": {
        "MySetting1": {
            "type": "GRID_LAYOUT",
            "hint": "Select an option for MySetting1",
            "order": 0,
            "dataType": "STATIC"
        },
        "MySetting2": {
            "type": "INPUT_LIST_STRICT",
            "hint": "Type an option for MySetting2",
            "order": 1,
            "dataType": "STATIC"
        }
    },
    "settings": {
        "MySetting1": [
            { "name": "Option #1", "value": "1" },
            { "name": "Option #2", "value": "2" }
        ],
        "MySetting2": [
            { "name": "Option #1", "value": "1" },
            { "name": "Option #2", "value": "2" }
        ]
    },
    "defaults": {
        "MySetting1": { "value": "1" },
        "MySetting2": { "value": "1" }
    }
}

## RenderOption : Object #### Properties

  • name String (can be omitted if this in the value of a property with the same name)
  • type String (one of the following: GRID_LAYOUT, INPUT_LIST or INPUT_LIST_STRICT)
  • hint String (optional)
  • order Number (required if more than a RenderOption is specified)
  • dataType String (one of the following: STATIC or DYNAMIC)
  • asYouType Boolean (required if dataType is DYNAMIC)
  • minChars Number (required if asYouType is true)

Example

{
    "type": "GRID_LAYOUT",
    "dataType": "STATIC"
}

## SettingValue : Object #### Properties

  • name String (can be omitted if this is the value of a property with the same name)
  • value Object (can be anything, but most of the times this will be String)

Example

{
    "name": "Option #1",
    "value": "1"
}

## AuthTokens : Object This class holds the tokens required for authorization.

This class is abstract, see OAuth1Tokens and OAuth2Tokens for concrete implementations.

## OAuth1Tokens : AuthTokens #### Properties

  • oauth_access_token String
  • oauth_access_token_secret String

Example

{
    "oauth_access_token": "...",
    "oauth_access_token_secret": "..."
}

## OAuth2Tokens : AuthTokens #### Properties

Example

{
    "access_token": "..."
}

Sample

This is a simple stream that shows the current number of stream instances or the last update(month.year).

var streamDevTools = require('./index.js');
var configJSON = {
    streamUUID: "***",
    streamType: "public",
    hasSettings: true,
    token: "***",
    portNumber: "2999"
};
var sample_stream = streamDevTools.createStreamNode(configJSON);
var DisplaySettings0 = [{name: 'simple', value: 1}, {name: 'detailed', value: 2}],
    DisplaySettings1 = [{name: 'with icons', value: 1}, {name: 'without icons', value: 2}];
 
var counter = 0, lastUpdated;
setLastUpdated();
 
/** Update the stream value for all users
 * @returns {null}
 */
function updateAll() {
    sample_stream.retrieveSettings(function (allSettingsObject) {
        //Here we retrieve all the settings from the db. 
        /*
        * allSettingsObject = {"...chanelLabel...":{...State Object...},
        *                      "...chanelLabel...":{...State Object...},
*                               ....
        *                       }
        * */
        var pushArray = [];
        for (channelLabel in  allSettingsObject) {
            //Here we iterate through all the settings and 'push' the corresponding information. 
            if (allSettingsObject.hasOwnProperty(channelLabel)) {
                sample_stream.push(allSettingsObject[channelLabel], getData(allSettingsObject[channelLabel]));
            }
        }
        //Flush the buffer and send all the information to the cloud. 
        sample_stream.pushNow();
    });
}
 
/** Returns the corresponding String that will be shown on th watch for a set of settings
 * @returns {null}
 */
function getData(settings) {
    var data = '', counterText = " stream instances:", dateText = " last update:";
    if (settings.DataTypeSettings.name.indexOf("Counter") > -1) {
        data = counter;
        if(settings.DisplaySettings0.name.indexOf("detailed") > -1) {
            data += counterText;
        }
    } else {
        data = lastUpdated;
        if(settings.DisplaySettings0.name.indexOf("detailed") > -1) {
            data += dateText;
        }
    }
    if(settings.DisplaySettings0.name.indexOf("without") == -1) {
        return " " + data;
    } else {
        return data;
    }
}
 
/** Returns the corresponding String that will be shown on th watch for a set of settings
 * @returns {null}
 */
function setLastUpdated(){
    lastUpdated = new Date().getDate() +"." + new Date().getMonth();
}
 
/** Over-ridden method. Calls the success callback with the desired configuration.
 * @returns {null}
 */
sample_stream.requestConfig = function (resolve, reject) {
    resolve({
        "renderOptions": {
            "DataTypeSettings": {
                "type": "GRID_LAYOUT",
                "dataType": "STATIC",
                "order": 0
            },
            "DisplaySettings0": {
                "type": "INPUT_LIST",
                "dataType": "DYNAMIC",
                "order": 1,
                "asYouType": true,
                "minChars": 3
            },
            "DisplaySettings1": {
                "type": "INPUT_LIST",
                "dataType": "DYNAMIC",
                "order": 2,
                "asYouType": true,
                "minChars": 3
            }
        },
        "settings": {
            "DataTypeSettings": [
                {
                    "name": "Counter",
                    "value": 1
                },
                {
                    "name": "Last Update",
                    "value": 2
                }
            ],
            "DisplaySettings0": [],
            "DisplaySettings1": []
        },
        "defaults": {
            "DataTypeSettings": {
                "name": "Counter",
                "value": 1
            }
        }
    });
};
 
 
/** Over-ridden method. Calls the success callback with the found options for the current dynamic setting
 * @returns {null}
 */
sample_stream.requestOptions = function (resolve, reject, settingName, searchTerm, state) {
    var settingItem, results = [];
    switch (settingName) {
        case 'DisplaySettings0':
            settingItem = DisplaySettings0;
            break;
        case 'DisplaySettings1':
            settingItem = DisplaySettings1;
            break;
    }
    settingItem.forEach(function (option) {
        if (option.name.indexOf(searchTerm) > -1) {
            results.push(option);
        }
    });
    resolve(results);
};
 
/** Over-ridden method. The counter and the lastUpdated variables are updated and the corresponding information is generated and send
 * @returns {null}
 */
sample_stream.registerSettings = function (resolve, reject, settings) {
    counter++;
    resolve(getData(settings));
    setLastUpdated();
};
 
/** Over-ridden method. The counter and the lastUpdated variables are updated and the corresponding information is generated and send
 * @returns {null}
 */
sample_stream.unregisterSettings = function (resolve, reject, settings) {
    // success 
    counter--;
    setLastUpdated();
    resolve();
};
 
sample_stream.startStreamServer(configJSON.portNumber, function () {
    console.log('Listening on ' + configJSON.portNumber);
    updateAll();
});
setInterval(updateAll, 60 * 60 * 60);