When I set up this site, I was wondering if there was an easy way to display the current state of a smart system as a kind of widget. Since there are a plethora of WordPress plugins available, I assumed there would exist a plugin for this kind of task. Unfortunately that was not the case, so I had to come up with a solution myself (demo).

Functionality

As a proof of concept, I wanted to display the current state of my coffee machine that I hooked up to the internet (maybe I’m going to write a post about that project in the future!). This coffee machine publishes its state (1 or 0) over MQTT. I started the project with the following goals in mind; the widget has to:

  • Connect to the MQTT broker;
  • Subscribe to the topic on which the coffee machine publishes its state;
  • Change its appearance in realtime based on the current state of the coffee machine.

MQTT over Websocket

I searched for WP plugins that were able to connect to a MQTT broker. I found a few: WP-MQTT, DIOT SCADA with MQTT and a couple of WooCommerce plugins. The first plugin was designed for publishing messages, rather than subscribing to a topic. Very useful, but not in this particular case.

The second plugin (DIOT SCADA with MQTT) looked promising, but it wasn’t updated in years and apparently I couldn’t inject my credentials to the broker. In WordPress it’s really simple to edit plugin code though, so I went to the Plugin Editor and selected js/eds_mqttsub.js

It was as easy as changing:

connOpt = { onSuccess:onConnect };

to the following:

connOpt = {
    userName: "...",
    password: "...",
    onSuccess:onConnect
};

Note that this way your credentials are up for grabs. This Javascript file is now injected in the browser for everyone to read. A better way to do this is to retrieve the credentials via PHP, just as is done with the server/port. It’s also good practice to restrict the MQTT user to ReadOnly access using ACL. However, for testing purposes this was fine.

The plugin is an implementation of the Paho library, which sets up an MQTT connection over a websocket. It is easy enough to create your own implementation, but using this plugin saves some time. I edited the settings (host, port and client id) and found that the shortcode produced the correct value!

[diot topic="coffee/status"]

Now I had to rewrite the plugin to display an icon instead of the value. However, I didn’t like the idea of every page view resulting in a new MQTT connection. All the more because I was testing with a free plan from CloudMQTT which is limited to ten connections (at this time they don’t offer those plans anymore by the way). A more elegant solution would be introducing a bridge that communicates with WordPress. This way we only need a single MQTT connection. Let’s give that a shot!

Endpoint

WordPress comes with a REST API. This provides an interface for applications to interact with your site. We need an endpoint that toggles the coffee icon and a bridge between that endpoint and the MQTT broker.

With the Ultimate Endpoints With Rest Api plugin it’s very easy to add an endpoint to your site. First you’ll have to add a new Api Secret. This consist of a full name and an email address. Then you specify a base and select the forementioned secret. I’ll name the base iot. Now we got an endpoint!

It still doesn’t do anything though, so we’ll write some code for when the endpoint is accessed. Go to functions.php (using the Theme Editor) and add the following function:

// IOT Callback
add_filter("wcra_iot_callback" , "wcra_iot_callback_handler");	
function wcra_iot_callback_handler($param){
	$response = "changed from " . get_option('coffee') . " to " . $param['coffee'];
	update_option('coffee', $param['coffee']);

	return $response;
}

Note that the name of the base should be in the filter hook, i.e. wcra_iot_callback. This code changes the value of coffee in the Options table according to the given parameter. It returns the new value.

Now we need the code to actually change the icon based on this value in Options. I used the XYZ PHP Code plugin to add the following snippet.

<?php
$coffee = get_option('coffee') == 1 ? "coffee-enabled" : "coffee-disabled";
$tt = get_option('coffee') == 1 ? "Currently making coffee!" : "Currently not making coffee.";
echo "<div class=\"tooltip\"><svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"0 0 1000 1000\" enable-background=\"new 0 0 1000 1000\" xml:space=\"preserve\"class=\"$coffee\">
<g><path d=\"M888.6,331c0-28.2-9.9-52.1-29.6-71.8c-19.7-19.7-43.7-29.6-71.8-29.6h-33.8v202.8h33.8c28.2,0,52.1-9.9,71.8-29.6C878.8,383.1,888.6,359.2,888.6,331z M10,736.6h946.2c0,37.3-13.2,69.2-39.6,95.6c-26.4,26.4-58.3,39.6-95.6,39.6H145.2c-37.3,0-69.2-13.2-95.6-39.6C23.2,805.7,10,773.9,10,736.6L10,736.6z M990,331c0,56-19.8,103.7-59.4,143.4c-39.6,39.6-87.4,59.4-143.3,59.4h-33.8v16.9c0,32.4-11.6,60.2-34.8,83.4c-23.2,23.3-51,34.9-83.4,34.8H263.4c-32.4,0-60.2-11.6-83.4-34.8c-23.3-23.2-34.9-51-34.8-83.4V162.1c0-9.1,3.3-17.1,10-23.8s14.6-10,23.8-10h608.3c56,0,103.7,19.8,143.3,59.4S990,275.1,990,331L990,331z\"/></g>
</svg><span class=\"tooltiptext\">$tt</span></div>" ?>

Which I can now summon with the following shortcode.

[xyz-ips snippet="coffee"]	

Currently making coffee!

Live demo!

I used this SVG: https://commons.wikimedia.org/wiki/File:Coffee-icon.svg

To give the icon the proper color, I used this stylesheet (Customize > Additional CSS).

.coffee-disabled{
	  width:50px;
	  fill: #C9C9C9; 
}

.coffee-enabled{
	  width:50px;
	  fill: #84BC6F; 
}

Bridge

At this point, I can control the coffee button by sending an HTTP request to my endpoint, i.e. https://joszuijderwijk.nl/wp-json/wcra/v1/iot/?secret_key=???. The request (JSON) is illustrated below.

{
    "coffee" : "0"  -or-  "1"
}

Any script that monitors the MQTT server and sends this request upon a changed value would suffice. I used Node-RED to do this.

Figure 1: Flow to update site
[{"id":"b83ab05e.6240c","type":"tab","label":"Coffee button site","disabled":false,"info":""},{"id":"f6c2c09f.f5e4d","type":"http request","z":"b83ab05e.6240c","name":"","method":"POST","ret":"txt","paytoqs":"ignore","url":"https://joszuijderwijk.nl/wp-json/wcra/v1/iot/?secret_key=???","tls":"","persist":false,"proxy":"","authType":"","x":630,"y":280,"wires":[[]]},{"id":"7372880b.dd2348","type":"mqtt in","z":"b83ab05e.6240c","name":"","topic":"coffee/status","qos":"1","datatype":"auto","broker":"f1de1ece.72762","x":290,"y":280,"wires":[["e63d1a57.23e2f8"]]},{"id":"e63d1a57.23e2f8","type":"function","z":"b83ab05e.6240c","name":"","func":"var data = msg.payload;\nmsg.payload = {\"coffee\" : data};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":460,"y":280,"wires":[["f6c2c09f.f5e4d"]]},{"id":"f1de1ece.72762","type":"mqtt-broker","name":"koffie-test","broker":"m23.cloudmqtt.com","port":"11773","clientid":"server-berenhuis","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]

Results

This works beautifully! One minor drawback is that the state isn’t updated in real time: you only see changes when you refresh the site. This would not be the case in the approach described at the beginning of this post. However, I don’t see it as a problem for this particular use.

Video 1: Demo

Leave a Reply

Your email address will not be published. Required fields are marked *