10.Develop a CoAP-based Environmental Monitoring System that retrieves weather and air quality data from the Open-Meteo API on demand, displays the data on a client dashboard using CoAP GET requests, and stores the retrieved data in Cloud DB.

Weather API
https://api.open-meteo.com/v1/forecast?latitude=10.0159&longitude=76.3419&current=temperature_
2m,relative_humidity_2m,weather_code

Air Quality API
https://air-quality-api.open-meteo.com/v1/air-quality?latitude=10.0159&longitude=76.3419&current=pm10,pm2_5,carbon_
monoxide,nitrogen_
dioxide,ozone,european_aqi


pip install aiocoap flask requests supabase python-dotenv


supabase:

CREATE TABLE environmental_readings (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    temperature FLOAT,
    humidity FLOAT,
    weather_code INTEGER,
    pm10 FLOAT,
    pm2_5 FLOAT,
    carbon_monoxide FLOAT,
    nitrogen_dioxide FLOAT,
    ozone FLOAT,
    european_aqi FLOAT,
    created_at TIMESTAMPTZ DEFAULT NOW()
);


coap_environmental_monitor/
│
├── server.py
├── client.py
├── app.py
├── .env
│
└── templates/
    └── index.html


.env:

SUPABASE_URL=https://YOUR_PROJECT_ID.supabase.co
SUPABASE_KEY=YOUR_SERVICE_ROLE_KEY

server.py:


import asyncio
import json
import requests
import os

from aiocoap import resource, Message, Context
from supabase import create_client
from dotenv import load_dotenv

load_dotenv()

supabase = create_client(
    os.getenv("SUPABASE_URL"),
    os.getenv("SUPABASE_KEY")
)

WEATHER_URL = (
    "https://api.open-meteo.com/v1/forecast"
    "?latitude=10.0159&longitude=76.3419"
    "&current=temperature_2m,relative_humidity_2m,weather_code"
)

AIR_URL = (
    "https://air-quality-api.open-meteo.com/v1/air-quality"
    "?latitude=10.0159&longitude=76.3419"
    "&current=pm10,pm2_5,carbon_monoxide,"
    "nitrogen_dioxide,ozone,european_aqi"
)


class EnvironmentResource(resource.Resource):

    async def render_get(self, request):

        print("Fetching environmental data...")

        try:

            weather = requests.get(
                WEATHER_URL,
                timeout=15
            ).json()

            air = requests.get(
                AIR_URL,
                timeout=15
            ).json()

            current_weather = weather["current"]
            current_air = air["current"]

            data = {
                "temperature": current_weather["temperature_2m"],
                "humidity": current_weather["relative_humidity_2m"],
                "weather_code": current_weather["weather_code"],
                "pm10": current_air["pm10"],
                "pm2_5": current_air["pm2_5"],
                "carbon_monoxide": current_air["carbon_monoxide"],
                "nitrogen_dioxide": current_air["nitrogen_dioxide"],
                "ozone": current_air["ozone"],
                "european_aqi": current_air["european_aqi"]
            }

            # Store data in Supabase
            supabase.table(
                "environmental_readings"
            ).insert(data).execute()

            print("Data stored in Supabase")
            print(data)

            return Message(
                payload=json.dumps(data).encode("utf-8")
            )

        except Exception as e:

            print("Error:", e)

            return Message(
                code=128,
                payload=str(e).encode("utf-8")
            )


async def main():

    site = resource.Site()

    site.add_resource(
        ["environment"],
        EnvironmentResource()
    )

    await Context.create_server_context(
        site,
        bind=("127.0.0.1", 5683)
    )

    print("Environmental CoAP Server Started")
    print("Resource: /environment")

    await asyncio.get_running_loop().create_future()


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("Server stopped")


client.py:


import asyncio
import json

from aiocoap import Context, Message, GET


async def main():

    protocol = await Context.create_client_context()

    request = Message(
        code=GET,
        uri="coap://127.0.0.1:5683/environment"
    )

    print("Requesting Environmental Data...")

    response = await protocol.request(request).response

    data = json.loads(response.payload.decode())

    print("\nWeather Information")

    print("Temperature:", data["temperature"], "°C")
    print("Humidity:", data["humidity"], "%")
    print("Weather Code:", data["weather_code"])

    print("\nAir Quality Information")

    print("PM10:", data["pm10"])
    print("PM2.5:", data["pm2_5"])
    print("Carbon Monoxide:", data["carbon_monoxide"])
    print("Nitrogen Dioxide:", data["nitrogen_dioxide"])
    print("Ozone:", data["ozone"])
    print("European AQI:", data["european_aqi"])

    await protocol.shutdown()


