Skip to content

Repository files navigation

🌙 Moonpool

Self-hosted pool control for Pentair panels. Everything on your Raspberry Pi. Nothing in anyone's cloud.

Moonpool is a full replacement for Pentair ScreenLogic: a fast, beautiful, phone-first web app that sits on top of nodejs-poolController (njsPC) and adds authentication, roles, automations, history, alerts, voice control, and an AI copilot. Users, history, scenes, chat threads, the audit log — all of it lives in a single SQLite file on the Pi.

Dashboard

Why

njsPC does the heroic work of speaking Pentair's RS-485 protocol — but it has no authentication and its bundled UIs aren't built for handing to your family or opening to the internet. Moonpool is the security and experience layer: the browser never talks to njsPC directly; every request goes through Moonpool's session- and role-checked API, and every state change is audited.

Features

  • Night-swim UI — deep teal-navy glass panels over animated water caustics, liquid-fill temperature dials that ripple while heating, spring physics everywhere, OLED-black and light themes, custom accent colors. Mobile-first PWA: install to your home screen, offline last-known state.
  • Everything ScreenLogic did, better — circuits & features, thermostat dials with heat modes, IntelliFlo RPM control with live watts, IntelliChlor output/salt/super-chlorinate, IntelliBrite themes + saved light combos, schedule CRUD with a visual week view and conflict warnings.
  • Discovered, not hardcoded — the UI renders whatever bodies, circuits, pumps, chlorinators, light groups and chem controllers your panel reports.
  • Roles — Owner / Family / Guest with per-circuit and per-scene guest visibility. Local accounts, no cloud sign-ups.
  • Automations — time, cron, sunrise/sunset offsets, temperature thresholds, salt low, freeze protect, controller events. Scenes, schedules, one-shot timers and the copilot all ride the same engine.
  • Pool Copilot — natural language in, structured tool calls out. Three switchable brains: local Ollama (private, free), OpenAI API key, or Sign in with ChatGPT (Codex-style OAuth, uses your subscription). The model only parses intent — Moonpool validates, bounds-checks, role-checks, confirms first, and audits. Plus a UniFi-style ask bar on the dashboard.
  • Weather-aware — keyless Open-Meteo forecasts power confirmations like "Heat the pool? Rain is forecast tomorrow 3–4 PM (80%)", an evaporation-based water-level estimator ("down ~1.4 in, no rain coming — add water"), and freeze/heat advisories. Optional WeatherFlow Tempest integration (local UDP): measured rain, on-site wind, and lightning "out of the pool" alerts.
  • Voice — Siri Shortcuts (5-minute setup, no dev account) and a private Alexa custom skill, both with token auth and full auditing.
  • History & insight — temperature/pump/salt/chemistry charts with daily rollups, equipment runtime and energy cost at your $/kWh, chemistry logging with dosing math sized to your pool volume.
  • Alerts — self-hosted web push (no push provider account): faults, freeze protect, salt low, chemistry out of range, spa ready, controller offline, water level low, lightning.
Ask bar Lights

Install

One line on your Pi (or any Linux box with Docker — the installer offers to install Docker for you):

curl -fsSL https://github.com/v6iox/poolsystem/releases/latest/download/install.sh | bash

An interactive, arrow-key installer walks you through everything: real pool vs. demo simulator, RS-485 adapter auto-detection, timezone & location, the copilot's AI brain, optional remote access — then builds the stack and hands you the URL. Re-run it any time to reconfigure; install.sh --update upgrades in place.

Try it in 60 seconds (no hardware)

git clone <this repo> moonpool && cd moonpool
npm install && cp .env.example .env.local && npm run dev

Open http://localhost:3000MOCK_MODE boots a full pool simulator (pool + spa, 10 circuits, VS pump, chlorinator, color lights, drifting temperatures) so every screen, the copilot, and the weather features work with zero equipment.

Real setup

The complete setup guide covers everything: hardware list, flashing the Pi, RS-485 wiring, Docker bring-up, phone install, remote access without a VPN app (Cloudflare Tunnel / Tailscale Funnel), push notifications, copilot brains, Siri & Alexa, Tempest, backups, and running alongside ScreenLogic during the transition.

The short version:

ssh pi@moonpool.local
curl -fsSL https://get.docker.com | sh
git clone <this repo> moonpool && cd moonpool
cp .env.example .env && nano .env        # AUTH_SECRET, TZ, lat/long, MOCK_MODE=false
docker compose up -d --build
docker compose exec ollama ollama pull qwen3:1.7b

