================================================================================
  Question 4: CoAP Weather Monitoring & Decision Tree Classification [30 Marks]
================================================================================

FULL QUESTION:
CAE 2 IoT Practical Exam - Section B (Answer either Q3 or Q4):
Q4. CoAP-Based Weather Monitoring and Decision Tree Classification [30 Marks]
Develop a CoAP-based weather monitoring and classification system for Kochi.
Input API Link: https://api.open-meteo.com/v1/forecast?latitude=9.93&longitude=76.27&current=temperature_2m,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,surface_pressure
Historical Dataset API: https://archive-api.open-meteo.com/v1/archive?latitude=9.93&longitude=76.27&start_date=2023-01-01&end_date=2025-12-31&hourly=temperature_2m,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,surface_pressure,weather_code
Target weather_class: 0,1 -> Clear; 2,3 -> Cloudy; 51,53,55 -> Drizzle; 61,63,65 -> Rain.
The system should:
- Develop CoAP Server and CoAP Client. [6 Marks]
- Retrieve live weather data of Kochi via CoAP Server. [6 Marks]
- Transfer data to CoAP Client using CoAP GET request. [5 Marks]
- Develop dashboard to display Temperature, Humidity, Precipitation, Cloud Cover, Wind Speed, Surface Pressure. [5 Marks]
- Train Decision Tree model on historical weather dataset to classify current weather into Clear, Cloudy, Drizzle, or Rain. [5 Marks]
- Display predicted weather classification on dashboard. [3 Marks]
Flow: Open-Meteo Weather API -> CoAP Server -> CoAP Client -> Dashboard -> Decision Tree Classification.

SHORT QUESTION:
CoAP Weather Server & Client with Decision Tree Classification and Dashboard [30 Marks].

================================================================================
README & SETUP INSTRUCTIONS:
================================================================================
# Question 4: CoAP-Based Weather Monitoring and Decision Tree Classification

## Problem Statement [30 Marks]
Develop a CoAP-based weather monitoring and classification system for Kochi.

- **Input API Link**:
  `https://api.open-meteo.com/v1/forecast?latitude=9.93&longitude=76.27&current=temperature_2m,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,surface_pressure`
- **Weather Parameters**:
  Temperature, Relative Humidity, Precipitation, Cloud Cover, Wind Speed, Surface Pressure
- **Historical Dataset API**:
  `https://archive-api.open-meteo.com/v1/archive?latitude=9.93&longitude=76.27&start_date=2023-01-01&end_date=2025-12-31&hourly=temperature_2m,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,surface_pressure,weather_code`
- **Weather Class Mapping**:
  | weather_code | weather_class |
  | :--- | :--- |
  | `0, 1` | **Clear** |
  | `2, 3` | **Cloudy** |
  | `51, 53, 55` | **Drizzle** |
  | `61, 63, 65` | **Rain** |
- **Model**: Decision Tree Classifier
  - Input Features: `temperature_2m`, `relative_humidity_2m`, `precipitation`, `cloud_cover`, `wind_speed_10m`, `surface_pressure`
  - Target: `weather_class`

---

## Architectural Flow
```text
Open-Meteo Weather API ──> CoAP Server (UDP 5683) ──> CoAP Client ──> Dashboard ──> Decision Tree Classification
```

---

## Files in this Folder
- `server.py`: Asynchronous CoAP Server listening on port 5683 (`/weather`), retrieving live Kochi weather telemetry from Open-Meteo API.
- `client.py`: Standalone CLI CoAP Client that queries the CoAP server, trains the Decision Tree on historical observations, and displays predictions in the terminal.
- `app.py`: Interactive Streamlit dashboard and CoAP Client with visualizations, metric cards, and interactive manual parameter tuning.
- `historical_weather_kochi.csv`: 26,304 historical hourly weather observations for Kochi with mapped weather classes.

---

## How to Run

### Step 1: Install Dependencies
```bash
pip install pandas scikit-learn streamlit aiocoap requests matplotlib
```

### Step 2: Start the CoAP Server
Open a terminal in the `Q4` directory:
```bash
python server.py
```
*Expected Server Output:*
```text
============================================================
  Kochi CoAP Weather Server Active on Port 5683
============================================================
Endpoint: coap://127.0.0.1:5683/weather
Waiting for CoAP Client GET requests...
```

### Step 3: Run the CLI Client OR the Streamlit Dashboard

#### Option A: Run CLI Client
Open a second terminal in the `Q4` directory:
```bash
python client.py
```

#### Option B: Run Streamlit Dashboard
```bash
streamlit run app.py
```
Open your browser at `http://localhost:8501`. Click **"Fetch Live Weather via CoAP"** to initiate a CoAP GET transaction and view live predictions and model metrics!

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

1. server.py
--------------------------------------------------------------------------------
"""
================================================================================
CAE 2 – IOT PRACTICAL EXAMINATION
QUESTION 4: CoAP-Based Weather Monitoring and Decision Tree Classification
================================================================================
Develop a CoAP-based weather monitoring and classification system for Kochi.
Input API Link:
https://api.open-meteo.com/v1/forecast?latitude=9.93&longitude=76.27&current=temperature_2m,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,surface_pressure
The system should retrieve the following weather parameters:
Temperature, Relative Humidity, Precipitation, Cloud Cover, Wind Speed, Surface Pressure

Dataset:
The Decision Tree classification model should be trained using historical weather
data retrieved from the Open-Meteo Historical Weather API.
Historical Dataset API:
https://archive-api.open-meteo.com/v1/archive?latitude=9.93&longitude=76.27&start_date=2023-01-01&end_date=2025-12-31&hourly=temperature_2m,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,surface_pressure,weather_code

The system should:
1. Develop a CoAP Server and CoAP Client. [6 Marks]
2. On receiving a request from the CoAP Client, retrieve the live weather data
   of Kochi from the given Open-Meteo API through the CoAP Server. [6 Marks]
3. Transfer the retrieved weather data from the CoAP Server to the CoAP Client
   using a CoAP GET request. [5 Marks]
4. Develop a dashboard to display the retrieved weather parameters:
   Temperature, Relative Humidity, Precipitation, Cloud Cover, Wind Speed and
   Surface Pressure. [5 Marks]
5. Train and apply a Decision Tree classification model using the historical
   weather dataset to classify the current weather conditions into appropriate
   weather categories such as Clear, Cloudy, Drizzle or Rain. [5 Marks]
6. Display the predicted weather classification on the dashboard. [3 Marks]

The system should demonstrate the following flow:
Open-Meteo Weather API → CoAP Server → CoAP Client → Dashboard → Decision Tree Classification
================================================================================
"""

# Short Description: CoAP Server providing live Kochi weather telemetry (Temperature, Humidity, Precipitation, Cloud Cover, Wind Speed, Pressure) via CoAP GET on UDP port 5683.

"""
README / BEFORE RUNNING:
- Prerequisites: Python 3.8+ with aiocoap and requests installed (`pip install aiocoap requests`).
- Port / Protocol: Listens on UDP port 5683 at endpoint '/weather'.
- How to Run:
    1. Start this CoAP server first:
       python server.py
    2. Once running, execute client.py or app.py in another terminal.
- Expected Behavior:
    * Server binds to 127.0.0.1:5683 with resource URI 'coap://127.0.0.1:5683/weather'.
    * On receiving CoAP GET, requests live weather parameters from Open-Meteo API for Kochi.
    * Serves the payload containing temperature, humidity, precipitation, cloud cover, wind speed, and surface pressure.
"""

import asyncio
import json
import requests
from aiocoap import Context, Message, resource

LIVE_WEATHER_URL = (
    "https://api.open-meteo.com/v1/forecast?"
    "latitude=9.93&longitude=76.27&"
    "current=temperature_2m,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,surface_pressure"
)


class WeatherResource(resource.Resource):
    """CoAP Resource serving live Kochi weather parameters over CoAP GET."""

    def fetch_live_api(self):
        response = requests.get(LIVE_WEATHER_URL, timeout=10)
        response.raise_for_status()
        return response.json().get("current", {})

    async def render_get(self, request):
        print(f"\n[CoAP Server] Received GET request from {request.remote}")
        try:
            # Query live Open-Meteo API asynchronously
            current = await asyncio.to_thread(self.fetch_live_api)

            # Build standardized weather payload
            payload = {
                "status": "success",
                "city": "Kochi",
                "latitude": 9.93,
                "longitude": 76.27,
                "time": current.get("time"),
                "temperature_2m": current.get("temperature_2m"),
                "relative_humidity_2m": current.get("relative_humidity_2m"),
                "precipitation": current.get("precipitation"),
                "cloud_cover": current.get("cloud_cover"),
                "wind_speed_10m": current.get("wind_speed_10m"),
                "surface_pressure": current.get("surface_pressure"),
            }

            print("[CoAP Server] Serving live weather parameters:")
            print(f"  Temperature      : {payload['temperature_2m']} °C")
            print(f"  Relative Humidity: {payload['relative_humidity_2m']} %")
            print(f"  Precipitation    : {payload['precipitation']} mm")
            print(f"  Cloud Cover      : {payload['cloud_cover']} %")
            print(f"  Wind Speed       : {payload['wind_speed_10m']} km/h")
            print(f"  Surface Pressure : {payload['surface_pressure']} hPa")

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

        except Exception as e:
            error_msg = json.dumps({"status": "error", "error": str(e)}).encode("utf-8")
            print(f"[CoAP Server] Error fetching live weather API: {e}")
            return Message(payload=error_msg)


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

    await Context.create_server_context(root, bind=("127.0.0.1", 5683))
    print("=" * 60)
    print("  Kochi CoAP Weather Server Active on Port 5683")
    print("=" * 60)
    print("Endpoint: coap://127.0.0.1:5683/weather")
    print("Waiting for CoAP Client GET requests (Press Ctrl+C to stop)...")
    await asyncio.get_running_loop().create_future()


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("\n[CoAP Server] Stopped by user.")
--------------------------------------------------------------------------------

2. client.py
--------------------------------------------------------------------------------
"""
================================================================================
CAE 2 – IOT PRACTICAL EXAMINATION
QUESTION 4: CoAP-Based Weather Monitoring and Decision Tree Classification
================================================================================
Develop a CoAP-based weather monitoring and classification system for Kochi.
Input API Link:
https://api.open-meteo.com/v1/forecast?latitude=9.93&longitude=76.27&current=temperature_2m,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,surface_pressure
The system should retrieve the following weather parameters:
Temperature, Relative Humidity, Precipitation, Cloud Cover, Wind Speed, Surface Pressure

Dataset:
The Decision Tree classification model should be trained using historical weather
data retrieved from the Open-Meteo Historical Weather API.
Historical Dataset API:
https://archive-api.open-meteo.com/v1/archive?latitude=9.93&longitude=76.27&start_date=2023-01-01&end_date=2025-12-31&hourly=temperature_2m,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,surface_pressure,weather_code

The system should:
1. Develop a CoAP Server and CoAP Client. [6 Marks]
2. On receiving a request from the CoAP Client, retrieve the live weather data
   of Kochi from the given Open-Meteo API through the CoAP Server. [6 Marks]
3. Transfer the retrieved weather data from the CoAP Server to the CoAP Client
   using a CoAP GET request. [5 Marks]
4. Develop a dashboard to display the retrieved weather parameters:
   Temperature, Relative Humidity, Precipitation, Cloud Cover, Wind Speed and
   Surface Pressure. [5 Marks]
5. Train and apply a Decision Tree classification model using the historical
   weather dataset to classify the current weather conditions into appropriate
   weather categories such as Clear, Cloudy, Drizzle or Rain. [5 Marks]
6. Display the predicted weather classification on the dashboard. [3 Marks]

The system should demonstrate the following flow:
Open-Meteo Weather API → CoAP Server → CoAP Client → Dashboard → Decision Tree Classification
================================================================================
"""

# Short Description: CoAP CLI Client that retrieves live Kochi weather data from CoAP server, trains a Decision Tree on historical weather data, and outputs the predicted weather class.

"""
README / BEFORE RUNNING:
- Prerequisites: Python 3.8+ with pandas, scikit-learn, aiocoap, requests (`pip install pandas scikit-learn aiocoap requests`).
- Setup:
    1. Ensure `historical_weather_kochi.csv` is present (or internet access is available to download automatically).
    2. Start the CoAP server in another terminal (`python server.py`).
- How to Run:
    python client.py
- Expected Behavior:
    * Sends a CoAP GET request to coap://127.0.0.1:5683/weather.
    * Receives live Kochi parameters: Temperature, Relative Humidity, Precipitation, Cloud Cover, Wind Speed, Surface Pressure.
    * Trains Decision Tree Classifier mapping weather_code to Clear, Cloudy, Drizzle, Rain.
    * Outputs live parameters and predicted weather classification to the console.
"""

import asyncio
import json
import os
import requests
import pandas as pd
from aiocoap import Context, Message, GET
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score, classification_report

FEATURES = [
    "temperature_2m",
    "relative_humidity_2m",
    "precipitation",
    "cloud_cover",
    "wind_speed_10m",
    "surface_pressure"
]
TARGET = "weather_class"
SERVER_URI = "coap://127.0.0.1:5683/weather"
HISTORICAL_CSV = "historical_weather_kochi.csv"
HISTORICAL_API = (
    "https://archive-api.open-meteo.com/v1/archive?"
    "latitude=9.93&longitude=76.27&start_date=2023-01-01&end_date=2025-12-31&"
    "hourly=temperature_2m,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,surface_pressure,weather_code"
)

WEATHER_CODE_MAPPING = {
    0: "Clear", 1: "Clear",
    2: "Cloudy", 3: "Cloudy",
    51: "Drizzle", 53: "Drizzle", 55: "Drizzle",
    61: "Rain", 63: "Rain", 65: "Rain"
}


async def fetch_weather_coap(uri=SERVER_URI):
    """Retrieve live weather data from CoAP Server via CoAP GET."""
    protocol = await Context.create_client_context()
    try:
        request = Message(code=GET, uri=uri)
        response = await protocol.request(request).response
        return json.loads(response.payload.decode("utf-8"))
    finally:
        await protocol.shutdown()


def load_or_fetch_dataset():
    """Load local historical CSV or fetch from Open-Meteo Archive API if missing."""
    if os.path.exists(HISTORICAL_CSV):
        print(f"[1] Loading historical weather data from '{HISTORICAL_CSV}'...")
        df = pd.read_csv(HISTORICAL_CSV)
    else:
        print("[1] Local CSV not found. Fetching historical weather dataset from Open-Meteo Archive API...")
        r = requests.get(HISTORICAL_API, timeout=30)
        r.raise_for_status()
        hourly = r.json()["hourly"]
        df = pd.DataFrame(hourly)
        df["weather_class"] = df["weather_code"].map(WEATHER_CODE_MAPPING)
        df.to_csv(HISTORICAL_CSV, index=False)
        print(f"    Saved {len(df)} records to '{HISTORICAL_CSV}'.")

    if "weather_class" not in df.columns:
        df["weather_class"] = df["weather_code"].map(WEATHER_CODE_MAPPING)

    return df


def train_decision_tree():
    """Train Decision Tree Classifier on historical weather data."""
    df = load_or_fetch_dataset()
    data = df[FEATURES + [TARGET]].dropna()
    print(f"    Dataset loaded with {len(data):,} valid historical observations.")

    X = data[FEATURES]
    y = data[TARGET]

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.20, random_state=42, stratify=y
    )

    print("[2] Training Decision Tree Classifier...")
    model = DecisionTreeClassifier(max_depth=10, random_state=42)
    model.fit(X_train, y_train)

    y_pred = model.predict(X_test)
    acc = accuracy_score(y_test, y_pred)
    print(f"    Model Accuracy: {acc * 100:.2f}%\n")
    print("Classification Summary:")
    print(classification_report(y_test, y_pred))

    return model, acc


def main():
    print("=" * 70)
    print("  Kochi CoAP Weather Monitor & Decision Tree Classification")
    print("=" * 70)

    # 1. Fetch live telemetry via CoAP
    print(f"\n[Step 1] Connecting to CoAP Server at {SERVER_URI}...")
    try:
        coap_data = asyncio.run(fetch_weather_coap())
        if coap_data.get("status") == "error":
            print(f"[Error from Server]: {coap_data.get('error')}")
            return
    except Exception as e:
        print(f"[ERROR] Could not connect to CoAP Server on 127.0.0.1:5683: {e}")
        print("Please ensure 'python server.py' is running in another terminal.")
        return

    print("\n[Step 2] Live Weather Telemetry Received via CoAP:")
    print(f"  Location          : {coap_data.get('city')} (Lat: {coap_data.get('latitude')}, Lon: {coap_data.get('longitude')})")
    print(f"  Timestamp         : {coap_data.get('time')}")
    print(f"  Temperature       : {coap_data.get('temperature_2m')} °C")
    print(f"  Relative Humidity : {coap_data.get('relative_humidity_2m')} %")
    print(f"  Precipitation     : {coap_data.get('precipitation')} mm")
    print(f"  Cloud Cover       : {coap_data.get('cloud_cover')} %")
    print(f"  Wind Speed        : {coap_data.get('wind_speed_10m')} km/h")
    print(f"  Surface Pressure  : {coap_data.get('surface_pressure')} hPa")

    # 2. Train Decision Tree model
    model, _ = train_decision_tree()

    # 3. Classify live weather condition
    input_sample = pd.DataFrame([{
        "temperature_2m": float(coap_data["temperature_2m"]),
        "relative_humidity_2m": float(coap_data["relative_humidity_2m"]),
        "precipitation": float(coap_data["precipitation"]),
        "cloud_cover": float(coap_data["cloud_cover"]),
        "wind_speed_10m": float(coap_data["wind_speed_10m"]),
        "surface_pressure": float(coap_data["surface_pressure"])
    }])

    predicted_class = model.predict(input_sample)[0]
    probabilities = model.predict_proba(input_sample)[0]

    print("=" * 70)
    print(f"  PREDICTED WEATHER CLASS: >>> {predicted_class.upper()} <<<")
    print("=" * 70)
    print("Class Probabilities:")
    for cls, prob in zip(model.classes_, probabilities):
        print(f"  {cls:10s}: {prob * 100:6.2f}%")


if __name__ == "__main__":
    main()
--------------------------------------------------------------------------------

3. app.py
--------------------------------------------------------------------------------
"""
================================================================================
CAE 2 – IOT PRACTICAL EXAMINATION
QUESTION 4: CoAP-Based Weather Monitoring and Decision Tree Classification
================================================================================
Develop a CoAP-based weather monitoring and classification system for Kochi.
Input API Link:
https://api.open-meteo.com/v1/forecast?latitude=9.93&longitude=76.27&current=temperature_2m,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,surface_pressure
The system should retrieve the following weather parameters:
Temperature, Relative Humidity, Precipitation, Cloud Cover, Wind Speed, Surface Pressure

Dataset:
The Decision Tree classification model should be trained using historical weather
data retrieved from the Open-Meteo Historical Weather API.
Historical Dataset API:
https://archive-api.open-meteo.com/v1/archive?latitude=9.93&longitude=76.27&start_date=2023-01-01&end_date=2025-12-31&hourly=temperature_2m,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,surface_pressure,weather_code

The system should:
1. Develop a CoAP Server and CoAP Client. [6 Marks]
2. On receiving a request from the CoAP Client, retrieve the live weather data
   of Kochi from the given Open-Meteo API through the CoAP Server. [6 Marks]
3. Transfer the retrieved weather data from the CoAP Server to the CoAP Client
   using a CoAP GET request. [5 Marks]
4. Develop a dashboard to display the retrieved weather parameters:
   Temperature, Relative Humidity, Precipitation, Cloud Cover, Wind Speed and
   Surface Pressure. [5 Marks]
5. Train and apply a Decision Tree classification model using the historical
   weather dataset to classify the current weather conditions into appropriate
   weather categories such as Clear, Cloudy, Drizzle or Rain. [5 Marks]
6. Display the predicted weather classification on the dashboard. [3 Marks]

The system should demonstrate the following flow:
Open-Meteo Weather API → CoAP Server → CoAP Client → Dashboard → Decision Tree Classification
================================================================================
"""

# Short Description: Streamlit Dashboard acting as a CoAP Client to retrieve live Kochi weather parameters from CoAP Server, display them, and classify weather into Clear, Cloudy, Drizzle, or Rain using a trained Decision Tree model.

"""
README / BEFORE RUNNING:
- Prerequisites: Python 3.8+ with streamlit, pandas, scikit-learn, aiocoap, matplotlib, requests (`pip install streamlit pandas scikit-learn aiocoap matplotlib requests`).
- Setup:
    1. Ensure `historical_weather_kochi.csv` is present in the current folder.
    2. Start the CoAP server in a separate terminal:
       python server.py
    3. Launch this Streamlit dashboard:
       streamlit run app.py
- Expected Behavior:
    * Loads and trains Decision Tree Classifier on historical weather observations.
    * Clicking 'Fetch Live Weather via CoAP' retrieves live parameters from CoAP server.
    * Displays 6 weather parameters: Temperature, Humidity, Precipitation, Cloud Cover, Wind Speed, Pressure.
    * Classifies conditions into Clear, Cloudy, Drizzle, or Rain with confidence probabilities.
"""

import asyncio
import json
import os
import requests
import streamlit as st
import pandas as pd
import matplotlib.pyplot as plt
from aiocoap import Context, Message, GET
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score, confusion_matrix

st.set_page_config(
    page_title="Kochi Weather CoAP & Decision Tree",
    page_icon="⛅",
    layout="wide"
)

FEATURES = [
    "temperature_2m",
    "relative_humidity_2m",
    "precipitation",
    "cloud_cover",
    "wind_speed_10m",
    "surface_pressure"
]
TARGET = "weather_class"
COAP_URI = "coap://127.0.0.1:5683/weather"
HISTORICAL_CSV = "historical_weather_kochi.csv"
HISTORICAL_API = (
    "https://archive-api.open-meteo.com/v1/archive?"
    "latitude=9.93&longitude=76.27&start_date=2023-01-01&end_date=2025-12-31&"
    "hourly=temperature_2m,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,surface_pressure,weather_code"
)

WEATHER_CODE_MAPPING = {
    0: "Clear", 1: "Clear",
    2: "Cloudy", 3: "Cloudy",
    51: "Drizzle", 53: "Drizzle", 55: "Drizzle",
    61: "Rain", 63: "Rain", 65: "Rain"
}

WEATHER_STYLE = {
    "Clear":   {"icon": "☀️", "color": "#f39c12", "desc": "Clear Skies / Sunny"},
    "Cloudy":  {"icon": "☁️", "color": "#7f8c8d", "desc": "Overcast / Partly Cloudy"},
    "Drizzle": {"icon": "🌦️", "color": "#3498db", "desc": "Light Drizzle / Mist"},
    "Rain":    {"icon": "🌧️", "color": "#2980b9", "desc": "Moderate to Heavy Rain"}
}


# ==============================================================================
# Model Training & Caching
# ==============================================================================
@st.cache_resource
def load_and_train_dt():
    if os.path.exists(HISTORICAL_CSV):
        df = pd.read_csv(HISTORICAL_CSV)
    else:
        r = requests.get(HISTORICAL_API, timeout=30)
        r.raise_for_status()
        hourly = r.json()["hourly"]
        df = pd.DataFrame(hourly)
        df["weather_class"] = df["weather_code"].map(WEATHER_CODE_MAPPING)
        df.to_csv(HISTORICAL_CSV, index=False)

    if "weather_class" not in df.columns:
        df["weather_class"] = df["weather_code"].map(WEATHER_CODE_MAPPING)

    data = df[FEATURES + [TARGET]].dropna()

    X = data[FEATURES]
    y = data[TARGET]

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.20, random_state=42, stratify=y
    )

    model = DecisionTreeClassifier(max_depth=10, random_state=42)
    model.fit(X_train, y_train)

    y_pred = model.predict(X_test)
    acc = accuracy_score(y_test, y_pred)
    cm = confusion_matrix(y_test, y_pred, labels=model.classes_)

    return model, acc, cm, model.classes_, len(data)


# ==============================================================================
# CoAP Client Function
# ==============================================================================
async def async_fetch_weather_coap(uri=COAP_URI):
    protocol = await Context.create_client_context()
    try:
        request = Message(code=GET, uri=uri)
        response = await protocol.request(request).response
        return json.loads(response.payload.decode("utf-8"))
    finally:
        await protocol.shutdown()


def get_live_weather_via_coap():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        return loop.run_until_complete(async_fetch_weather_coap())
    finally:
        loop.close()


# ==============================================================================
# UI Header
# ==============================================================================
st.title("⛅ Kochi Weather Monitoring & Decision Tree Classification")
st.markdown(
    "**Demonstration Flow:** `Open-Meteo Weather API` ➔ `CoAP Server` ➔ `CoAP Client` ➔ `Dashboard` ➔ `Decision Tree Classification`"
)

# Load Model
with st.spinner("Training Decision Tree Classifier on historical weather data..."):
    model, accuracy, cm, classes, total_samples = load_and_train_dt()

# Sidebar summary
st.sidebar.header("📊 Model Specifications")
st.sidebar.info(
    f"**Model:** Decision Tree Classifier\n\n"
    f"**Input Features (6):**\n"
    f"1. Temperature (2m)\n"
    f"2. Relative Humidity (2m)\n"
    f"3. Precipitation\n"
    f"4. Cloud Cover\n"
    f"5. Wind Speed (10m)\n"
    f"6. Surface Pressure\n\n"
    f"**Target:** weather_class (Clear, Cloudy, Drizzle, Rain)\n\n"
    f"**Historical Records:** {total_samples:,}\n\n"
    f"**Test Accuracy:** **{accuracy * 100:.2f}%**"
)
st.sidebar.markdown("---")
st.sidebar.markdown(
    "📡 **CoAP Server Endpoint:** `coap://127.0.0.1:5683/weather`"
)

# ==============================================================================
# CoAP Telemetry Acquisition Section
# ==============================================================================
st.subheader("1. CoAP Telemetry Transfer (Server ➔ Client)")

col_btn, _ = st.columns([1, 3])
with col_btn:
    fetch_clicked = st.button("📡 Fetch Live Weather via CoAP", type="primary")

if "weather_data" not in st.session_state:
    st.session_state["weather_data"] = None

if fetch_clicked:
    try:
        with st.spinner("Sending CoAP GET request to coap://127.0.0.1:5683/weather..."):
            res = get_live_weather_via_coap()
            if res.get("status") == "error":
                st.error(f"Server Error: {res.get('error')}")
            else:
                st.session_state["weather_data"] = res
                st.success("✅ CoAP 2.05 Content successfully received from CoAP Server!")
    except Exception as e:
        st.error(f"❌ CoAP Transmission Error: {e}")
        st.info("Tip: Ensure the CoAP Server is running in terminal: `python server.py`")

weather_data = st.session_state["weather_data"]

if weather_data and weather_data.get("status") == "success":
    st.caption(f"📍 Location: **{weather_data.get('city')}** | ⏱️ Timestamp: **{weather_data.get('time')}** | Lat: 9.93, Lon: 76.27")
    m1, m2, m3, m4, m5, m6 = st.columns(6)
    m1.metric("Temperature", f"{weather_data['temperature_2m']} °C")
    m2.metric("Relative Humidity", f"{weather_data['relative_humidity_2m']} %")
    m3.metric("Precipitation", f"{weather_data['precipitation']} mm")
    m4.metric("Cloud Cover", f"{weather_data['cloud_cover']} %")
    m5.metric("Wind Speed", f"{weather_data['wind_speed_10m']} km/h")
    m6.metric("Surface Pressure", f"{weather_data['surface_pressure']} hPa")

    def_temp = float(weather_data["temperature_2m"])
    def_hum = float(weather_data["relative_humidity_2m"])
    def_precip = float(weather_data["precipitation"])
    def_cloud = float(weather_data["cloud_cover"])
    def_wind = float(weather_data["wind_speed_10m"])
    def_press = float(weather_data["surface_pressure"])
else:
    st.warning("CoAP data not fetched yet. Click the button above to query CoAP Server, or test using the sliders below.")
    def_temp, def_hum, def_precip, def_cloud, def_wind, def_press = 29.5, 70.0, 0.0, 15.0, 12.0, 1010.5

st.divider()

# ==============================================================================
# Decision Tree Classification Section
# ==============================================================================
st.subheader("2. Decision Tree Classification & Prediction")

col_left, col_right = st.columns([1, 1])

with col_left:
    st.write("##### Weather Input Parameters")
    r1, r2 = st.columns(2)
    with r1:
        in_temp = st.number_input("Temperature (°C)", min_value=-10.0, max_value=60.0, value=def_temp, step=0.5)
        in_hum = st.number_input("Relative Humidity (%)", min_value=0.0, max_value=100.0, value=def_hum, step=1.0)
        in_precip = st.number_input("Precipitation (mm)", min_value=0.0, max_value=200.0, value=def_precip, step=0.1)
    with r2:
        in_cloud = st.number_input("Cloud Cover (%)", min_value=0.0, max_value=100.0, value=def_cloud, step=1.0)
        in_wind = st.number_input("Wind Speed (km/h)", min_value=0.0, max_value=150.0, value=def_wind, step=0.5)
        in_press = st.number_input("Surface Pressure (hPa)", min_value=900.0, max_value=1100.0, value=def_press, step=0.5)

    sample_df = pd.DataFrame([{
        "temperature_2m": in_temp,
        "relative_humidity_2m": in_hum,
        "precipitation": in_precip,
        "cloud_cover": in_cloud,
        "wind_speed_10m": in_wind,
        "surface_pressure": in_press
    }])

    pred_class = model.predict(sample_df)[0]
    proba = model.predict_proba(sample_df)[0]
    style = WEATHER_STYLE.get(pred_class, {"icon": "🌦️", "color": "#3498db", "desc": "Unknown"})

with col_right:
    st.write("##### Classification Result")
    st.markdown(
        f"""
        <div style="background-color: {style['color']}; padding: 24px; border-radius: 12px; text-align: center; color: white;">
            <div style="font-size: 50px; line-height: 1;">{style['icon']}</div>
            <h3 style="margin: 6px 0; color: white;">PREDICTED WEATHER CLASS</h3>
            <h1 style="margin: 0; font-size: 40px; font-weight: bold; color: white;">{pred_class.upper()}</h1>
            <p style="margin-top: 6px; font-size: 16px; opacity: 0.95;">{style['desc']}</p>
        </div>
        """,
        unsafe_allow_html=True
    )

    st.write("")
    st.write("**Prediction Probabilities Across Classes:**")
    prob_df = pd.DataFrame({
        "Weather Class": model.classes_,
        "Probability (%)": [round(p * 100, 2) for p in proba]
    }).sort_values("Probability (%)", ascending=False)
    st.dataframe(prob_df, hide_index=True, use_container_width=True)

st.divider()

# ==============================================================================
# Model Analytics & Performance
# ==============================================================================
st.subheader("3. Model Performance & Evaluation")
tab1, tab2 = st.tabs(["Feature Importances", "Confusion Matrix"])

with tab1:
    fig, ax = plt.subplots(figsize=(8, 3.5))
    feat_labels = [
        "Temperature (2m)", "Humidity (2m)", "Precipitation",
        "Cloud Cover", "Wind Speed (10m)", "Surface Pressure"
    ]
    feat_imp = pd.Series(model.feature_importances_, index=feat_labels).sort_values()
    feat_imp.plot(kind="barh", ax=ax, color="#2980b9")
    ax.set_xlabel("Feature Importance (Gini)")
    ax.set_title("Decision Tree Feature Importance")
    ax.grid(axis="x", linestyle="--", alpha=0.6)
    st.pyplot(fig)

with tab2:
    cm_df = pd.DataFrame(cm, index=classes, columns=classes)
    st.write("**Confusion Matrix (Test Split = 20%):**")
    st.dataframe(cm_df, use_container_width=True)
--------------------------------------------------------------------------------
