Alternatively Titled: How I Made Home Assistant Aware of the Volcano Next Door

As I’ve previously mentioned, I’m a big fan of the Home Assistant MQTT Discovery feature. I’ve also historically been a fan of Node-RED and have recently been getting back into it. This has been mostly due to the uptick in interest in the platform in the HASS community. So, I decided to have a play around and come up with an implementation of an auto-discovered MQTT sensor in Node-RED. This post documents using this approach pull some interesting data into Home Assistant.

Since moving to a different part of New Zealand last year I’ve wanted implement a sensor in HASS which would monitor the state of the local volcano. Luckily, GeoNet provide a nice API for getting volcanic alert levels for all the volcanic fields in NZ. I was initially going to write a custom component for doing this (and at some point contribute it back). However, being generally even shorter on time than usual at the moment I never quite got there. That was until I was playing around with Node-RED and had a brain wave.

The Flow

I’m going to cut straight to the chase and show a screenshot of the flow I came up with. I’ll then explain it below (the flow JSON can be found later in the post):

The start of the flow is pretty basic – a simple inject node which injects a timestamp every 6 hours. The payload to this is irrelevant since it’s just used to kick off the flow. I didn’t want to hit the API endpoint too often since I’ve so far never seen the data change. If the mountain suddenly goes boom, I think I’ll have more pressing issues than whether my data is up to date.

Next, we have the HTTP Request node which goes out and performs a GET request to the URL given in the API documentation above. I enabled TLS support and opted to get the response data back as a parsed JSON object.

Filtering Data

Since the API returns data for all the volcanic fields in New Zealand, I needed to filter the data. The next node just selects the Taranaki/Egmont field that I am interested in. I used the following code in a function node to do this:

for(var i in msg.payload.features) { var feature = msg.payload.features[i]; if(feature.properties.volcanoID=="taranakiegmont") { msg.payload = feature.properties; msg.topic = "homeassistant/sensor/volcano_" + msg.payload.volcanoID + "/state"; break; } }

Basically this just iterates over all the features in the data and finds the one with the ID taranakiegmont and then substitutes it’s data in as the message payload. I also build the topic for the subsequent publish to MQTT based on the volcano ID.

The output of this function branches to another function node on one branch and a delay node on the other. The delay node here is used to make sure that the function node above runs and sends it’s output before the original message passes to the the MQTT publish node.

Building HASS Configuration

The top function node is responsible for building the required configuration payloads and topics for the three sensors this will create via Home Assistant MQTT Discovery. Here I create one sensor for each of the quantities in the data from the API. This is achieved with the following snippet of code:

var config1 = { payload: { name: msg.payload.volcanoTitle + " Activity Level", state_topic: "homeassistant/sensor/volcano_" + msg.payload.volcanoID + "/state", value_template: "{{ value_json.level }}" }, topic: "homeassistant/sensor/volcano_" + msg.payload.volcanoID + "_level/config" }; var config2 = { payload: { name: msg.payload.volcanoTitle + " Activity Description", state_topic: "homeassistant/sensor/volcano_" + msg.payload.volcanoID + "/state", value_template: "{{ value_json.activity }}" }, topic: "homeassistant/sensor/volcano_" + msg.payload.volcanoID + "_activity/config" }; var config3 = { payload: { name: msg.payload.volcanoTitle + " Hazards", state_topic: "homeassistant/sensor/volcano_" + msg.payload.volcanoID + "/state", value_template: "{{ value_json.hazards }}" }, topic: "homeassistant/sensor/volcano_" + msg.payload.volcanoID + "_hazards/config" }; return [config1, config2, config3];

This does the same thing for three new message objects, building a payload and topic for each. I use the ability of HASS to grab data from the payload of the main publish by specifying the state topic. I set this to the topic I built in the previous function node. A value template is also specified for each, pretty much exactly as in the Home Assistant MQTT Discovery documentation.

Output Via MQTT

All three outputs of this node are passed to the MQTT publish node, which publishes with QoS 2 and the retain flag set. This means that whenever Home Assistant comes up after a restart it will see the values in both the configuration and state topics for these sensors and re-create them automatically.

Attentive readers would have also noticed that I publish the configuration messages whenever I publish the state (every 6 hours). This doesn’t matter as HASS will just ignore the configuration messages for sensors which it has already discovered.

