Local air-quality & wildfire-smoke alert app — built for low-bandwidth, mobile-first contexts across Kenya and beyond.
AirWatch shows the current AQI, health advice by vulnerable group, wildfire-smoke banners, and nearby monitoring stations. Citizens can report haze/smoke conditions in their own neighborhood so the whole community stays informed.
Relevant in 2026 — escalating wildfires, smog episodes, and PM2.5 health crises make hyper-local, offline-friendly air-quality awareness a daily need.
- Dashboard — large color-coded AQI number + category (Good → Hazardous), dominant pollutant (PM2.5 / PM10 / O₃), and headline health advice.
- Wildfire-smoke banner — prominent red alert with guidance (close windows, wear N95, limit outdoor activity) when a station detects smoke.
- Health advice by group — tailored guidance for children, elderly, asthma/respiratory, outdoor workers, and the general public.
- Nearby stations — haversine-sorted list with distance, current AQI, smoke flag, and last-updated time. "Use this station" sets it as the selected location.
- Citizen reports — anyone can submit observed AQI, visibility (hazy/clear/severe), and notes.
- Stats — total stations, average AQI, active smoke alerts.
- Geolocation — uses the browser's location to find the nearest monitoring stations.
- Accessible palette + mobile-first UI with the US-EPA AQI color scale.
cd backend
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000Or:
cd backend && ./run.shThe API will be available at http://localhost:8000 (interactive docs at /docs).
cd frontend
npm install
npm run devOpen http://localhost:5175. The app calls the backend at VITE_API (defaults to http://localhost:8000).
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Service health check |
| GET | /stations |
All stations with latest AQI, category, smoke flag, last updated |
| GET | /readings?station_id= |
Readings for a station (newest first) |
| GET | /nearest?lat=&lng=&radius_km= |
Stations sorted by distance with distance_km |
| GET | /alerts |
Stations/readings where aqi >= 150 OR wildfire_smoke is true |
| GET | /health-advice?group=&aqi= |
Advice text for a group at a given AQI |
| POST | /readings |
Create a reading (station_id, or name+lat+lng; aqi, pm25, pm10, o3, wildfire_smoke) |
| POST | /reports |
Create a citizen report (location_name, lat, lng, observed_aqi, visibility, notes) |
| GET | /reports?limit=50 |
List recent citizen reports |
| GET | /stats |
{ stations, avg_aqi, smoke_alerts } |
Example:
curl "http://localhost:8000/nearest?lat=-1.2921&lng=36.8219&radius_km=500"
curl "http://localhost:8000/health-advice?group=children&aqi=180"See .env.example. Key variables:
DB_PATH— SQLite database file path (default./airwatch.db).VITE_API— frontend backend URL override.PORT— API port (default8000).
Build and run the self-contained image (builds the frontend, serves it alongside the API):
docker build -t airwatch .
docker run -p 8000:8000 -e DB_PATH=/data/airwatch.db airwatchOr with compose:
docker-compose up --buildThen open http://localhost:8000.
- Backend: Python 3, FastAPI, Uvicorn, stdlib
sqlite3+json(no ORM), Pydantic v2. - Frontend: React 18, Vite, TypeScript, Tailwind CSS, lucide-react.
- Deploy: Multi-stage Docker, single container serving API + static SPA.
MIT © Antorm. Built by antorm1.