# SwitchBot Temperature Dashboard Dockerised SwitchBot temperature monitor with: - Flask web dashboard for desktop and mobile. - Python collector polling the SwitchBot API v1.1. - MySQL storage. - Auto-populated device names from the SwitchBot API. - Configurable collection interval, defaulting to 15 minutes. - Day-so-far temperature graph with high/low cards. - Date-range reports with CSV export. - PDF report generation and manual email sending through SMTP2GO. ## Quick Start Create your local environment file: ```sh cp .env.example .env ``` Edit `.env` and set: ```sh SWITCHBOT_TOKEN=your-token SWITCHBOT_SECRET=your-secret FLASK_SECRET_KEY=a-random-local-secret ``` Start everything: ```sh docker compose up --build ``` Open: ```text http://localhost:8000 ``` The collector service will create database tables, discover devices, and begin recording temperature/humidity readings. To change the polling interval, update `COLLECT_INTERVAL_SECONDS` in `.env`; `900` is 15 minutes. Compose builds the internal database URL from `MYSQL_USER`, `MYSQL_PASSWORD`, and `MYSQL_DATABASE`. Set `DATABASE_URL` only if you want to point the app at a different database. ## Maintenance Remove bad readings where temperature, humidity, and battery are all zero: ```sh scripts/remove_zero_readings.sh ``` Preview the count without deleting: ```sh scripts/remove_zero_readings.sh --dry-run ``` ## Reports Open `/reports` in the web app to generate a date-range summary. Use the download button for a CSV export. The same page can create and email a PDF report for the selected date range and device filter. Configure SMTP2GO in `.env` before using it: ```sh REPORT_SENDER_EMAIL=reports@example.com REPORT_SENDER_NAME="SwitchBot Temps" SMTP2GO_API_KEY=your-smtp2go-api-key ``` Generated PDFs and chart images are written under `REPORT_OUTPUT_DIR`, which defaults to `/tmp/switchbot-reports` inside the web container. Open `/report-settings` to manage scheduled report recipients and daily, weekly, or monthly schedules. The `report-scheduler` service sends the previous complete day, week, or month and records each period in the database before sending so restarts do not duplicate emails. ## Services - `db`: MySQL 8.4 with persistent `mysql_data` volume. - `web`: Flask app served by Gunicorn on port 8000. - `report-scheduler`: recurring PDF report sender. - `collector`: SwitchBot polling loop. ## POC Script The original proof-of-concept script is still available: This demo signs a SwitchBot API v1.1 request and prints the JSON response. ## Usage Set your credentials as environment variables: ```sh export SWITCHBOT_TOKEN="your-token" export SWITCHBOT_SECRET="your-secret" ``` List devices: ```sh python3 switchbot_poc.py ``` List devices with an empty-inventory diagnostic: ```sh python3 switchbot_poc.py --diagnose ``` List scenes: ```sh python3 switchbot_poc.py --endpoint /scenes ``` Get a device status: ```sh python3 switchbot_poc.py --endpoint /devices/YOUR_DEVICE_ID/status ``` You can also pass credentials directly: ```sh python3 switchbot_poc.py --token "your-token" --secret "your-secret" ``` The v1.1 signature is: ```text Base64(HMAC-SHA256(secret, token + timestamp_ms + nonce)) ``` ## Empty Device List If `/devices` returns: ```json { "statusCode": 100, "message": "success", "body": { "deviceList": [], "infraredRemoteList": [] } } ``` the signed API request is working, but the account behind that token has no Cloud API-visible devices. Things to check: - The token and secret were generated from the same SwitchBot app account that owns the devices. - You are not logged into a different SwitchBot account, Apple/Google login, or home/family than the one containing the devices. - The devices appear in the SwitchBot mobile app while logged into that account. - Bluetooth-only devices such as Bots, Locks, Curtains, and Meters generally need to be reachable through a SwitchBot Hub for cloud/API access. - If using app version 9 or newer, SwitchBot says the old manual "Cloud Service" toggle was removed for some devices; keep the device near a Hub and verify it is remotely controllable from the app.