So, that’s it. With this in place the sensors should appear in Home Assistant:

The JSON:

As promised, here is the full JSON for the flow. To add this to your Node-RED instance copy it to your clipboard and go to Hamburger->Import->Clipboard in Node-RED and paste the JSON. You can select whether to import to the current flow or a new flow and then hit ‘import’ and you should see the nodes:

[{"id":"1f3ef70f.e7a6b9","type":"http request","z":"9b7b48a9.a28de8","name":"Get Volcano Data","method":"GET","ret":"obj","url":"https://api.geonet.org.nz/volcano/val","tls":"49e1f229.3ce5f4","x":330,"y":120,"wires":[["37e97f8a.c207e8"]]},{"id":"ce39e789.a300f","type":"inject","z":"9b7b48a9.a28de8","name":"Every 6 hours","topic":"","payload":"","payloadType":"date","repeat":"21600","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":120,"wires":[["1f3ef70f.e7a6b9"]]},{"id":"37e97f8a.c207e8","type":"function","z":"9b7b48a9.a28de8","name":"Filter for Taranaki","func":"for(i in msg.payload.features) {

var feature = msg.payload.features[i];

if(feature.properties.volcanoID==\"taranakiegmont\") {

msg.payload = feature.properties;

msg.topic = \"homeassistant/sensor/volcano_\" + msg.payload.volcanoID + \"/state\";

break;

}

}

return msg;","outputs":1,"noerr":0,"x":530,"y":120,"wires":[["ea23e4a2.eaa928","1ca7b72e.202389"]]},{"id":"104d115c.305927","type":"mqtt out","z":"9b7b48a9.a28de8","name":"Send Messages","topic":"","qos":"2","retain":"true","broker":"d76a3146.667c3","x":1020,"y":120,"wires":[]},{"id":"ea23e4a2.eaa928","type":"function","z":"9b7b48a9.a28de8","name":"Format config messages","func":"var config1 = {

payload: {

name: msg.payload.volcanoTitle + \" Activity Level\",

state_topic: \"homeassistant/sensor/volcano_\" + msg.payload.volcanoID + \"/state\",

value_template: \"{{ value_json.level }}\"

},

topic: \"homeassistant/sensor/volcano_\" + msg.payload.volcanoID + \"_level/config\"

};

var config2 = {

payload: {

name: msg.payload.volcanoTitle + \" Activity Description\",

state_topic: \"homeassistant/sensor/volcano_\" + msg.payload.volcanoID + \"/state\",

value_template: \"{{ value_json.activity }}\"

},

topic: \"homeassistant/sensor/volcano_\" + msg.payload.volcanoID + \"_activity/config\"

};

var config3 = {

payload: {

name: msg.payload.volcanoTitle + \" Hazards\",

state_topic: \"homeassistant/sensor/volcano_\" + msg.payload.volcanoID + \"/state\",

value_template: \"{{ value_json.hazards }}\"

},

topic: \"homeassistant/sensor/volcano_\" + msg.payload.volcanoID + \"_hazards/config\"

};

return [config1, config2, config3];","outputs":3,"noerr":0,"x":780,"y":60,"wires":[["104d115c.305927"],["104d115c.305927"],["104d115c.305927"]]},{"id":"1ca7b72e.202389","type":"delay","z":"9b7b48a9.a28de8","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":760,"y":120,"wires":[["104d115c.305927"]]},{"id":"49e1f229.3ce5f4","type":"tls-config","z":"","name":"Standard","cert":"","key":"","ca":"","certname":"","keyname":"","caname":"","verifyservercert":true},{"id":"d76a3146.667c3","type":"mqtt-broker","z":"","name":"Home Broker","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""}]

If you are importing this directly, you will need to configure your MQTT broker settings under the MQTT publish node before hitting ‘deploy’.

Wrap Up

That’s pretty much all there is to it. I hope this has demonstrated the concept of using Node-RED to create sensors in Home Assistant, without any changes to the HASS configuration. The flow presented is pretty simple but actually serves a useful purpose. Hopefully, you can come up with some uses of your own for this approach. Please feel free to share them in the comments below if you do, so that others may benefit from your ideas.

Thanks for reading. I’m working on a few more things with Node-RED so hopefully I’ll post about them soon. Bye!