68 lines
No EOL
3.1 KiB
Markdown
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. |