Sensor.Comunity (luftdaten) Integration issue. Solved with Node-RED

PREFACE
The solution below makes use of Node-RED and a MQTT server configured in Home Assistant. Directions on how to install and setup them are not provided herein.
Node-RED Home Assistant palette is not required.

BACKGROUND
I own an air quality sensor enrolled in the Sensor.Community.
I make use of the corresponding Home Assistant integration to plot the values accordingly.

image

THE ISSUE
I have been experiencing plot quality issues for quite sometime:
image

THE ANALYSIS
The header of the HA log shows: Unexpected error fetching luftdaten_XXXXX data which seems concerning a timeout issue. I found the issue has been registered in GitHub.
Note
Although the GitHub issue is closed, I still experience the problem.

THE SOLUTION
The solution I built is based on the workaround posted by ignisf in GitHub.
I decided to make use of Node-RED to solve the issue.

The Node-RED Flow

The Flow in JSON

[
    {
        "id": "c5bc630a86565761",
        "type": "tab",
        "label": "Home Assistant & Sensor.Community",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "caae032a51a9ec72",
        "type": "http request",
        "z": "c5bc630a86565761",
        "name": "luftdaten API",
        "method": "GET",
        "ret": "obj",
        "paytoqs": "ignore",
        "url": "",
        "tls": "",
        "persist": false,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "",
        "senderr": false,
        "headers": [],
        "x": 550,
        "y": 160,
        "wires": [
            [
                "0ee3fca891178449"
            ]
        ]
    },
    {
        "id": "c1c520a860d82996",
        "type": "inject",
        "z": "c5bc630a86565761",
        "name": "sensorId",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "300",
        "crontab": "",
        "once": true,
        "onceDelay": "30",
        "topic": "",
        "payload": "",
        "payloadType": "str",
        "x": 160,
        "y": 160,
        "wires": [
            [
                "195d30fdbe19ea29"
            ]
        ]
    },
    {
        "id": "a56737d36492e822",
        "type": "mqtt out",
        "z": "c5bc630a86565761",
        "name": "HA MQTT",
        "topic": "",
        "qos": "",
        "retain": "",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "28edf644b1616230",
        "x": 1220,
        "y": 160,
        "wires": []
    },
    {
        "id": "8604b48a797bf09e",
        "type": "debug",
        "z": "c5bc630a86565761",
        "name": "to MQTT",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1220,
        "y": 120,
        "wires": []
    },
    {
        "id": "0ee3fca891178449",
        "type": "switch",
        "z": "c5bc630a86565761",
        "name": "Is empty or error?",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "empty"
            },
            {
                "t": "nempty"
            },
            {
                "t": "cont",
                "v": "RequestError",
                "vt": "str"
            }
        ],
        "checkall": "false",
        "repair": false,
        "outputs": 3,
        "x": 770,
        "y": 160,
        "wires": [
            [
                "3032f69cb8eed27a"
            ],
            [
                "a9a1975ce7be5c0e"
            ],
            [
                "f86a3e4651d21fb2"
            ]
        ]
    },
    {
        "id": "3032f69cb8eed27a",
        "type": "debug",
        "z": "c5bc630a86565761",
        "name": "empty",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 970,
        "y": 120,
        "wires": []
    },
    {
        "id": "195d30fdbe19ea29",
        "type": "change",
        "z": "c5bc630a86565761",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "url",
                "pt": "msg",
                "to": "\"https://data.sensor.community/airrohr/v1/sensor/\" & msg.payload & \"/\"",
                "tot": "jsonata"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 350,
        "y": 160,
        "wires": [
            [
                "caae032a51a9ec72"
            ]
        ]
    },
    {
        "id": "f94524013fa18737",
        "type": "comment",
        "z": "c5bc630a86565761",
        "name": "Luftdaten - Home Assistant Integration",
        "info": "**RELEASE NOTES**\n\n**Author**: Tamadite\n\n**Date**: November 14th, 2022\n\n**Reference**: https://github.com/home-assistant/core/issues/61687\n\n1. Type the **sensorId** in the injector, e.g. 12345\n2. P1 -> PM10  --- UoM: µg/m³\n3. P2 -> PM2.5 --- UoM: µg/m³\n4. The solution assumes that API response object[0] provides latest update. \n\n",
        "x": 230,
        "y": 80,
        "wires": []
    },
    {
        "id": "a9a1975ce7be5c0e",
        "type": "function",
        "z": "c5bc630a86565761",
        "name": "get PM10 & PM2.5",
        "func": "var i = 0;\nvar found = 0;     // counter when P1 or P2 is found\nvar msg_out = [];  // array for message objects\n\nwhile (i < msg.payload[0].sensordatavalues.length && found < 2) {\n    var newmsg = null;\n    if (msg.payload[0].sensordatavalues[i].value_type == \"P1\") {\n        newmsg = {payload: msg.payload[0].sensordatavalues[i].value, topic: \"luftdaten/\" + msg.payload[0].sensor.id + \"/PM10\"};\n        found ++;\n    } else {\n        if (msg.payload[0].sensordatavalues[i].value_type == \"P2\") {\n            newmsg = {payload: msg.payload[0].sensordatavalues[i].value, topic: \"luftdaten/\" + msg.payload[0].sensor.id + \"/PM2_5\"};\n            found ++;\n        }\n    }\n    if (newmsg) {\n        msg_out.push(newmsg);\n    }\n    i ++;\n}\nreturn [msg_out];",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1010,
        "y": 160,
        "wires": [
            [
                "8604b48a797bf09e",
                "a56737d36492e822"
            ]
        ]
    },
    {
        "id": "f86a3e4651d21fb2",
        "type": "debug",
        "z": "c5bc630a86565761",
        "name": "error",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 970,
        "y": 200,
        "wires": []
    },
    {
        "id": "28edf644b1616230",
        "type": "mqtt-broker",
        "name": "",
        "broker": "",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "willMsg": {},
        "userProps": "",
        "sessionExpiry": ""
    }
]

Node-RED Flow Notes

  • The Injector node needs to be configured with the sensorId.
  • The sensorId can be retrieved from the sensor.community map when clicking on the desire station.
  • The Injector automatically starts 30 seconds after deployment (see Injector node).
  • The Injector repeat interval is set to every five minutes (see Injector node).
  • The MQTT Out node has to be configured with the corresponding MQTT server used by HA.
  • The Switch node is necessary because the API returns empty sometimes.
  • The flow assumes the API response object [0] contains the latest update.
  • The data gets published in MQTT as:
    ** luftdaten/[sensorId]/PM10
    ** luftdaten/[sensorId]/PM2_5

The HA Configuration
In the configuration.yaml file under section mqtt: add the following:

mqtt:
  sensor:
    - name: "luftdaten_pm10"
      state_topic: "luftdaten/[sensorId]/PM10"
      unit_of_measurement: "μg/m³"
    - name: "luftdaten_pm2_5"
      state_topic: "luftdaten/[sensorId]/PM2_5"
      unit_of_measurement: "μg/m³"

Remark: Replace [sensorId] by the sensorId introduced in the Node-RED Injector node.

THE RESULT

FINAL NOTE
The Sensor Community was formerly known as Luftdaten.

Hello, but what if I wanted to do the opposite?
I have a SDS011 sensor with Tasmota and I would like to send data to “sensor community”, in python I can do it, but I can’t do it with Node-red, I’m not very good with javascript.

Would you have any examples?

Giuseppe

I answer myself, I changed the API call type and managed to pass data from Node-Red.

I use this call:
https://api.sensor.community/v1/push-sensor-data/?format=json