diff --git a/README.md b/README.md new file mode 100644 index 0000000..3db7860 --- /dev/null +++ b/README.md @@ -0,0 +1,171 @@ +# ShellyDash ⚡ + +Dashboard web per il monitoraggio in tempo reale di un impianto fotovoltaico con **Shelly EM Gen3**. + +Legge i dati da un Shelly EM (2 pinze: produzione pannelli e consumo casa/rete) via **Shelly Cloud API v2**, li memorizza su SQLite e li visualizza su una dashboard con gauge a lancetta semi-circolare e grafico storico. + +## Funzionalità + +- **Due gauge analogici**: produzione pannelli (0–6000W, verde) e consumo casa (0–3000W, ambra), con scala graduata e lancetta +- **Surplus/deficit** istantaneo: produzione − consumo +- **Energia odierna**: prodotto, consumato, immesso in rete +- **Grafico storico**: 24h / 3gg / 7gg produzione + consumo +- **API REST JSON** per automazioni e integrazioni +- **Auto-refresh** ogni 15 secondi +- **Tema scuro**, UI in italiano + +## Architettura + +``` +shellydash/ +├── app.py ← Entrypoint Flask (6 route API + serve frontend) +├── shelly_client.py ← Client HTTP per Shelly Cloud API v2 +├── database.py ← SQLite (WAL mode), schema readings +├── poller.py ← Thread background polling periodico +├── templates/ +│ └── index.html ← Dashboard: gauge Chart.js custom + grafico storico +├── data/ +│ └── shellydash.db ← SQLite (gitignorato) +├── .env ← Credenziali Shelly (gitignorato) +├── AGENTS.md ← Istruzioni per AI agent +└── requirements.txt ← flask, requests, python-dotenv +``` + +### Flusso dati + +``` +Shelly Cloud + ↕ HTTP POST (auth_key) +poller.poll_once() ← ogni 15s (thread background) + ↕ +shelly_client.py ← parse readings (em1:0, em1:1) + ↕ +database.py (SQLite) ← INSERT storico + ↕ +app._latest_parsed (dict) ← cache in-memory per /api/status + ↕ +app.py → /api/status ← da cache (risposta immediata) + → /api/history ← da DB (storico) + → /api/energy/* ← da DB (aggregazioni) + → / → templates/index.html +``` + +## Setup + +### Prerequisiti + +- Python 3.14+ +- Shelly EM Gen3 connesso al cloud Shelly +- Chiave API Cloud (trovabile in App Shelly → Impostazioni → Casa → Cloud) + +### Installazione + +```bash +cd /home/codex/shellydash +python3 -m venv venv +venv/bin/pip install -r requirements.txt +``` + +### Configurazione + +Crea un file `.env` nella root del progetto (non tracciato in git): + +```env +SHELLY_SERVER="https://shelly-255-eu.shelly.cloud" +SHELLY_AUTH_KEY="la_tua_auth_key" +SHELLY_DEVICE_ID="id_dispositivo" +POLL_INTERVAL=15 +FLASK_PORT=5050 +FLASK_HOST="0.0.0.0" +``` + +### Avvio + +```bash +venv/bin/python3 app.py +``` + +Apri il browser su `http://localhost:5050`. + +## API REST + +Tutte le API restituiscono JSON con campo `ok` booleano. + +### `GET /api/status` +Letture live in tempo reale (dalla cache del poller). + +```json +{ + "ok": true, + "ts": 1722520000, + "production_w": 1735.4, + "consumption_w": 134.4, + "delta_w": 1601.0, + "voltage": 229.8, + "ch0_current": 7.55, + "ch1_current": 0.59, + "ch0_pf": 0.99, + "ch1_pf": 0.85, + "temperature": 59.6, + "wifi_rssi": -67, + "online": true +} +``` + +### `GET /api/history?hours=24` +Array di readings storici dal database. + +### `GET /api/energy/today` +Energia odierna in kWh. + +### `GET /api/energy/monthly` +Breakdown giornaliero del mese corrente. + +### `GET /api/delta` +Surplus/deficit booleano (utile per automazioni). + +```json +{ + "ok": true, + "delta_w": 1601.0, + "production_w": 1735.4, + "consumption_w": 134.4, + "surplus": true +} +``` + +## Shelly EM — Configurazione canali + +Il progetto è tarato su un **Shelly EM Gen3** (S3EM-002CXCEU) con una singola unità e **due pinze**: + +| Canale | ID | Misura | Segno | +|---|---|---|---| +| Produzione | `em1:0` | Uscita inverter → potenza generata | Sempre positivo | +| Consumo | `em1:1` | Ingresso inverter → prelievo/rete | Negativo = consumo, Positivo = immissione | + +L'API normalizza automaticamente: `consumption_w = abs(ch1)` quando ch1 è negativo. + +## Frontend + +- **Framework**: nessuno — vanilla JavaScript +- **Grafici**: Chart.js 4.4.7 via CDN +- **Gauge**: plugin Chart.js custom (`gaugePlugin`) con disegno su canvas di: + - Scala graduata (tacche maggiori ogni 1000W, minori ogni 500W) + - Lancetta rotante + - Valore numerico al centro +- **Tema**: scuro (#0f172a sfondo) +- **Refresh**: `setInterval(refreshAll, 15000)` + +## Stack tecnologico + +| Componente | Tecnologia | +|---|---| +| Backend | Python 3.14, Flask 3.1 | +| Database | SQLite (WAL mode) | +| Cloud API | Shelly Cloud Control API v2 | +| Frontend | HTML5, CSS3, Chart.js 4.4 (CDN) | +| Ambiente | venv | + +## Licenza + +Progetto privato — uso personale.