Live AQI Monitoring

from vayuayan import CPCBLive
import json
import time
from datetime import datetime
import pandas as pd

1. Get Your Location

# Initialize the Live AQI client
client = CPCBLive()

# Get your location
location = client.get_system_location()
print("Your Location:")
print(json.dumps(location, indent=2))
Your Location:
[
  19.0748,
  72.8856
]

2. Find Nearest Monitoring Station

# Find nearest station
nearest_station = client.get_nearest_station()
print("Nearest Monitoring Station:")

# Extract station ID for future use
station_id = nearest_station[0]
station_name = nearest_station[1]
print(f"\nMonitoring Station ID: {station_id}")
print(f"Station Name: {station_name}")
Nearest Monitoring Station:

Monitoring Station ID: site_5104
Station Name: Kurla, Mumbai - MPCB

3. Get Current Air Quality Data

# Get current AQI data
aqi_data = client.get_live_aqi_data(station_id=station_id)

if isinstance(aqi_data, Exception):
    print(f"❌ {aqi_data}")
print(f"Current Air Quality at {station_name}:")
metrics = aqi_data.get("metrics", [])
if metrics:
    print("Pollutant   Avg   Min   Max   Period")
    print("-" * 40)
    for m in metrics:
        print(
            f"{m['name']:<10} {m['avg']:<5} {m['min']:<5} {m['max']:<5} {m['avgDesc']}"
        )
else:
    print("No data available, possibly due to station being offline.")
Current Air Quality at Kurla, Mumbai - MPCB:
Pollutant   Avg   Min   Max   Period
----------------------------------------
PM2.5      62    51    83    Over the last 24 hours
PM10       77    60    106   Over the last 24 hours
NO2        5     4     7     Over the last 24 hours
NH3        1     1     2     Over the last 24 hours
SO2        37    36    37    Over the last 24 hours
CO         9     4     16    Over the last 8 hours
OZONE      28    22    29    Over the last 8 hours

4. Continuous Monitoring

import matplotlib.pyplot as plt

# Analyze and plot time series for each pollutant in last_hours
# Collect all pollutants' time series
pollutant_names = []
all_values = []
all_dates = None

for pollutant in aqi_data["last_hours"]:
    name = pollutant["name"]
    data = pollutant["data"]
    if not data:
        continue

    dates = [entry["date"] for entry in data]
    values = [entry["val"] for entry in data]

    # Convert dates to datetime
    dates_dt = pd.to_datetime(dates)
    pollutant_names.append(name)
    all_values.append(values)
    if all_dates is None:
        all_dates = dates_dt

# Format x-axis labels as "HourAM/PM, DD/MM/YY"
x_labels = [
    dt.strftime("%I%p, %d/%m/%y").replace("AM", "AM").replace("PM", "PM")
    for dt in all_dates
]

plt.figure(figsize=(14, 6))
for i, values in enumerate(all_values):
    plt.plot(all_dates, values, marker="o", label=pollutant_names[i])

plt.title(f"Pollutant Levels - Last {len(all_dates)} Hours")
plt.xlabel("Time")
plt.ylabel("Value")
plt.xticks(all_dates, x_labels, rotation=45)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.legend()
plt.show()

# Show min, max, mean for each pollutant
for i, values in enumerate(all_values):
    print(f"\n{pollutant_names[i]} Analysis:")
    print(f"  Min: {min(values)}")
    print(f"  Max: {max(values)}")
    print(f"  Mean: {sum(values)/len(values):.2f}")
C:\Users\mahes\AppData\Local\Temp\ipykernel_22044\2686430132.py:19: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  dates_dt = pd.to_datetime(dates)
C:\Users\mahes\AppData\Local\Temp\ipykernel_22044\2686430132.py:19: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  dates_dt = pd.to_datetime(dates)
C:\Users\mahes\AppData\Local\Temp\ipykernel_22044\2686430132.py:19: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  dates_dt = pd.to_datetime(dates)
C:\Users\mahes\AppData\Local\Temp\ipykernel_22044\2686430132.py:19: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  dates_dt = pd.to_datetime(dates)
C:\Users\mahes\AppData\Local\Temp\ipykernel_22044\2686430132.py:19: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  dates_dt = pd.to_datetime(dates)
C:\Users\mahes\AppData\Local\Temp\ipykernel_22044\2686430132.py:19: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  dates_dt = pd.to_datetime(dates)
C:\Users\mahes\AppData\Local\Temp\ipykernel_22044\2686430132.py:19: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  dates_dt = pd.to_datetime(dates)
../_images/8679bba63ad7ac751cc3193881e1c169efe0b30085d772d8b5c132089a606a04.png
PM2.5 Analysis:
  Min: 51
  Max: 83
  Mean: 62.08

PM10 Analysis:
  Min: 60
  Max: 106
  Mean: 76.75

