================================================================================
  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
--------------------------------------------------------------------------------
"""
================================================================================
IOT EXPERIMENT 10: CoAP Environmental Gateway for Open-Meteo Cloud
================================================================================
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 Description: CoAP server proxy querying live Open-Meteo meteorological and air quality APIs on demand and serving data over CoAP UDP protocol to clients.

"""
README / BEFORE RUNNING:
- Prerequisites: Python 3.x, requests (pip install requests). Active internet connection.
- How to Run: python server.py (start this first before launching app.py).
- Expected Behavior: Listens on UDP port 5683, queries Open-Meteo for Kochi coordinates (10.0159° N, 76.3419° E), and returns formatted air & weather data.
"""

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
--------------------------------------------------------------------------------
"""
================================================================================
IOT EXPERIMENT 10: Environmental Dashboard & Client (CoAP + Cloud DB)
================================================================================
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 Description: Flask web client making CoAP queries to the local gateway and displaying live environmental metrics, AQI levels, and meteorological data.

"""
README / BEFORE RUNNING:
- Prerequisites: Python 3.x, flask, requests (pip install flask requests).
- Setup: Ensure 'python server.py' is running in another terminal.
- How to Run: python app.py (open http://localhost:5000 in your browser).
- Expected Behavior: Fetches weather and air quality via CoAP, logs records to local cloud database cache, and renders a live environmental dashboard.
"""

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()
    asyncio.set_event_loop(loop)
    try:
        return json.loads(loop.run_until_complete(fetch_coap(uri)))
    finally:
        loop.close()

st.set_page_config(page_title="CoAP Environmental Monitor", layout="wide")
st.title("CoAP Environmental Monitoring System")

col1, col2 = st.columns(2)

with col1:
    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.metric("Temperature", f"{data['temperature']} °C")
            st.metric("Humidity", f"{data['humidity']} %")
            st.metric("Weather Code", data["weather_code"])
            save_to_cloud("weather_logs", data)
            st.success("Retrieved and logged to Cloud DB")
        else:
            st.error(data["error"])

with col2:
    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.metric("PM2.5", f"{data['pm2_5']} µg/m³")
            st.metric("PM10", f"{data['pm10']} µg/m³")
            st.metric("AQI", data["european_aqi"])
            save_to_cloud("air_quality_logs", data)
            st.success("Retrieved and logged to Cloud DB")
        else:
            st.error(data["error"])
--------------------------------------------------------------------------------
