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.
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.
- 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 |
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 | bashAn 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.
git clone <this repo> moonpool && cd moonpool npm install && cp .env.example .env.local && npm run dev
Open http://localhost:3000 โ MOCK_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.
โ 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
โโโโโโโโโโโโโโโโโโโโโโโโโโโ 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 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.
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.
- 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.
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.
- 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.
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.