NO2 Analysis:
  Min: 4
  Max: 7
  Mean: 4.75

NH3 Analysis:
  Min: 1
  Max: 2
  Mean: 1.21

SO2 Analysis:
  Min: 36
  Max: 37
  Mean: 36.71

CO Analysis:
  Min: 4
  Max: 16
  Mean: 8.21

OZONE Analysis:
  Min: 22
  Max: 29
  Mean: 27.96

5. Set Up Alerts

def check_air_quality_alert(aqi_value):
    """
    Check AQI and return alert level.

    AQI Categories:
    - 0-50: Good
    - 51-100: Satisfactory
    - 101-200: Moderate
    - 201-300: Poor
    - 301-400: Very Poor
    - 401+: Severe
    """
    if aqi_value <= 50:
        return "✅ Good", "Air quality is good. Enjoy outdoor activities!"
    elif aqi_value <= 100:
        return "🟢 Satisfactory", "Air quality is acceptable."
    elif aqi_value <= 200:
        return (
            "🟡 Moderate",
            "Sensitive individuals should limit prolonged outdoor exposure.",
        )
    elif aqi_value <= 300:
        return (
            "🟠 Poor",
            "Everyone may begin to experience health effects. Reduce outdoor activities.",
        )
    elif aqi_value <= 400:
        return "🔴 Very Poor", "Health alert! Everyone should avoid outdoor activities."
    else:
        return (
            "🆘 Severe",
            "Health emergency! Stay indoors and avoid any outdoor exposure.",
        )


# Check current AQI
if aqi_data and "aqi" in aqi_data:
    print(aqi_data["aqi"])
    current_aqi = aqi_data["aqi"]
    alert_level, message = check_air_quality_alert(current_aqi["value"])

    print(f"\nCurrent AQI: {current_aqi}")
    print(f"Alert Level: {alert_level}")
    print(f"Recommendation: {message}")
{'param': 'PM10', 'value': 77, 'remark': 'Satisfactory', 'color': '#009933'}

Current AQI: {'param': 'PM10', 'value': 77, 'remark': 'Satisfactory', 'color': '#009933'}
Alert Level: 🟢 Satisfactory
Recommendation: Air quality is acceptable.

6. Get AQI for Specific Coordinates

# Example: Get AQI for Mumbai coordinates
mumbai_lat = 19.0760
mumbai_lon = 72.8777

coords = (mumbai_lat, mumbai_lon)

# Find nearest station to these coordinates
mumbai_station = client.get_nearest_station(coords=coords)
print("Nearest station to Mumbai:")
print(json.dumps(mumbai_station, indent=2))

# Get AQI data for that location
mumbai_aqi = client.get_live_aqi_data(coords=coords)
mumbai_aqi.pop("last_hours", None)  # Remove last_hours for brevity
print("\nCurrent AQI in Mumbai:")
print(json.dumps(mumbai_aqi, indent=2))
Nearest station to Mumbai:
[
  "site_5104",
  "Kurla, Mumbai - MPCB"
]

Current AQI in Mumbai:
{
  "title": "Kurla, Mumbai - MPCB",
  "nOfCom": 100,
  "down": "false",
  "downmessage": "",
  "date": "Wednesday, 08 Oct 2025 12:00 PM",
  "temp": "",
  "aqi": {
    "param": "PM10",
    "value": 77,
    "remark": "Satisfactory",
    "color": "#009933"
  },
  "metrics": [
    {
      "name": "PM2.5",
      "avg": 62,
      "avgDesc": "Over the last 24 hours",
      "min": 51,
      "max": 83,
      "pollutantName": "PM2.5"
    },
    {
      "name": "PM10",
      "avg": 77,
      "avgDesc": "Over the last 24 hours",
      "min": 60,
      "max": 106,
      "pollutantName": "PM10"
    },
    {
      "name": "NO2",
      "avg": 5,
      "avgDesc": "Over the last 24 hours",
      "min": 4,
      "max": 7,
      "pollutantName": "NO2"
    },
    {
      "name": "NH3",
      "avg": 1,
      "avgDesc": "Over the last 24 hours",
      "min": 1,
      "max": 2,
      "pollutantName": "NH3"
    },
    {
      "name": "SO2",
      "avg": 37,
      "avgDesc": "Over the last 24 hours",
      "min": 36,
      "max": 37,
      "pollutantName": "SO2"
    },
    {
      "name": "CO",
      "avg": 9,
      "avgDesc": "Over the last 8 hours",
      "min": 4,
      "max": 16,
      "pollutantName": "CO"
    },
    {
      "name": "OZONE",
      "avg": 28,
      "avgDesc": "Over the last 8 hours",
      "min": 22,
      "max": 29,
      "pollutantName": "OZONE"
    }
  ]
}