Most IoT applications require monitoring the status of devices, collecting and aggregating the data they receive from various sources in order to derive information from it. Status data and aggregated data are then displayed to the end users in dashboards. Since implementing dashboards can be a tedious task without the appropriate tool, scriptr.io provides you with its dashboard builder that will allow you to build your dashboards in a snap.
The simplest way to launch the dashboard builder is to click on the arrow near “New Script” on the bottom left corner of the scriptr.io workspace, then select “Dashboard”.
How it works
As we will see it in more depth very shortly, the visual components you add to your dashboard receive data that is generated by some server-side endpoints to which the visual components are connected through HTTP or WebSockets. Endpoints are typically scriptr.io back-end scripts you wrote, but they actually could be any remote service that is returning data structured in a way that complies with the component’s expectations. Note that since this latter option requires some advanced configuration, for now we’ll stick to connecting to scriptr.io endpoints.
Configuring the dashboard builder
As mentioned, you can connect the dashboard’s components to scripts through websockets, but you can also subscribe them to a scriptr.io channel in order to receive messages as soon as they are published to the latter.
Specify a request and a response channels
Configuring the dashboard is about specifying the channels to which your dashboard subscribes to receive messages targeting its components, respectively to which it publishes messages towards your scripts. Clicking on the gear at the top right corner of the dashboard opens the channel configuration panel.
By default, two channel names are specified: responseChannel (the channel from which the visual components receive data) and requestChannel. You can keep the names or change them but in any case you will need to create the corresponding channels.
To create channels, click on the drop-down arrow near your username at the top left corner of the workspace, select “settings” then the “Channels” tab. Click on the “Add” button to add a new channel, entering the name you’ve chosen. You then need to specify the publish/subscribe permissions on the channel (un-check the boxes if only authenticated users are able to read/write to it or if it is public). Proceed similarly for both channels.
Create a sub domain
If not already done, you need to create a sub domain for your account. Click on the drop-down next to your username on the top left corner of the workspace, click on “Account” then select the “Sub-domain” tab. Enter a name at your convenience and close the panel.
Building a simple dashboard
Let’s first start by implementing a simple script that will simulate the behavior of a device. For now, our script will contain a simple function that randomly generates a temperature value. We’ll name this script “device” and save it.
/** * Device simulator */ function getTemperature() { var temp = 20 + Math.round(Math.random() * 5); return temp; }
Displaying the current temperature in a gauge
Now that we have our device simulator, let’s create another script (“getTemperature”) that reads the temperature from the simulated device. This new script is used as an API to which we will connect a gauge.
Create a getTemperature API operation
/** * Temperature API */ var device = require("./device"); // we require the device script we've just implemented return device.getTemperature();
For now, make sure to set the permissions on this script to “anonymous”, which allows unauthenticated access to the script (we’ll see about authenticated access later). Click on the red lock on the top right corner of the script, click on the “select a user/group” drop-down, choose “anonymous”, then click on “Add” and “Yes” to confirm your choice, then save your changes.
Add a gauge to the dashboard
Let’s now add a gauge to our dashboard by clicking on the gauge icon:
Next step is to connect our gauge to the “getTemperature” API. Click on the gear at the top right corner of the gauge on the dashboard to open the gauge’s configuration panel:
- The “transport” field allows you to specify whether the component will use WebSockets (wss) or HTTP to communicate with the back-end. The default value is wss and we’ll keep it as is,
- The “Api” field allows you to specify the path to the script that serves as a data source to the current gauge. Set it to the path to your script (in the figure, the path to my script is: blog/dashboardintro/getTemperature) (note that you should not prefix your path with “/” even if this is the path from the root).
For now, we will ignore the other fields and save.
Let’s also fine-tune our gauge by clicking on the “Dimensions” tab to specify the min an max values of the gauge (0, 50), then click on the “Labels” tab to specify a label (“Temperature”)
Once done, click on “OK” to validate the new configuration. As you will notice, the gauge is automatically updated with the value obtained from the script’s invocation.
View the dashboard in your browser
Save your dashboard by clicking on the “save” option of the workspace menu, then click on “view” to open the dashboard into your browser.
Viewing historical data
Displaying temperature is cool, but what about following-up on its evolution over time? Line charts are typically used for such a task and we’ll see how to use a line chart component in this section.
Persisting temperature values
We need to modify our current version of the device simulator so it persists the temperature value it generates. We’ll use a document for that and add the logic to create a document every time the getTemperature() function is invoked.
We also add the listTemperatures() function to list the latest 50 temperature values (by default a request returns the 50 latest matching documents. Although we can paginate easily with scriptr.io, we’ll stick to the default behavior in this post). Since line charts components expect to receive data in the following structure: [{“x-axis”: value, “y-axis”: value}, {“x-axis”: value, “y-axis”: value}, …], our function will also be responsible for transforming the list into the latter structure.
/** * Device simulator */ var document = require("document"); // require the native "document" library function getTemperature() { var temp = 20 + Math.round(Math.random() * 5); var fields = { // prepate the data stucture to persist temperature: temp, "meta.types": { temperature: "numeric" } } document.create(fields); // create a document return temp; } function listTemperatures() { var request = { // retrieve all documents where temperature >= 0 "query": "temperature >= 0", "fields": "temperature, creationDate" // fields to return (key and version number are automatically added) }; var resp = document.query(request); if (resp.metadata.status == "failure") { return "error"; } var docs = resp.result.documents; return docs; }
Add the listTemperatures API operation
As we’ve done it previously, we’ll expose the listTemperatures() function through a new API script (“listTemperatures”) to which the line chart will be connected (don’t forget to set the script’s permissions to anonymous).
/** * List temperatures API */ var device = require("./device"); return device.listTemperatures();
Add the line chart to the Dashboard
Click on the line chart component in the dashboard builder’s menu, then click on the gear to configure the component.
Similarly to what we’ve done it for the gauge, we use the “Api” field to specify the API that returns the data structure to fill the chart, in the below figure, the path to my script is: “blog/dashboardintro/lisTemperatures”. Notice the “Data” field that gives an example of the data structure that is expected by the line chart.
Since our API returns a list of objects that have a “creationDate” (automatically set by scriptr.io to the creation date/time of the document) and a “temperature” attributes, we need to inform the component about it by clicking on the “Dimensions” tab. There, replace “y” with “creationDate” and [“a”, “b”] with [“temperature”]
Last, we set the labels of the dots on the chart by clicking on the “Labels” tab, then replacing [“Series A”, “Series B”] with [“Temperature”]
Run the “getTemperature” API a few times from the workspace in order to generate a few temperature values, then click “OK” to validate the configuration of the line chart, which is immediately updated with the corresponding data. Again, save your dashboard an click on “view” to open it in the browser, or just refresh the browser page.
Displaying data in real time
So far, the content on our dashboard is static, i.e. its components are only populated the first time the dashboard is loaded or modified. This is fine but what is even better is to automatically and immediately reflect any change on the data and the best part is that this is very easy to do with scriptr.io.
As you might remember, our dashboard is subscribed to a channel (“responseChannel”). Therefore, any component using “wss” as transport will automatically receive any message published to that channel and can immediately reflect the message’s content to the screen. Visual components understand that they should consume a message by comparing the value of their “tag” field to the value of the “id” field in the message.
In order to specify the “tag” field of a visual component, open its configuration panel, click on the “Data” tab (visible by default) and set the value of the “tag” field to the desired value. We’ll keep the default values (“gauge” and “chart” respectively) in our example.
So what’s next?
We’re almost done: what is left is to modify our device simulator to make it publish a message every time the “getTemperature()” function is invoked. The published message should adopt the following structure:
{"id": "the_tag_of_the_target_component", "result": expected_data_structure}
So let’s proceed and have our device simulator publish two distinct messages: one for the gauge and the other for the line chart. For that we enhance it with a new publishMessage(temperature) function that is invoked by the getTemperature() function:
/** * Device simulator */ var document = require("document"); function getTemperature() { var temp = 20 + Math.round(Math.random() * 5); var fields = { temperature: temp, "meta.types": { temperature: "numeric" } } document.create(fields); publishMessages(temp); // publish to the responseChannel return temp; } function listTemperatures() { // same implentation as above // ... } function publishMessages(temp) { // this function handles the publishing of messages var gaugeMsg = {"id": "gauge", result: temp}; publish("responseChannel", gaugeMsg); // we use the native publish function to publish messages var temperatureList = listTemperatures(); // one a new temperature is read, we retrieve the updated temperatures list var chartMsg = {"id": "chart", result: temperatureList}; publish("responseChannel", chartMsg); }
That’s it.