if __name__ == "__main__":
    asyncio.run(main())


app.py:


from flask import Flask, jsonify, render_template
from supabase import create_client
from dotenv import load_dotenv
import os

load_dotenv()

app = Flask(__name__)

supabase = create_client(
    os.getenv("SUPABASE_URL"),
    os.getenv("SUPABASE_KEY")
)


@app.route("/")
def dashboard():
    return render_template("index.html")


@app.route("/api/readings")
def readings():

    result = (
        supabase.table("environmental_readings")
        .select("*")
        .order("created_at", desc=True)
        .limit(20)
        .execute()
    )

    return jsonify(result.data)


if __name__ == "__main__":
    app.run(debug=True)


templates/index.html:


<!DOCTYPE html>
<html>
<head>
    <title>Environmental Monitoring Dashboard</title>

    <meta name="viewport" content="width=device-width, initial-scale=1">

    <style>
        body {
            font-family: Arial;
            background: #eef2f7;
            text-align: center;
            padding: 25px;
        }

        h1 {
            color: #1e293b;
        }

        .grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
            gap: 15px;
            max-width: 1000px;
            margin: 30px auto;
        }

        .card {
            background: white;
            padding: 20px;
            border-radius: 12px;
            box-shadow: 0 3px 10px #ccc;
        }

        .value {
            font-size: 28px;
            color: #2563eb;
            font-weight: bold;
        }

        button {
            background: #2563eb;
            color: white;
            padding: 12px 25px;
            border: none;
            border-radius: 6px;
            cursor: pointer;
        }

        table {
            margin: 30px auto;
            border-collapse: collapse;
            background: white;
            width: 95%;
        }

        th, td {
            padding: 10px;
            border: 1px solid #ddd;
        }

        th {
            background: #2563eb;
            color: white;
        }
    </style>
</head>

<body>

    <h1>Environmental Monitoring Dashboard</h1>

    <button onclick="loadData()">Refresh Data</button>

    <div class="grid">

        <div class="card">
            <h3>Temperature</h3>
            <div class="value" id="temperature">--</div>
        </div>

        <div class="card">
            <h3>Humidity</h3>
            <div class="value" id="humidity">--</div>
        </div>

        <div class="card">
            <h3>PM10</h3>
            <div class="value" id="pm10">--</div>
        </div>

        <div class="card">
            <h3>PM2.5</h3>
            <div class="value" id="pm25">--</div>
        </div>

        <div class="card">
            <h3>Carbon Monoxide</h3>
            <div class="value" id="co">--</div>
        </div>

        <div class="card">
            <h3>Nitrogen Dioxide</h3>
            <div class="value" id="no2">--</div>
        </div>

        <div class="card">
            <h3>Ozone</h3>
            <div class="value" id="ozone">--</div>
        </div>

        <div class="card">
            <h3>European AQI</h3>
            <div class="value" id="aqi">--</div>
        </div>

    </div>

    <h2>Historical Readings</h2>

    <table>
        <thead>
            <tr>
                <th>Temperature</th>
                <th>Humidity</th>
                <th>PM2.5</th>
                <th>AQI</th>
                <th>Date</th>
            </tr>
        </thead>

        <tbody id="history"></tbody>
    </table>

    <script>
        async function loadData() {

            try {

                const response = await fetch("/api/readings");
                const data = await response.json();

                if (data.length === 0) return;

                const latest = data[0];

                document.getElementById("temperature").innerText =
                    latest.temperature + " °C";

                document.getElementById("humidity").innerText =
                    latest.humidity + " %";

                document.getElementById("pm10").innerText =
                    latest.pm10;

                document.getElementById("pm25").innerText =
                    latest.pm2_5;

                document.getElementById("co").innerText =
                    latest.carbon_monoxide;

                document.getElementById("no2").innerText =
                    latest.nitrogen_dioxide;

                document.getElementById("ozone").innerText =
                    latest.ozone;

                document.getElementById("aqi").innerText =
                    latest.european_aqi;

                const history = document.getElementById("history");

                history.innerHTML = "";

                data.forEach(item => {

                    history.innerHTML += `
                        <tr>
                            <td>${item.temperature}</td>
                            <td>${item.humidity}</td>
                            <td>${item.pm2_5}</td>
                            <td>${item.european_aqi}</td>
                            <td>${new Date(item.created_at).toLocaleString()}</td>
                        </tr>
                    `;
                });

            }
            catch (error) {
                console.log(error);
            }
        }

        loadData();

        setInterval(loadData, 10000);
    </script>

</body>
</html>

