Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Notifies me via Twilio when my local AQI slips into "unhealthy" ranges
How to use:
- Set up Twilio:
You will need a Twilio account and a Twilio phone number. All in all this should run you
about $1. Details here:
You'll want to save your new Twilio # into your phone contacts and set to favorite so it can wake you up
if you're using DND.
- Find the sensor you want to monitor:
Go to Purple Air ( and select the sensor closest to yourself.
You can find the ID by looking at the network request (ID in the "show=" param when you click it)
- Running as a cron:
Either run this as a node process as it is currently set up and then cron is taken care of for you.
Or extract the code from the cron.schedule() call and use it to set up a worker differently.
As written this code will run every 1 minute.
- Current limitations:
This script has no knowledge of previous alerts or any mechanism for "silencing" and as
such it will continue to harass you until the air quality slips back beneath the acceptable
threshold or you stop the script
Also, this currently will only notify one person. It'd be cool if you could add a group of phone numbers
to notify, but this is a lazily hacked together little piece of code so....
It would be cool if this properly logged to somewhere other than the console, but again, lazy.
const cron = require('node-cron');
const express = require('express');
const fetch = require('node-fetch');
const accountSid = '<Twilio Account SID Here>';
const authToken = '<Twilio Auth Token Here>';
const client = require('twilio')(accountSid, authToken);
const endpoint = '<Enter the ID for the sensor you want to watch here>';
app = express();
// Job runs every minute
cron.schedule('* * * * *', () => {
console.log('Running check on air quality');
.then(response => response.json())
.then(data => {
const averagedPm25 = pm25Average(data);
const threatLevel = getLevel(averagedPm25);
const checkTime = new Date();
console.log(`PM25 level: ${averagedPm25} at ${new Date (checkTime)}`);
console.log`Current threat level: , ${threatLevel}`);
// You'll want to indicate your threshold for when you no longer want to be breathing outside air here
// This is currently set to alarm when the air quality enters "Unhealthly for Sensitive Groups"
if (averagedPm25 >= 35.5) {
url: '', // Using Twilio defaults here because it doesn't matter, I just want it to call me
from: '<Twilio Number>',
to: '<Your Mobile Number>',
.then(call => console.log(call.sid))
.catch(err => console.log(err));
body: `Batten down the hatches! We just dipped into ${threatLevel}`,
from: '<Twilio Number>',
to: '<Your Mobile Number>',
.then(message => console.log(message.sid))
.catch(err => console.log(err))
const pm25Average = (data) => {
const stats0 = JSON.parse(data['results'][0]['Stats']);
const stats1 = JSON.parse(data['results'][1]['Stats']);
return (stats0['v'] + stats1['v']) / 2;
const getLevel = (average) => {
if (average <= 12.0) {
return 'Good!';
if (average <= 35.4) {
return 'Moderate';
if (average <= 55.4) {
return 'Unhealthy for Sensitive Groups';
if (average <= 150.4) {
return 'Unhealthy';
if (average <= 250.4) {
return 'Very Unhealthy';
if (average <= 350.4) {
return 'You might be in Mordor';
app.listen(process.env.PORT || 5000)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment