weather-portal/PLAN.md

68 lines
No EOL
3.1 KiB
Markdown

# Plan: Balcony Weather Station Dashboard
## 1. Project Overview
A real-time, neighborhood-facing weather dashboard that streams sensory data from an MQTT broker (Home Assistant) to a web interface.
### Tech Stack
- **Backend:** Gleam (Target: Erlang/OTP)
- **Web Server:** Mist (HTTP & WebSockets)
- **Database:** SQLite (via `sqlight`) for persistent uptime tracking
- **Frontend:** SolidJS (Signals-based reactivity) + Tailwind CSS
- **Infrastructure:** Nomad (Docker-based deployment)
---
## 2. Core Architecture & Actors
- **MQTT Actor:** Subscribes to weather topics (e.g., `tele/sensor/SENSOR`), parses JSON, and broadcasts updates.
- **Monitor Actor:** Tracks sensor "heartbeats." Maintains a state of "Last Seen."
- **WebSocket Manager:** Handles browser connections; pushes HTML-ready JSON to SolidJS.
- **Storage:** Single `weather.db` file using "Schema-on-Boot" (no manual migrations).
---
## 3. Implementation Phases
### Phase 1: Backend Infrastructure (Gleam)
- [ ] Initialize Gleam project with `gleam_otp`, `mist`, `wisp`, and `sqlight`.
- [ ] Implement `database.gleam`:
- Function `init_db` to run `CREATE TABLE IF NOT EXISTS sensor_stats`.
- Schema: `sensor_id (PK)`, `last_seen (DATETIME)`, `uptime_start (DATETIME)`.
- [ ] Implement `mqtt_handler.gleam`:
- Use `gemqtt` (wrapper for `emqtt`) to connect to the broker.
- Map incoming payloads to a `WeatherUpdate` custom type.
### Phase 2: Uptime Logic
- [ ] Implement the `Monitor` Actor logic:
- On MQTT message: Update `last_seen` in SQLite.
- If `(CurrentTime - last_seen) > 10 minutes`, consider the sensor "disrupted."
- On recovery: Reset `uptime_start` to `now`.
- [ ] Calculate "Days since disruption" using: $$(CurrentTime - UptimeStart) / 86400$$
### Phase 3: Real-time Streaming
- [ ] Set up a Mist WebSocket handler.
- [ ] Use a simple process registry (or `gleam_otp` subject) to broadcast JSON to all connected clients.
- [ ] Ensure the JSON payload includes: `temp`, `humidity`, `wind`, and `uptime_days`.
### Phase 4: Frontend (SolidJS)
- [ ] Scaffold SolidJS via Vite.
- [ ] Create a `WeatherCard` component.
- [ ] Implement `createEffect` for WebSocket management:
- On `ws.onmessage`: Update Solid signals.
- On `ws.onclose`: Implement simple exponential backoff for reconnection.
- [ ] Style with Tailwind CSS (Card-based layout, dark mode).
### Phase 5: Deployment (Nomad)
- [ ] Create a multi-stage `Dockerfile`:
- Build SolidJS assets.
- Compile Gleam to a production Erlang release.
- [ ] Create `weather.nomad` job file:
- Mount `host_volume` for the `.db` file.
- Configure networking for the WebSocket port (default 8080).
---
## 4. Claude Code Implementation Instructions
1. **Start with the Backend:** Focus on the `WeatherUpdate` type and the MQTT subscriber first.
2. **Database:** Use `sqlight.exec` to ensure the table exists on every app start.
3. **Frontend:** Keep the SolidJS app in a `./frontend` subdirectory.
4. **Mocking:** If the MQTT broker isn't reachable during coding, create a "Simulator" actor that sends random weather data every 5 seconds.