About the Publish/Subscribe model

Publish/subscribe is a well known communication pattern where a sender (called a publisher) broadcasts messages to multiple receivers that declared their interest in the type of messages sent by the publisher (hence they are called subscribers). Messages are usually published to and consumed from a topic to which the receivers are subscribed. Resorting to publish/subscribe allows you to adopt an event-based programming style, and brings some advantages to your design, mainly decoupling (publisher and subscriber do not have to be online at the same time) and scalability, to some extent.

The core artefact to use in scriptr.io when adopting publish/subscribe communication in your design are “channels” and bridges.

Channels

Channels are similar to topics and are used by publishers to broadcast messages to subscribers. In scriptr.io, a publisher could be any of the scripts you implemented, or could be a JavaScript client-side program that is broadcasting messages through a Web Socket connection to scriptr.io. Likewise, subscribers could be any script that is subscribed to a channel, or a JavaScript client-side program using a Web Socket connection to subscribe to one of your channels for example.

You can subscribe a script to a channel from your scriptr.io workspace (click on the “subscribe” button in the top-right corner), but you can also do this programmatically, which gives you more flexibility. The shorthand notation is to use the built-in subscribe() function. You can also use the native “pubsub” module that gives you even more control over the channels.

MQTT Bridges

Scriptr.io’s channels are in fact abstractions of any publish/subscribe mechanism. Therefore, you can use them to subscribe your scripts to external topics, such as MQTT topics notably. This is really valuable considering that MQTT is a light-weight pub/sub protocol, initially designed for machine-to-machine communication, and currently one of the most used protocols in IoT projects.

Subscribing to MQTT topics is done through “bridges”, which basically are protocol adapters. Configuring a new MQTT bridge for your scriptr.io account is a two steps process: first create an MQTT endpoint, second, bind the endpoint to a channel.

To create an MQTT endpoint, click on your username in the top-right corner of the workspace, then select “settings”. In the settings dialog, click on the “External Endpoint” tab, then on the “+” sign, “Add External Point Configuration”:
In the protocol field, select “mqtt”, then enter a name for your endpoint configuration. In the URL field, enter the URL the MQTT broker, making sure to add the “mqtt://” prefix your URL!. Specify the name of MQTT topic to subscribe to then enter a Client Id field and your credentials on the targeted MQTT broker.

Once the endpoint is ready, you create the bridge by binding the endpoint to a channel. From your username in the top-right corner of the workspace, click on “settings” then on “channels”. Select an existing channel (or create a new one) then click on the little “earth” (manage bridges) icon. Select an endpoint from the drop-down and paste one of your scriptr.io authentication tokens (your private token or one of your devices’)

Publish to MQTT topics

Yes, you can publish to MQTT topics as well from your scripts, by requiring the “mqtt” module, from which you need to create an MQTT client instance, using the getInstance() method. There are actually two options to do that:

Option 1 – ad hoc
In this option, you need to invoque the getInstance() method of the mqtt module passing the endpoint and the corresponding credentials, as in the below example:

var mqtt = require("mqtt");
var mqttInstance = mqtt.getInstance("mqtt://some_endpoint", {"clientId": "some_id", "username": "some_username", "password": "somepassword"}); // other options are available

Option 2 – using an endpoint
Assume you will be publishing regularly to the same MQTT endpoint. In that case, it is preferable to actually configure an endpoint, as described in the preceding “MQTT Bridges”paragraph, then use the configuration’s name when obtaining an instance, as in the below:

// assume we have create the mosquitto endpoint configuration, from settings/endpoints
var mqtt = require("mqtt");
var mqttInstance = mqtt.getInstance("mosquitto");

The mqtt client instance exposes the “publish()” function:

mqttInstance.publish("some_topic", JSON.stringify(some_msg));

Trying MQTT publish/subscribe from scriptr.io

If you do not have any MQTT broker to use for testing your bridges, there are many free brokers out there, such as: test.mosquitto.org. We will use the latter in the following example.

Create the bridge

As explained in the above paragraphs, create an bridge, providing mqtt://test.mosquitto.org as the URL and 1883 as the port. Pick any values you want for the bridge (we’ll call it “free_mosquitto”) and the client id (we’ll set it to “scriptrio”).

mosquitto_bridge

Once you are done with the endpoint, bind it to a channel, as previously explained.

channel_bridge

subscribe a script to the MQTT topic

Select one of your existing scripts or create a new one an subscribe it to the channel you used to create the MQTT bridge. In our example, we will create a new script (“housecontroller”) that will expect to find the following payload structure in the received messages:

{
 "humidity": <some_value>,
 "temperature": <some_value>
}

If the message is in the JSON format, it will be automatically parsed by scriptr.io and made available in the “body” field of the native request object. If it’s format is not JSON, the message will be available in request.rawBody.

var msg = requet.body; // assuming it is json
if (msg.temperature < 21) {
  // turn heater on
}

if (msg.humidity > 60) {
  // turn dehumidifier on
}

Publish to MQTT

The below is an example of the code required to connect and publish to a topic from a script. Note that since we are using the free mosquitto broker that does not require authentication, we can omit the username and password.

var mqtt = require("mqtt");
var endpoint = "mqtt://test.mosquitto.org:1883";
var topic = "scriptrio_commands"; // publish to a different topic
var username = "";
var password = "";
var clientId = "scriptrio1234567890";

var msg = {"command": {"heater":"on", "dehumidifer":"on"}};
try {
    
    var mqttInstance = mqtt.getInstance(endpoint, {"clientId": clientId});
    var resp = mqttInstance.publish(topic, JSON.stringify(msg));
    if (resp.metadata.status == "failure") {
        throw resp;
    }

    return resp.result;
}catch(exception){
    return exception;
}

The complete script

Susbcribe the below script to the channel that is bound to your MQTT endpoint. Using an MQTT client, publish messages on the corresponding topic and check that the script is receiving them and publishing commands to the other one.

var mqtt = require("mqtt");
var endpoint = "tcp://test.mosquitto.org:1883";
var topic = "scriptrio_commands";
var username = "";
var password = "";
var clientId = "scriptrio1234567890";


var msg = request.body; // assuming it is json
var instruction = { command: {}};
if (msg.temperature < 21) {
  instruction.command.heater = "on";
}

if (msg.humidity > 60) {
  instruction.command.dehumidifer = "on";
} 

try {
    var mqttInstance = mqtt.getInstance(endpoint, {"clientId": clientId});
    var resp = mqttInstance.publish(topic, JSON.stringify(msg));
    if (resp.metadata.status == "failure") {
        throw resp;
    }

    return resp.result;
}catch(exception){
    return exception;
}