Flask app con gauge Chart.js custom, polling Shelly Cloud API v2, storico SQLite, due widget gauge (produzione/consumo) con scala graduata.
87 lines
3.5 KiB
Markdown
87 lines
3.5 KiB
Markdown
# shellydash — AGENTS.md
|
|
|
|
## Quick start
|
|
```bash
|
|
cd /home/codex/shellydash
|
|
venv/bin/python3 app.py # avvia su 0.0.0.0:5050
|
|
```
|
|
|
|
Il `.env` carica automaticamente via `load_dotenv()` in `app.py` e `poller.py`.
|
|
|
|
## Stack
|
|
- Python 3.14, Flask 3.1, SQLite, Chart.js 4.4 (CDN)
|
|
- Venv: `venv/` (già creato)
|
|
- Dipendenze: `flask`, `requests`, `python-dotenv`
|
|
|
|
## Architettura
|
|
|
|
| File | Ruolo |
|
|
|---|---|
|
|
| `app.py` | Entrypoint Flask, 6 route API + serve `templates/index.html` |
|
|
| `shelly_client.py` | Chiamate HTTP POST a Shelly Cloud Control API v2 |
|
|
| `database.py` | SQLite with WAL mode, schema `readings`, energy aggregations |
|
|
| `poller.py` | Thread background: poll Shelly ogni 15s → insert DB + cache `latest_parsed` |
|
|
| `templates/index.html` | Dashboard HTML + JS (Chart.js gauge custom plugin, storico, energia) |
|
|
|
|
### Flusso dati
|
|
```
|
|
Shelly Cloud ← poller.poll_once() (ogni 15s) → database.py (SQLite)
|
|
→ app._latest_parsed (dict, in-memory)
|
|
→ /api/status (da cache, non DB)
|
|
→ /api/history (da DB)
|
|
```
|
|
|
|
### API endpoints
|
|
| Route | Descrizione | Fonte dati |
|
|
|---|---|---|
|
|
| `GET /api/status` | Letture live (produzione, consumo, delta, V, A, PF, temp) | cache poller |
|
|
| `GET /api/history?hours=24` | Array timestamp → produzione/consumo | DB |
|
|
| `GET /api/energy/today` | Energia odierna (prodotta, consumata, immessa) | DB (differenza energy counter) |
|
|
| `GET /api/energy/monthly` | Breakdown giornaliero mese corrente | DB |
|
|
| `GET /api/delta` | Surplus/deficit booleano per automazioni | cache poller |
|
|
|
|
## Shelly EM — assetto canali
|
|
- **Un solo device** con ID `d885ac0b5860` (Shelly EM Gen3, modello S3EM-002CXCEU)
|
|
- `em1:0` → produzione pannelli (pinza sull'uscita inverter)
|
|
- `em1:1` → consumo casa/rete (pinza sull'ingresso inverter, valori negativi = prelievo)
|
|
- `consumption_w` nelle API = `abs(ch1)` quando negativo
|
|
- Auth via `auth_key` (parametro query `?auth_key=...`), **non** email/password
|
|
- Endpoint: `https://shelly-255-eu.shelly.cloud/v2/devices/api/{action}`
|
|
|
|
## Convenzioni codice
|
|
- `from __future__ import annotations` in ogni .py
|
|
- Type hints ovunque
|
|
- Logging via `logging.getLogger("shellydash.*")`
|
|
- `.env` non tracciato in git (`.gitignore` lo esclude)
|
|
|
|
## Frontend (index.html)
|
|
- **Non** usa framework JS — vanilla JS con Chart.js via CDN
|
|
- Gauge semi-circolare con lancetta: plugin Chart.js custom (`gaugePlugin`), **non** libreria esterna
|
|
- Due widget gauge: `#gaugeProd` (produzione, max 6000W, verde) e `#gaugeCons` (consumo, max 3000W, ambra)
|
|
- Scala graduata con tacche maggiori (1000W) e minori (500W) disegnate su canvas
|
|
- Auto-refresh ogni 15s via `setInterval(refreshAll, 15000)`
|
|
- UI in italiano, tema scuro (#0f172a sfondo)
|
|
|
|
## Comandi utili
|
|
```bash
|
|
# Avviare
|
|
venv/bin/python3 app.py
|
|
|
|
# Test API singola
|
|
curl -s http://localhost:5050/api/status | python3 -m json.tool
|
|
|
|
# Verificare import (senza avviare server)
|
|
venv/bin/python3 -c "from app import app; print('ok')"
|
|
|
|
# Installare nuove dipendenze
|
|
venv/bin/pip install <package>
|
|
# E aggiungere a requirements.txt
|
|
```
|
|
|
|
## Note operative
|
|
- Il poller parte in un thread **dopo** il primo `poller.poll_once()`. Serve almeno un poll riuscito prima che `/api/status` risponda.
|
|
- Il DB è in `data/shellydash.db` — escluso da git.
|
|
- `POLL_INTERVAL` in `.env` (default 15s).
|
|
- Non ci sono test (repo in fase iniziale).
|
|
- Il device Shelly deve essere online e connesso al cloud.
|