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":""
}
]
@matfiz
Copy link

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":""}]

@marcinwrobel
Copy link

Cześć korzystam z tego prawie od roku ale z aktualizacją grudniową flow przestał działać, dasz radę w wolnej chwili naprawić ?
Pozdrawiam :)

@tnowak
Copy link
Author

tnowak commented Dec 27, 2022

A co Ci konkretnie nie działa? Bo właśnie sprawdziłem i u mnie wszystko zdaje się działać poprawnie. Mam czas odpytywania ustawiony na co 20 minut.

@marcinwrobel
Copy link

Dzięki za odzew, wszystko już działa, problemem była zmiana formatu sensora mqtt. Pozdrawiam.

@tomaszhiterski
Copy link

Gdzie dokładnie wpisać komendę aby zainstalować : node-red-contrib-credentials.

@tnowak
Copy link
Author

tnowak commented Aug 16, 2023

@tomaszhiterski Menu -> Manage palette

@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