Architecture

                 ┌────────────────────────── Raspberry Pi ──────────────────────────┐
                 │                                                                  │
 Pentair panel ──RS-485──▶ USB adapter ──▶ njsPC (no auth, internal only)           │
                 │                            ▲ REST + Socket.IO                    │
                 │                            │                                     │
 Phone/laptop ◀──HTTPS──▶ Moonpool (Next.js)  │                                     │
      ▲          │        ├─ session auth (scrypt + SQLite)                         │
      │          │        ├─ validated control layer → audit log                    │
 Cloudflare      │        ├─ SSE state bridge (role-filtered)                       │
 Tunnel /        │        ├─ automations worker (cron·sun·thresholds·events)        │
 Tailscale       │        ├─ history sampler + rollups        ┌────────────┐        │
 Funnel          │        ├─ web-push alerts                  │ SQLite     │        │
                 │        ├─ Tempest UDP listener             │ moonpool.db│        │
                 │        └─ copilot engine ──▶ Ollama/OpenAI └────────────┘        │
                 └──────────────────────────────────────────────────────────────────┘

Design decisions worth knowing:

  • No external database. SQLite (better-sqlite3, WAL). The idempotent schema (src/server/db/schema.sql) applies itself at boot — that file is the migration.
  • One action vocabulary. Every button, scene, automation, schedule, voice command and copilot plan produces the same typed PoolActions, executed by one validation + audit path (src/server/control.ts).
  • Server-held realtime. The server owns the single Socket.IO connection to njsPC and fans role-filtered snapshots out to browsers over SSE. Optimistic UI with automatic rollback on every control.
  • AI can't touch hardware. Any model — local or cloud — only emits tool calls. Bounds checks (setpoints 60–104 °F), circuit whitelisting, role rules, confirmation-first cards and auditing all happen in Moonpool.

Moonpool Cloud (optional)

Moonpool can dial out to Moonpool Cloud (moonpool-cloud, a separate, privately-run service) for remote access from the mobile app when you're away from home — device claiming, request relay, and push fan-out ride a single outbound WebSocket the Pi opens to the cloud (see CONTRACT.md §3 for the wire protocol, src/server/cloudlink/ for the client).

This is strictly opt-in, and off by default. Leaving the four MOONPOOL_CLOUD_* variables unset (or MOONPOOL_CLOUD_ENABLED=false) means Moonpool never attempts a connection, never imports the tunnel code path at runtime beyond a no-op check, and every local feature — the dashboard, automations, alerts, the copilot — keeps working exactly as if the cloud didn't exist. The cloud is never load-bearing: even once connected, no local request ever waits on it, and losing the connection degrades to "no remote access" with zero effect on LAN use. A self-hosted install stays fully local until someone deliberately turns this on.

Remote requests never bypass local auth. A relayed phone request carries a short-lived, signed actor assertion (CONTRACT §12), not a raw user id. Moonpool verifies it — signature, expiry (60s, hard-capped at 120s regardless of what the token claims), replay, and that it's scoped to this exact device — maps it to the local account it's linked to, and only then mints a normal, ephemeral, single-request local session for that user. Nothing in the app's own route guards ever reads the raw header; the local HTTP surface can't be tricked by anyone on the LAN into acting as someone else, and a cloud "staff" claim never grants any local permission — see src/server/cloudlink/actor-*.ts.

Trademarks

Any physical interface cable is the "Moonpool Link cable, compatible with Pentair® EasyTouch®/IntelliTouch®/IntelliCenter®" — never "a Pentair cable" or "Pentair Moonpool". Nominative use only; Moonpool is not affiliated with or endorsed by Pentair. See docs/AGPL-BOUNDARY.md for the njsPC licensing boundary this project depends on.

Security model

  • njsPC and Ollama are never published on host ports — only Moonpool is.
  • scrypt password hashing, httpOnly session cookies, login rate limiting.
  • Guests only see/touch what an Owner explicitly shares.
  • Voice endpoints use single-purpose revocable tokens (hash-stored); the Alexa endpoint additionally verifies Amazon's request signatures.
  • Secrets (VAPID keys, API keys, OAuth tokens) live only in the Pi's SQLite.

Development

npm run dev        # MOCK_MODE dev server
npm run typecheck  # strict TS, no `any`
npm test           # includes the 30-utterance copilot eval (mock parser)
npm run build      # production build (standalone)

COPILOT_LIVE=true npm test additionally runs the eval against a live LLM backend. PRs welcome — keep the design language (tokens in src/app/globals.css) and add loading/empty/error/offline states to anything you build.

Credits

  • nodejs-poolController by @tagyoureit and contributors — the foundation this stands on. Moonpool shares no code with it; it drives njsPC's REST + Socket.IO API. Go star it.
  • Open-Meteo — keyless weather + evapotranspiration.
  • WeatherFlow Tempest — UDP broadcast reference for the local station integration.
  • Next.js, Tailwind, Motion, Radix, Recharts, TanStack Query, dnd-kit, better-sqlite3, Ollama.

Disclaimer

This software controls physical equipment — heaters, pumps, valves, chlorinators. It is provided as is, without warranty of any kind. Keep your panel's built-in safeties in place, supervise early use, and understand that you run it at your own risk. Not affiliated with Pentair, OpenAI, Amazon, Apple, or WeatherFlow.

License

MIT

About

Self-hosted, local-first pool control for Pentair panels. Night-swim UI, automations, local AI copilot. Runs entirely on your Pi.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages