Back to Library
Stage 06 / 8D build

Grow Room Monitor MCP.

An 8D production build that polls AC Infinity sensors on a schedule, logs time-series data to a local CSV, and renders a self-contained HTML dashboard you open in any browser. Zero SaaS dependencies. Nothing leaves your computer. The whole thing runs on Python, a cron, and a Claude Code skill that wakes up twice a day to do the pull.

What you'll have
  • A local time-series log of temperature, humidity, and VPD at ~/Documents/grow-room/sensor-log.csv. Two rows per day. Yours forever. No vendor lock-in.
  • A self-contained HTML dashboard rendered to dashboards/index.html on every run. Open it in any browser. No server, no build step, no Vercel.
  • A twice-daily autonomous loop A Claude Code skill plus a cron that fires at 8 AM and 8 PM. The agent polls, logs, renders, and goes back to sleep without you touching it.
8D
Build time ~30 to 45 min
Track 8D
Touches MCP server · Claude Code skill · Cron · Python · Local-only
Source blueprints/grow-room-monitor/
One-click start

Build this with Claude.

Open Claude Code in your local workshop repo and paste the prompt. Claude reads the blueprint end to end, then walks you through each phase, pausing between steps for your confirmation.

How this fits in the OS.

Depends on

Your CLAUDE.md (Stage 01), Python 3.10 or newer on your computer, an AC Infinity controller already paired in the AC Infinity mobile app, and Infisical or 1Password for credentials (Stage 01).

Uses

A local Python MCP server (Claude Desktop calls these connectors), a Claude Code skill that orchestrates the tools, a local CSV for the time-series log, a single static HTML file as the dashboard, and a cron for the twice-daily schedule. Nothing leaves your computer.

Unlocks

The "local-only, no SaaS" pattern. Once you've built one MCP server that wraps a community-reverse-engineered API and exposes it through a skill, the same shape transfers to any device, sensor, or service with no official API.

What you'll build.

A small Python MCP server that wraps the AC Infinity cloud API and exposes six named tools to Claude: authenticate, list_devices, get_sensor_data, log_reading, render, and summarize. A Claude Code skill called grow-room-monitor orchestrates those tools. A cron fires the skill at 8 AM and 8 PM local time. Every run appends two rows to a CSV log and re-renders a single HTML dashboard with a 200-point sparkline for temperature, humidity, and VPD.

The whole thing runs on your computer. No Vercel, no Supabase, no third-party dashboards, no telemetry going anywhere. If you stop running the cron, the data stops growing. That's the entire dependency graph.

Why local-only matters here

AC Infinity does not publish an official API. The Homebridge and Home Assistant communities reverse-engineered the cloud endpoints. That's fine for personal use on your own controller. It is not fine to point at someone else's setup or to run as a hosted SaaS. Local-only is not a stylistic choice for this build, it's the only responsible shape.

What changes for you.

The dashboard is the visible artifact. The bigger shifts are quieter:

  • You stop checking the AC Infinity app. You open one HTML file instead. The data is yours, the rendering is yours, the schedule is yours.
  • You stop trusting a vendor to keep your history. The CSV grows on your disk. If AC Infinity ever sunsets the cloud or breaks the API shape, you still have every reading you ever pulled.
  • You start asking Claude grow-room questions in plain English. "What was my average VPD this week?" works because the skill knows how to call summarize on the log. The data has an agent attached to it.

Before you start.

Four things specific to this build

  • An AC Infinity controller already paired and showing live readings in the AC Infinity mobile app. If the app sees data, the cloud will return it.
  • The email and password you use for the AC Infinity app. Note: the cloud only honors the first 25 characters of your password, longer passphrases get silently truncated.
  • Python 3.10 or newer on your computer.
  • Infisical or 1Password set up from Stage 01. You'll store the AC Infinity credentials there with the placeholder pattern.
Cost

Zero dollars. No SaaS subscriptions, no hosting, no API fees. Your existing AC Infinity account is the only requirement.

The build.

Five phases. About 30 to 45 minutes start to finish. The skill handles the mechanical work, you do the clicks that touch your accounts or your password manager.

Step 01

Pull the latest course updates

3 min

Open Claude Code in your local workshop folder. Onboarding wired your remotes: upstream points at the AI Build Lab course repo (read-only), origin points at your own private GitHub repo.

Paste into Claude Code /update-course
Claude says the remotes look wrong, or /update-course isn't recognized? Ask Claude: "Check my git remotes and tell me which one is missing." If origin or upstream isn't set, Claude can walk you through wiring it up.
Step 02

Install the Python environment

5 min

The MCP server lives in blueprints/grow-room-monitor/server/. Spin up a virtual environment so the dependencies don't pollute your system Python.

Paste into Claude Code

Set up a Python venv at blueprints/grow-room-monitor/server/.venv, activate it, and install the requirements from requirements.txt. If pip install fails on fastmcp, upgrade pip first and retry.

The agent does this for you. Claude creates the venv, activates it inside the session, runs the install, and reports back the resolved package versions.

Install fails on fastmcp? Pip is too old. pip install --upgrade pip, then retry the requirements install.
Step 03

Save your AC Infinity credentials safely

7 min

The MCP server needs your AC Infinity account email and password to authenticate against the cloud API. Both go into Infisical or 1Password as placeholders. Claude creates the entries with PASTE_HERE, then you fill in the real values yourself. You will never paste your real AC Infinity email or password into this chat with Claude.

