Deployed on integr8 VM (pve01/700) at environment.mineracks.com. Polls Netatmo every 5 min, stores in SQLite, exposes via FastAPI REST + MCP. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| app | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| README.md | ||
| requirements.txt | ||
netatmo-mcp
Self-hosted bridge that exposes a Netatmo Weather Station to Claude (via MCP) and to anything else (via REST), with a local SQLite store for history and threshold-based alerts. Designed to run as a Docker container on Proxmox.
What you get
One process serving three things on port 8088:
| Path | What it is |
|---|---|
/mcp/ |
MCP server (Streamable HTTP transport). Add to Claude as a connector. |
/api/... |
REST API — same data, for n8n / Grafana / cron / curl. |
/health |
Unauthenticated liveness + poller status. |
A background poller hits Netatmo every 5 minutes (configurable), stores every new reading in SQLite, and evaluates any alerts you've configured.
Capabilities
- Current readings for every station and module (
current_readingstool /GET /api/current) - Local historical queries at native ~5min resolution (
historytool /GET /api/history) - Direct Netatmo historical pulls for longer windows or pre-aggregated scales — 1hour, 1day, 1week, 1month (
history_from_netatmotool) - Threshold alerts on any metric — e.g. CO₂ > 1000 ppm, temp > 28 °C (
create_alerttool /POST /api/alerts) - Multi-station / multi-module — handled natively; everything is keyed by
module_id
One-time Netatmo setup
- Go to https://dev.netatmo.com/apps and create an app. Note the client_id and client_secret.
- On the app page, scroll to Token generator. Tick the
read_stationscope and click "Generate token". Copy the refresh token (you don't need the access token — it only lasts 3 hours). - Make sure your Netatmo account has the data centre station added.
Important: Netatmo rotates the refresh token on every refresh call. This service persists the rotated token to
/data/tokens.jsonso it survives restarts. Don't share that file or the refresh chain breaks.
Deploy on Proxmox
On your Ubuntu VM with Docker:
git clone <this repo> netatmo-mcp && cd netatmo-mcp
cp .env.example .env
# fill in NETATMO_CLIENT_ID, NETATMO_CLIENT_SECRET, NETATMO_REFRESH_TOKEN,
# and NETATMO_MCP_API_KEY=$(openssl rand -hex 32)
docker compose up -d --build
docker compose logs -f
You should see Poller starting and, within a minute or so, Refreshing Netatmo access token followed by the first poll. Hit http://<vm>:8088/health
to confirm.
After ~10 minutes there'll be a few rows in the DB and you can query history.
Putting it behind your reverse proxy
For Claude (web/app) connectors you need a public HTTPS URL. If you're on Caddy:
netatmo.mineracks.ai {
reverse_proxy <vm-ip>:8088
}
Or stick it on Cloudflare Tunnel — same deal. Bearer auth is enforced at the
app, so exposing it is fine as long as your NETATMO_MCP_API_KEY is strong.
Connecting Claude
In claude.ai → Settings → Connectors → Add custom connector:
- Name: Netatmo Datacentre
- URL:
https://netatmo.mineracks.ai/mcp/ - Auth: Bearer token → paste your
NETATMO_MCP_API_KEY
For Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json
on macOS) or Claude Code (~/.claude/settings.json):
{
"mcpServers": {
"netatmo": {
"transport": {
"type": "streamable-http",
"url": "https://netatmo.mineracks.ai/mcp/",
"headers": {
"Authorization": "Bearer YOUR_NETATMO_MCP_API_KEY"
}
}
}
}
}
Then ask Claude: "What's the current CO2 in the data centre?" or "Plot the last 48 hours of temperature for the rack module."
Using the REST API
KEY=your_api_key
BASE=https://netatmo.mineracks.ai
# All modules (find your module_id here)
curl -H "Authorization: Bearer $KEY" $BASE/api/modules
# Current snapshot
curl -H "Authorization: Bearer $KEY" $BASE/api/current
# Last 24h of CO2 for a specific module
curl -H "Authorization: Bearer $KEY" \
"$BASE/api/history?module_id=02:00:00:aa:bb:cc&metric=CO2&hours=24"
# Create an alert: CO2 > 1000 ppm on the rack module
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"name":"dc_co2_high","module_id":"02:00:00:aa:bb:cc","metric":"CO2","op":">","threshold":1000}' \
$BASE/api/alerts
Metrics reference
The fields exposed depend on module type:
| Module | Metrics |
|---|---|
| Indoor (NAMain / NAModule4) | Temperature, Humidity, CO2, Noise, Pressure |
| Outdoor (NAModule1) | Temperature, Humidity |
| Rain (NAModule3) | Rain, sum_rain_1, sum_rain_24 |
| Wind (NAModule2) | WindStrength, WindAngle, GustStrength, GustAngle |
For a data centre you almost certainly care about indoor Temperature,
Humidity, and CO2 — the noise sensor is also a surprisingly useful
"is the AC running differently?" canary.
Files / state
./data/netatmo.db— SQLite history + alerts. Back it up../data/tokens.json— rotating refresh token. Do not delete without generating a new bootstrap refresh token.
Notes & gotchas
- Netatmo rate limits: 50 requests / 10 seconds and 500 / hour per user. At 5-minute poll interval you use ~12/hour, so you're fine — but don't drop the interval below 60s.
- First poll is slow because it does a token refresh. Subsequent polls reuse the access token until it nears expiry.
- History granularity: the local store records whatever Netatmo returns,
which is roughly one point per metric per ~5 min. For longer windows
(weeks/months) use
history_from_netatmowithscale="1day"to get Netatmo's pre-aggregated values. - Alerts are evaluated each poll, so worst-case detection latency =
POLL_INTERVAL_SEC. To wire alerts to email/Slack/ntfy, point an n8n workflow atGET /api/alert-events, or extendPoller._tickto POST to a webhook.