netatmo-mcp/README.md
mineracks 9efe05eca0 Initial commit: Netatmo MCP + REST bridge for DC environmental monitoring
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>
2026-04-20 12:14:18 +00:00

6.1 KiB

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_readings tool / GET /api/current)
  • Local historical queries at native ~5min resolution (history tool / GET /api/history)
  • Direct Netatmo historical pulls for longer windows or pre-aggregated scales — 1hour, 1day, 1week, 1month (history_from_netatmo tool)
  • Threshold alerts on any metric — e.g. CO₂ > 1000 ppm, temp > 28 °C (create_alert tool / POST /api/alerts)
  • Multi-station / multi-module — handled natively; everything is keyed by module_id

One-time Netatmo setup

  1. Go to https://dev.netatmo.com/apps and create an app. Note the client_id and client_secret.
  2. On the app page, scroll to Token generator. Tick the read_station scope and click "Generate token". Copy the refresh token (you don't need the access token — it only lasts 3 hours).
  3. 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.json so 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_netatmo with scale="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 at GET /api/alert-events, or extend Poller._tick to POST to a webhook.