Skip to content

Instantly share code, notes, and snippets.

@tnowak

tnowak/README.md Secret

Last active January 3, 2024 11:32
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tnowak/8a80e0776de15ca118cd6d265f78e434 to your computer and use it in GitHub Desktop.
Save tnowak/8a80e0776de15ca118cd6d265f78e434 to your computer and use it in GitHub Desktop.
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":""
}
]
@mstefanowicz
Copy link

Macie podobny problem z tym flow?
obraz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment