Skip to content

Instantly share code, notes, and snippets.

@tnowak

tnowak/README.md Secret

Last active Mar 23, 2021
Embed
What would you like to do?
Airly2MQTT

Simple flow to get air quality measurements into MQTT broker. Includes current PM2.5 and PM10 values with limits percentage and extened CAQI air quality index. Based on data provided by Airly.eu public API. Please see map for available air quality stations.

Attention: before importing this flow please add node-red-contrib-credentials module to your Node-RED pallete first!

Update: Airly has lately limited it's free public API to 100 calls per day. That's 1 call every ~15 minutes but we also do nearest station API call every hour. So I changed the interval to 20 minutes now and the flow fits the current limit perfectly (96 calls per day in total).

Airly2MQTT-v3-desc

[
{
"id":"270210d8.03352",
"type":"http request",
"z":"1c429eb9.f7ea21",
"name":"Get measurements",
"method":"GET",
"ret":"obj",
"paytoqs":false,
"url":"https://airapi.airly.eu/v2/measurements/installation?installationId={{installationId}}",
"tls":"",
"proxy":"",
"authType":"",
"x":610,
"y":460,
"wires":[
[
"7fe14370.ea286c",
"569b13fc.2d5bcc",
"d56f9845.8f0038",
"57962394.99249c",
"4b01a992.9fc618",
"cd5469a3.ba8788"
]
]
},
{
"id":"7fe14370.ea286c",
"type":"function",
"z":"1c429eb9.f7ea21",
"name":"AIRLY CAQI value",
"func":"return { payload: msg.payload.current.indexes[0].value };",
"outputs":1,
"noerr":0,
"x":890,
"y":580,
"wires":[
[
"eab61933.a596b8"
]
]
},
{
"id":"a1c7aa6.f3ee558",
"type":"http request",
"z":"1c429eb9.f7ea21",
"name":"Get nearest station",
"method":"GET",
"ret":"obj",
"paytoqs":false,
"url":"https://airapi.airly.eu/v2/installations/nearest?lat={{lat}}&lng={{lng}}&maxDistanceKM={{maxDistanceKM}}",
"tls":"",
"proxy":"",
"authType":"",
"x":330,
"y":600,
"wires":[
[
"9da00eaa.4cfe",
"871ebf1b.9b3f"
]
]
},
{
"id":"a146c7a9.d64238",
"type":"function",
"z":"1c429eb9.f7ea21",
"name":"Set params",
"func":"msg.lat = flow.get(\"latitude\");\nmsg.lng = flow.get(\"longitude\");\nmsg.maxDistanceKM = flow.get(\"maxDistanceKM\");\nmsg.headers = {};\nmsg.headers['Accept'] = 'application/json';\nmsg.headers['apikey'] = flow.get(\"apikey\");\nreturn msg;",
"outputs":1,
"noerr":0,
"x":190,
"y":540,
"wires":[
[
"a1c7aa6.f3ee558"
]
]
},
{
"id":"569b13fc.2d5bcc",
"type":"function",
"z":"1c429eb9.f7ea21",
"name":"AIRLY CAQI level",
"func":"return { payload: msg.payload.current.indexes[0].level };",
"outputs":1,
"noerr":0,
"x":890,
"y":640,
"wires":[
[
"e0398db0.f1d2a"
]
]
},
{
"id":"d56f9845.8f0038",
"type":"function",
"z":"1c429eb9.f7ea21",
"name":"PM2.5 value",
"func":"return { payload: msg.payload.current.values[1].value };",
"outputs":1,
"noerr":0,
"x":870,
"y":300,
"wires":[
[
"bb4e6243.5d7be"
]
]
},
{
"id":"4b01a992.9fc618",
"type":"function",
"z":"1c429eb9.f7ea21",
"name":"PM10 value",
"func":"return { payload: msg.payload.current.values[2].value };",
"outputs":1,
"noerr":0,
"x":870,
"y":420,
"wires":[
[
"a1988859.7637a8"
]
]
},
{
"id":"57962394.99249c",
"type":"function",
"z":"1c429eb9.f7ea21",
"name":"PM2.5 limit-percent",
"func":"return { payload: msg.payload.current.standards[0].percent };",
"outputs":1,
"noerr":0,
"x":890,
"y":360,
"wires":[
[
"365d4481.588b3c"
]
]
},
{
"id":"cd5469a3.ba8788",
"type":"function",
"z":"1c429eb9.f7ea21",
"name":"PM10 limit-percent",
"func":"return { payload: msg.payload.current.standards[1].percent };",
"outputs":1,
"noerr":0,
"x":890,
"y":480,
"wires":[
[
"d2e68e8a.732ca"
]
]
},
{
"id":"bb4e6243.5d7be",
"type":"mqtt out",
"z":"1c429eb9.f7ea21",
"name":"node-red/Airly/PM25/value",
"topic":"node-red/Airly/PM25/value",
"qos":"0",
"retain":"true",
"broker":"a1b0e25b.130ed",
"x":1180,
"y":300,
"wires":[
]
},
{
"id":"365d4481.588b3c",
"type":"mqtt out",
"z":"1c429eb9.f7ea21",
"name":"node-red/Airly/PM25/limit-percent",
"topic":"node-red/Airly/PM25/limit-percent",
"qos":"0",
"retain":"true",
"broker":"a1b0e25b.130ed",
"x":1160,
"y":360,
"wires":[
]
},
{
"id":"a1988859.7637a8",
"type":"mqtt out",
"z":"1c429eb9.f7ea21",
"name":"node-red/Airly/PM10/value",
"topic":"node-red/Airly/PM10/value",
"qos":"0",
"retain":"true",
"broker":"a1b0e25b.130ed",
"x":1180,
"y":420,
"wires":[
]
},
{
"id":"d2e68e8a.732ca",
"type":"mqtt out",
"z":"1c429eb9.f7ea21",
"name":"node-red/Airly/PM10/limit-percent",
"topic":"node-red/Airly/PM10/limit-percent",
"qos":"0",
"retain":"true",
"broker":"a1b0e25b.130ed",
"x":1160,
"y":480,
"wires":[
]
},
{
"id":"eab61933.a596b8",
"type":"mqtt out",
"z":"1c429eb9.f7ea21",
"name":"node-red/Airly/CAQI/value",
"topic":"node-red/Airly/CAQI/value",
"qos":"0",
"retain":"true",
"broker":"a1b0e25b.130ed",
"x":1190,
"y":580,
"wires":[
]
},
{
"id":"e0398db0.f1d2a",
"type":"mqtt out",
"z":"1c429eb9.f7ea21",
"name":"node-red/Airly/CAQI/level",
"topic":"node-red/Airly/CAQI/level",
"qos":"0",
"retain":"true",
"broker":"a1b0e25b.130ed",
"x":1190,
"y":640,
"wires":[
]
},
{
"id":"871ebf1b.9b3f",
"type":"change",
"z":"1c429eb9.f7ea21",
"name":"Remember station",
"rules":[
{
"t":"set",
"p":"installationId",
"pt":"flow",
"to":"msg.payload[0].id",
"tot":"msg"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":570,
"y":600,
"wires":[
[
"73460a46.373094"
]
]
},
{
"id":"5285b86b.f2ed28",
"type":"switch",
"z":"1c429eb9.f7ea21",
"name":"Station selected?",
"property":"installationId",
"propertyType":"flow",
"rules":[
{
"t":"regex",
"v":"\\d",
"vt":"str",
"case":false
},
{
"t":"else"
}
],
"checkall":"false",
"repair":false,
"outputs":2,
"x":210,
"y":440,
"wires":[
[
"73460a46.373094"
],
[
"a146c7a9.d64238"
]
],
"outputLabels":[
"Yes",
"No"
]
},
{
"id":"632fa76e.0665d8",
"type":"inject",
"z":"1c429eb9.f7ea21",
"name":"Pool every 20m",
"topic":"",
"payload":"",
"payloadType":"date",
"repeat":"1200",
"crontab":"",
"once":true,
"onceDelay":0.1,
"x":210,
"y":320,
"wires":[
[
"566326a.93050d8",
"5285b86b.f2ed28",
"83c3ace3.ba29d"
]
]
},
{
"id":"566326a.93050d8",
"type":"credentials",
"z":"1c429eb9.f7ea21",
"name":"Airly API key",
"props":[
{
"value":"apikey",
"type":"flow"
}
],
"x":550,
"y":200,
"wires":[
[
]
]
},
{
"id":"cc331755.292fc8",
"type":"change",
"z":"1c429eb9.f7ea21",
"name":"Forget station",
"rules":[
{
"t":"delete",
"p":"installationId",
"pt":"flow"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":560,
"y":680,
"wires":[
[
]
]
},
{
"id":"9da00eaa.4cfe",
"type":"delay",
"z":"1c429eb9.f7ea21",
"name":"After 1h",
"pauseType":"delay",
"timeout":"1",
"timeoutUnits":"hours",
"rate":"1",
"nbRateUnits":"1",
"rateUnits":"second",
"randomFirst":"1",
"randomLast":"5",
"randomUnits":"seconds",
"drop":false,
"x":360,
"y":680,
"wires":[
[
"cc331755.292fc8"
]
]
},
{
"id":"73460a46.373094",
"type":"function",
"z":"1c429eb9.f7ea21",
"name":"Set params",
"func":"msg.installationId = flow.get(\"installationId\");\nmsg.headers = {};\nmsg.headers['Accept'] = 'application/json';\nmsg.headers['apikey'] = flow.get(\"apikey\");\nreturn msg;",
"outputs":1,
"noerr":0,
"x":470,
"y":400,
"wires":[
[
"270210d8.03352"
]
]
},
{
"id":"d3caa67a.85e148",
"type":"comment",
"z":"1c429eb9.f7ea21",
"name":"<-- Put your GPS location here",
"info":"",
"x":770,
"y":160,
"wires":[
]
},
{
"id":"ebfbb643.43c538",
"type":"comment",
"z":"1c429eb9.f7ea21",
"name":"<-- Put your Airly API key here (package -contrib-credentials required)",
"info":"",
"x":890,
"y":200,
"wires":[
]
},
{
"id":"83c3ace3.ba29d",
"type":"change",
"z":"1c429eb9.f7ea21",
"name":"Configuration",
"rules":[
{
"t":"set",
"p":"latitude",
"pt":"flow",
"to":"52.180000",
"tot":"str"
},
{
"t":"set",
"p":"longitude",
"pt":"flow",
"to":"21.02000",
"tot":"str"
},
{
"t":"set",
"p":"maxDistanceKM",
"pt":"flow",
"to":"25",
"tot":"str"
}
],
"action":"",
"property":"",
"from":"",
"to":"",
"reg":false,
"x":550,
"y":160,
"wires":[
[
]
],
"icon":"node-red/cog.png"
},
{
"id":"a1b0e25b.130ed",
"type":"mqtt-broker",
"z":"",
"name":"MQTT broker",
"broker":"192.168.1.2",
"port":"1883",
"clientid":"",
"usetls":false,
"compatmode":true,
"keepalive":"60",
"cleansession":true,
"birthTopic":"",
"birthQos":"0",
"birthPayload":"",
"closeTopic":"",
"closeQos":"0",
"closePayload":"",
"willTopic":"",
"willQos":"0",
"willPayload":""
}
]
@matfiz

This comment has been minimized.

Copy link

@matfiz matfiz commented Jan 3, 2020

Great work, @tnowak!

I have added a couple of more info that are available from Airly + added a header to fetch strings in polish locale.

Image 2020-01-03 at 4 48 39 PM

[{"id":"831a23e4.73668","type":"tab","label":"Airly","disabled":false,"info":""},{"id":"a3405745.2b3668","type":"http request","z":"831a23e4.73668","name":"Get measurements","method":"GET","ret":"obj","url":"https://airapi.airly.eu/v2/measurements/installation?installationId={{installationId}}","tls":"","x":510,"y":368,"wires":[["3a6c660f.5eb17a","e7f2fb4f.08d4e8","4d95ccd8.ccc954","127c8530.42e74b","c843bac4.816d28","39b397c7.dfd6c8","67c0407f.0dd34","4f81f43d.99a00c","6a41a5e.c4d035c","e7a00534.3854c8","423fb44d.ed5dec","e4ad28e9.6ecb38"]]},{"id":"3a6c660f.5eb17a","type":"function","z":"831a23e4.73668","name":"AIRLY CAQI value","func":"return { payload: msg.payload.current.indexes[0].value };","outputs":1,"noerr":0,"x":782,"y":606,"wires":[["4e1c6e09.ff18a"]]},{"id":"86e0b040.12bb2","type":"http request","z":"831a23e4.73668","name":"Get nearest station","method":"GET","ret":"obj","url":"https://airapi.airly.eu/v2/installations/nearest?lat={{lat}}&lng={{lng}}&maxDistanceKM={{maxDistanceKM}}","tls":"","x":230,"y":508,"wires":[["74e1d066.979de","612d2489.fda06c"]]},{"id":"c66a08f6.069738","type":"function","z":"831a23e4.73668","name":"Set params","func":"msg.lat = flow.get(\"latitude\");\nmsg.lng = flow.get(\"longitude\");\nmsg.maxDistanceKM = flow.get(\"maxDistanceKM\");\nmsg.headers = {};\nmsg.headers['Accept'] = 'application/json';\nmsg.headers['apikey'] = flow.get(\"apikey\");\nreturn msg;","outputs":1,"noerr":0,"x":90,"y":448,"wires":[["86e0b040.12bb2"]]},{"id":"e7f2fb4f.08d4e8","type":"function","z":"831a23e4.73668","name":"AIRLY CAQI level","func":"return { payload: msg.payload.current.indexes[0].level };","outputs":1,"noerr":0,"x":783,"y":669,"wires":[["6b9e3e96.ed37"]]},{"id":"4d95ccd8.ccc954","type":"function","z":"831a23e4.73668","name":"PM2.5 value","func":"return { payload: msg.payload.current.values[1].value };","outputs":1,"noerr":0,"x":770,"y":208,"wires":[["8d81414d.0f0b5"]]},{"id":"c843bac4.816d28","type":"function","z":"831a23e4.73668","name":"PM10 value","func":"return { payload: msg.payload.current.values[2].value };","outputs":1,"noerr":0,"x":770,"y":328,"wires":[["2414fbb2.8d8914"]]},{"id":"127c8530.42e74b","type":"function","z":"831a23e4.73668","name":"PM2.5 limit-percent","func":"return { payload: msg.payload.current.standards[0].percent };","outputs":1,"noerr":0,"x":790,"y":268,"wires":[["714d0b8b.8dc164"]]},{"id":"39b397c7.dfd6c8","type":"function","z":"831a23e4.73668","name":"PM10 limit-percent","func":"return { payload: msg.payload.current.standards[1].percent };","outputs":1,"noerr":0,"x":790,"y":388,"wires":[["6eae2a78.8133a4"]]},{"id":"8d81414d.0f0b5","type":"mqtt out","z":"831a23e4.73668","name":"node-red/Airly/PM25/value","topic":"node-red/Airly/PM25/value","qos":"0","retain":"true","broker":"f73acdfd.2b353","x":1080,"y":208,"wires":[]},{"id":"714d0b8b.8dc164","type":"mqtt out","z":"831a23e4.73668","name":"node-red/Airly/PM25/limit-percent","topic":"node-red/Airly/PM25/limit-percent","qos":"0","retain":"true","broker":"f73acdfd.2b353","x":1060,"y":268,"wires":[]},{"id":"2414fbb2.8d8914","type":"mqtt out","z":"831a23e4.73668","name":"node-red/Airly/PM10/value","topic":"node-red/Airly/PM10/value","qos":"0","retain":"true","broker":"f73acdfd.2b353","x":1080,"y":328,"wires":[]},{"id":"6eae2a78.8133a4","type":"mqtt out","z":"831a23e4.73668","name":"node-red/Airly/PM10/limit-percent","topic":"node-red/Airly/PM10/limit-percent","qos":"0","retain":"true","broker":"f73acdfd.2b353","x":1060,"y":388,"wires":[]},{"id":"4e1c6e09.ff18a","type":"mqtt out","z":"831a23e4.73668","name":"node-red/Airly/CAQI/value","topic":"node-red/Airly/CAQI/value","qos":"0","retain":"true","broker":"f73acdfd.2b353","x":1085,"y":602,"wires":[]},{"id":"6b9e3e96.ed37","type":"mqtt out","z":"831a23e4.73668","name":"node-red/Airly/CAQI/level","topic":"node-red/Airly/CAQI/level","qos":"0","retain":"true","broker":"f73acdfd.2b353","x":1087,"y":664,"wires":[]},{"id":"612d2489.fda06c","type":"change","z":"831a23e4.73668","name":"Remember station","rules":[{"t":"set","p":"installationId","pt":"flow","to":"msg.payload[0].id","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":508,"wires":[["875579a2.9960b8"]]},{"id":"2f52011.89896fe","type":"switch","z":"831a23e4.73668","name":"Station selected?","property":"installationId","propertyType":"flow","rules":[{"t":"regex","v":"\\d","vt":"str","case":false},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":110,"y":348,"wires":[["875579a2.9960b8"],["c66a08f6.069738"]],"outputLabels":["Yes","No"]},{"id":"b723d5aa.409dd8","type":"inject","z":"831a23e4.73668","name":"Pool every 10m","topic":"","payload":"","payloadType":"date","repeat":"600","crontab":"","once":true,"onceDelay":0.1,"x":110,"y":228,"wires":[["654315a1.c6329c","2f52011.89896fe","b258825b.4dc2e"]]},{"id":"654315a1.c6329c","type":"credentials","z":"831a23e4.73668","name":"Airly API key","props":[{"value":"apikey","type":"flow"}],"x":450,"y":108,"wires":[[]]},{"id":"22a4d9e2.663ca6","type":"change","z":"831a23e4.73668","name":"Forget station","rules":[{"t":"delete","p":"installationId","pt":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":460,"y":588,"wires":[[]]},{"id":"74e1d066.979de","type":"delay","z":"831a23e4.73668","name":"After 1h","pauseType":"delay","timeout":"1","timeoutUnits":"hours","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":260,"y":588,"wires":[["22a4d9e2.663ca6"]]},{"id":"875579a2.9960b8","type":"function","z":"831a23e4.73668","name":"Set params","func":"msg.installationId = flow.get(\"installationId\");\nmsg.headers = {};\nmsg.headers['Accept'] = 'application/json';\nmsg.headers['Accept-Language'] = 'pl';\nmsg.headers['apikey'] = flow.get(\"apikey\");\nreturn msg;","outputs":1,"noerr":0,"x":370,"y":308,"wires":[["a3405745.2b3668"]]},{"id":"e7911428.63f708","type":"comment","z":"831a23e4.73668","name":"<-- Put your GPS location here","info":"","x":689,"y":78,"wires":[]},{"id":"7849d431.a2c9ec","type":"comment","z":"831a23e4.73668","name":"<-- Put your Airly API key here (package -contrib-credentials required)","info":"","x":809,"y":118,"wires":[]},{"id":"b258825b.4dc2e","type":"change","z":"831a23e4.73668","name":"Configuration","rules":[{"t":"set","p":"latitude","pt":"flow","to":"50.141027","tot":"str"},{"t":"set","p":"longitude","pt":"flow","to":"18.602694","tot":"str"},{"t":"set","p":"maxDistanceKM","pt":"flow","to":"10","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":68,"wires":[[]],"icon":"node-red/cog.png"},{"id":"67c0407f.0dd34","type":"function","z":"831a23e4.73668","name":"pressure","func":"return { payload: msg.payload.current.values[3].value };","outputs":1,"noerr":0,"x":761,"y":448,"wires":[["757362c3.892bfc"]]},{"id":"4f81f43d.99a00c","type":"function","z":"831a23e4.73668","name":"humidity","func":"return { payload: msg.payload.current.values[4].value };","outputs":1,"noerr":0,"x":758,"y":496,"wires":[["51d18efa.f78a3"]]},{"id":"6a41a5e.c4d035c","type":"function","z":"831a23e4.73668","name":"temperature","func":"return { payload: msg.payload.current.values[5].value };","outputs":1,"noerr":0,"x":769,"y":544,"wires":[["842f7319.6d2d6"]]},{"id":"757362c3.892bfc","type":"mqtt out","z":"831a23e4.73668","name":"node-red/Airly/climate/pressure","topic":"node-red/Airly/climate/pressure","qos":"0","retain":"true","broker":"f73acdfd.2b353","x":1039,"y":446,"wires":[]},{"id":"51d18efa.f78a3","type":"mqtt out","z":"831a23e4.73668","name":"node-red/Airly/climate/humidity","topic":"node-red/Airly/climate/humidity","qos":"0","retain":"true","broker":"f73acdfd.2b353","x":1039,"y":491,"wires":[]},{"id":"842f7319.6d2d6","type":"mqtt out","z":"831a23e4.73668","name":"node-red/Airly/climate/temperature","topic":"node-red/Airly/climate/temperature","qos":"0","retain":"true","broker":"f73acdfd.2b353","x":1047,"y":545,"wires":[]},{"id":"e7a00534.3854c8","type":"function","z":"831a23e4.73668","name":"AIRLY CAQI color","func":"return { payload: msg.payload.current.indexes[0].color };","outputs":1,"noerr":0,"x":788,"y":730,"wires":[["5bdf1b2c.933784"]]},{"id":"5bdf1b2c.933784","type":"mqtt out","z":"831a23e4.73668","name":"node-red/Airly/CAQI/color","topic":"node-red/Airly/CAQI/color","qos":"0","retain":"true","broker":"f73acdfd.2b353","x":1084,"y":731,"wires":[]},{"id":"423fb44d.ed5dec","type":"function","z":"831a23e4.73668","name":"AIRLY CAQI description","func":"return { payload: msg.payload.current.indexes[0].description };","outputs":1,"noerr":0,"x":806,"y":783,"wires":[["ca84c3a4.58053"]]},{"id":"ca84c3a4.58053","type":"mqtt out","z":"831a23e4.73668","name":"node-red/Airly/CAQI/description","topic":"node-red/Airly/CAQI/description","qos":"0","retain":"true","broker":"f73acdfd.2b353","x":1106,"y":780,"wires":[]},{"id":"e4ad28e9.6ecb38","type":"function","z":"831a23e4.73668","name":"AIRLY CAQI advice","func":"return { payload: msg.payload.current.indexes[0].advice };","outputs":1,"noerr":0,"x":785,"y":838,"wires":[["b8ad3f2d.d4f57"]]},{"id":"b8ad3f2d.d4f57","type":"mqtt out","z":"831a23e4.73668","name":"node-red/Airly/CAQI/advice","topic":"node-red/Airly/CAQI/advice","qos":"0","retain":"true","broker":"f73acdfd.2b353","x":1095,"y":838,"wires":[]},{"id":"f73acdfd.2b353","type":"mqtt-broker","z":"","name":"MQTT - rancher6","broker":"172.17.17.109","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"domoticz/bridge/state","birthQos":"0","birthPayload":"online","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment