The agent
One static binary that reads your sensor, buffers through outages, and sends the readings.
curl -sSL https://openaqi.net/install.sh | shNo runtime, no dependencies, no container. It runs on a Raspberry Pi you forget about, which is the point — every install step loses contributors.
Two commitments
--dry-run prints exactly what would be sent and sends nothing. Anyone
weighing up whether to run this on their home network should be able to see the
payload rather than take our word for it.
Readings survive outages. They are buffered to disk and replayed with the timestamp they were taken, not when they were finally delivered. A three-day outage produces a three-day gap that fills in correctly afterwards.
Sources
Write anything that prints JSON and exits. Python, a shell one-liner, a compiled binary — whatever already talks to your sensor.
#!/bin/sh
# read-my-sensor.sh — this is the entire contract.
echo '{"pm2p5": 8.4, "co2": 612}'openaqi-agent --source exec \
--exec "/usr/local/bin/read-my-sensor.sh" \
--key oaq_your_sensor_keyThree output shapes are accepted, all equivalent:
{"pm2p5": 8.4, "co2": 612}
[{"metric": "pm2p5", "value": 8.4, "unit": "ug/m3"}]
{"readings": [{"metric": "pm2p5", "value": 8.4, "ts": "2026-08-02T09:00:00Z"}]}A local console account is required
Not a Ubiquiti cloud login, and not an API key. UniFi's documented Integration API does not expose air quality at all — its sensor endpoints return an error for those fields, and the readings only exist behind a session cookie. Create a local user on the console with read access to Protect.
openaqi-agent --source unifi \
--unifi-host 10.0.0.1 \
--unifi-user your-local-user \
--unifi-pass '••••••••' \
--dry-runDrop --dry-run and add --key to send for real. With more than one sensor,
add --unifi-sensor "Bedroom" — and if the name is wrong the agent lists the
ones it found rather than failing silently.
The console's self-signed certificate is accepted by default for LAN addresses.
That is what --unifi-insecure controls, and it defaults on because a console
on your own network is exactly the case where verification cannot succeed.
There is a second route for this hardware that does not involve the agent at all — the self-hosted dashboard forwards with one environment variable. See UniFi Protect.
Running it as a service
# /etc/systemd/system/openaqi-agent.service
[Unit]
Description=openaqi agent
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
EnvironmentFile=/etc/openaqi/agent.env
ExecStart=/usr/local/bin/openaqi-agent --source unifi
Restart=always
RestartSec=30
DynamicUser=yes
StateDirectory=openaqi
ProtectSystem=strict
PrivateTmp=yes
NoNewPrivileges=yes
[Install]
WantedBy=multi-user.target# /etc/openaqi/agent.env — chmod 600
OPENAQI_KEY=oaq_your_sensor_key
UNIFI_HOST=10.0.0.1
UNIFI_USER=your-local-user
UNIFI_PASS=••••••••Credentials go in a root-only file rather than on the command line, where every
process on the box can read them out of ps.
sudo systemctl enable --now openaqi-agent
sudo journalctl -u openaqi-agent -fOptions
| Flag | Env | Default | |
|---|---|---|---|
--key | OPENAQI_KEY | — | Sensor key |
--server | OPENAQI_SERVER | https://ingest.openaqi.net | |
--source | OPENAQI_SOURCE | — | unifi or exec |
--interval | OPENAQI_INTERVAL | 60s | How often to read |
--buffer | OPENAQI_BUFFER | state dir | Where unsent readings live |
--buffer-max | 50000 | Readings held when offline | |
--dry-run | off | Print, send nothing | |
--once | off | Read once and exit |
It tells you when a unit looks wrong
level=WARN msg="this reading is implausible as sent, but makes sense in another unit"
metric=temperature sent=150 assumed_unit=°C looks_like=F would_be="65.56 °C"
fix="emit {\"metric\":\"temperature\",\"value\":150,\"unit\":\"F\"}"It catches the loud half of the unit problem. It cannot catch 71.6 °F, which is
a plausible °C too — which is exactly why unit exists on the wire.
Source
The agent is open source, because nobody should run a closed binary that phones home with their location. Read it, build it yourself, or add support for your hardware: github.com/bsidio/openaqi-agent.