================================================================================
  Experiment 10: CoAP Environmental Monitoring & Cloud Storage (Open-Meteo)
================================================================================

FULL QUESTION:
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.

SHORT QUESTION:
CoAP server retrieving Open-Meteo weather & air quality on-demand with Streamlit client dashboard and cloud persistence.

================================================================================
README & SETUP INSTRUCTIONS:
================================================================================
# Experiment 10: CoAP Environmental Monitoring & Cloud Storage

## Objective
Retrieve on-demand weather and air-quality data from Open-Meteo REST APIs via a CoAP server, display telemetry on a client dashboard using CoAP GET requests, and persist records in a Cloud DB.

## Requirements
```bash
pip install aiocoap streamlit requests
```

## How to Run
1. Start CoAP Server:
   ```bash
   python server.py
   ```
2. Start Dashboard:
   ```bash
   streamlit run app.py
   ```

================================================================================
SOURCE CODE FILES:
================================================================================

1. server.py
--------------------------------------------------------------------------------
import asyncio
import json
import requests
from aiocoap import Context, Message, resource

WEATHER_URL = "https://api.open-meteo.com/v1/forecast?latitude=10.0159&longitude=76.3419&current=temperature_2m,relative_humidity_2m,weather_code"
AIR_QUALITY_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 WeatherResource(resource.Resource):
    async def render_get(self, request):
        try:
            res = requests.get(WEATHER_URL, timeout=10).json()["current"]
            result = {
                "temperature": res["temperature_2m"],
                "humidity": res["relative_humidity_2m"],
                "weather_code": res["weather_code"]
            }
            return Message(payload=json.dumps(result).encode("utf-8"))
        except Exception as e:
            return Message(payload=json.dumps({"error": str(e)}).encode("utf-8"))

class AirQualityResource(resource.Resource):
    async def render_get(self, request):
        try:
            res = requests.get(AIR_QUALITY_URL, timeout=10).json()["current"]
            result = {
                "pm10": res["pm10"],
                "pm2_5": res["pm2_5"],
                "carbon_monoxide": res["carbon_monoxide"],
                "nitrogen_dioxide": res["nitrogen_dioxide"],
                "ozone": res["ozone"],
                "european_aqi": res["european_aqi"]
            }
            return Message(payload=json.dumps(result).encode("utf-8"))
        except Exception as e:
            return Message(payload=json.dumps({"error": str(e)}).encode("utf-8"))

async def main():
    root = resource.Site()
    root.add_resource(["weather"], WeatherResource())
    root.add_resource(["airquality"], AirQualityResource())

    await Context.create_server_context(root, bind=("127.0.0.1", 5683))
    print("CoAP Server Active on port 5683")
    print("Endpoints: coap://127.0.0.1:5683/weather | coap://127.0.0.1:5683/airquality")
    await asyncio.get_running_loop().create_future()

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

2. app.py
--------------------------------------------------------------------------------
import streamlit as st
import asyncio
import json
import requests
from aiocoap import Context, Message, GET

SUPABASE_URL = "https://YOUR_PROJECT_ID.supabase.co/rest/v1"
SUPABASE_KEY = "YOUR_ANON_PUBLIC_KEY"

headers = {
    "apikey": SUPABASE_KEY,
    "Authorization": f"Bearer {SUPABASE_KEY}",
    "Content-Type": "application/json",
    "Prefer": "return=minimal"
}

def save_to_cloud(table, payload):
    try:
        requests.post(f"{SUPABASE_URL}/{table}", headers=headers, json=payload, timeout=5)
    except Exception:
        pass

async def fetch_coap(uri):
    protocol = await Context.create_client_context()
    try:
        request = Message(code=GET, uri=uri)
        response = await protocol.request(request).response
        return response.payload.decode("utf-8")
    finally:
        await protocol.shutdown()

def run_coap(uri):
    loop = asyncio.new_event_loop()
    try:
        return json.loads(loop.run_until_complete(fetch_coap(uri)))
    finally:
        loop.close()

st.title("CoAP Environmental Monitoring System")

st.subheader("Weather Data")
if st.button("Fetch Weather via CoAP"):
    data = run_coap("coap://127.0.0.1:5683/weather")
    if "error" not in data:
        st.write(f"Temperature: {data.get('temperature')} C")
        st.write(f"Humidity: {data.get('humidity')} %")
        st.write(f"Weather Code: {data.get('weather_code')}")
        save_to_cloud("weather_logs", data)
        st.success("Retrieved and logged to Cloud DB")
    else:
        st.error(data["error"])

st.subheader("Air Quality Data")
if st.button("Fetch Air Quality via CoAP"):
    data = run_coap("coap://127.0.0.1:5683/airquality")
    if "error" not in data:
        st.write(f"PM2.5: {data.get('pm2_5')}")
        st.write(f"PM10: {data.get('pm10')}")
        st.write(f"AQI: {data.get('european_aqi')}")
        save_to_cloud("air_quality_logs", data)
        st.success("Retrieved and logged to Cloud DB")
    else:
        st.error(data["error"])
--------------------------------------------------------------------------------