If Claude offers to enter the real values for you, say no. You always type the real values yourself, in your password manager. This rule has no exceptions.

Paste into Claude Code

Using my Infisical or 1Password setup, create two secrets called CONTROLLER_EMAIL and CONTROLLER_PASSWORD. Set both to the placeholder PASTE_HERE so I can fill them in myself. Then create the .env file at blueprints/grow-room-monitor/server/.env from the .env.example template, pulling the two values from my password manager at runtime. I will never paste my actual credentials into our chat, only the PASTE_HERE placeholder. Do not offer to enter the real values for me. I always type them directly into my password manager myself.

After Claude finishes, fill in the real values yourself in your password manager. Use the same email and password you log in to the AC Infinity mobile app with. Remember: only the first 25 characters of the password are honored by the cloud.

Not sure if your credentials are right? Open the AC Infinity mobile app and log in fresh with the email and password you plan to store. If the app sees your controller, the cloud will return data to the MCP server too.
Step 04

Run a manual smoke test

5 min

Before scheduling anything, prove the pull works once by hand. From blueprints/grow-room-monitor/server/ with the venv active:

Paste into Claude Code python main.py --once

You should see a printed dict with readings_count, log_path, and dashboard_path. Open the dashboard path in your browser. You should see three cards (temperature, humidity, VPD) and a sparkline. If you have no real readings yet, the bundled sample data renders so the dashboard is never empty on first run.

The agent does this for you. Ask Claude to run the smoke test, parse the output dict, and confirm the readings count is non-zero. If the count is zero, Claude can read the AC Infinity skill's failure-mode notes and tell you exactly which check to run next.

Auth error? Your .env credentials are off. Double-check email and password in your password manager. Remember the 25-character truncation.
Zero readings returned? Open the AC Infinity mobile app and confirm your controller is online there. The cloud only returns data the app would see.
API error code 10001 or 100001? The cached token is stale. Delete ~/.config/grow-room-monitor/token.json and re-run python main.py --once.
Step 05

Wire the twice-daily schedule

10 min

The cron config lives at blueprints/grow-room-monitor/schedule/grow-room-monitor.cron.json and fires at 8 AM and 8 PM local. You have two options. Pick one.

Option A: Claude Code scheduled tasks (preferred). The skill drives the pull, the agent handles the scheduling end:

Paste into Claude Code

Schedule the grow-room-monitor skill to run at 8 AM and 8 PM in my local timezone. Use the cron expression from blueprints/grow-room-monitor/schedule/grow-room-monitor.cron.json. The scheduled task should invoke the skill, which calls the log_reading MCP tool. After you wire it, show me the resulting task config and confirm the next two fire times.

Option B: System cron. Add this line to crontab -e, adjusting the path for your machine:

Paste into your terminal 0 8,20 * * * cd /path/to/grow-room-monitor/server && /usr/bin/env python main.py --once >> ../dashboards/cron.log 2>&1

The agent does this for you. Whichever option you pick, ask Claude to verify the schedule landed by showing the next two scheduled run times and pointing at the log file the cron writes to.

Cron does not fire on macOS? Lid-closed sleep blocks the runtime. Enable "Keep computer awake when display is off" in your power settings, or plug into power with caffeinate running.
Both fire times scheduled and verified? You have an autonomous grow-room loop. Open dashboards/index.html any time you want a snapshot.

When it works.

You'll know the build is real when five things happen in order, on a day you did not touch anything:

  1. The cron fires at 8 AM and 8 PM local time. The Claude Code skill wakes up.
  2. The MCP server authenticates against AC Infinity using your cached token, with no fresh login.
  3. Two new rows land in ~/Documents/grow-room/sensor-log.csv per day, one per fire time.
  4. The dashboard at dashboards/index.html re-renders with the latest 200 points on the sparkline.
  5. You ask Claude "summarize my last week of grow room data" in any session, and the skill pulls the CSV and reports highs, lows, and averages without you reminding it where the file lives.

Run it for a week. If the rows keep growing and the sparkline keeps extending, the build is yours.

If you get stuck.

Common failure modes, ranked by how often they hit on first build:

  • Auth fails on the first --once run. Your password got truncated past 25 characters or has a typo. Reset in your password manager, regenerate the .env, retry.
  • Zero readings returned. The controller is offline in the AC Infinity app, or never paired. Open the mobile app first. If the app sees nothing, the API returns nothing.
  • API code 10001 or 100001. Token cache is stale. Delete ~/.config/grow-room-monitor/token.json and rerun. The skill knows to re-authenticate when it sees these codes.
  • Cron fires but no rows append. The cron's working directory is wrong, so main.py ran but the relative paths missed. Fix the cd at the front of the cron line.
  • Dashboard renders but the sparkline is flat. You haven't accumulated enough real data yet. The bundled sample data covers 14 rows so the dashboard isn't empty. Real readings replace the sample as soon as the first cron fires.
  • HTTP errors from the AC Infinity cloud. The cloud is having a hiccup. Wait a few minutes and retry. Twice-daily polling is well under any rate-limit risk, so this is almost always a vendor-side issue.

Still stuck? Post the printed dict from python main.py --once, the contents of dashboards/cron.log (if you used system cron), and what Claude said in the workshop Slack. Someone on the team has seen the same thing.

Where to go next.

You shipped a local-only autonomous loop. The shape transfers. Here's what builds on it.

Back to the Library