Skip to content

science/HotTubController

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

233 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Hot Tub Controller

A web-based automation system for intelligent hot tub temperature management and equipment control. Control your hot tub heater, pump, and ionizer remotely via a mobile-friendly web interface with scheduling, temperature monitoring, predictive heating, and safety features.

Why Use This?

If you have a hot tub without built-in smart controls, this project lets you:

  • Turn heating on/off remotely - No more walking outside in the cold to check if the tub is ready
  • Heat to a target temperature - Set a desired temperature and the system automatically manages the heater, stopping when the target is reached
  • "Ready By" scheduling - Tell the system what time you want the tub ready, and it calculates when to start heating based on cooling and heating models
  • Schedule heating in advance - Create one-time or recurring daily schedules
  • Monitor water temperature - Check current water and ambient temperature from anywhere
  • Automate heating cycles - Schedule heater on, then auto-off after a set duration
  • Run the circulation pump - Keep water clean with scheduled ionizer/pump cycles
  • Skip upcoming schedules - Temporarily skip the next occurrence of a recurring job without deleting it

The system uses IFTTT webhooks to control SmartLife/Tuya smart relays, making it compatible with most affordable smart home equipment.

System Architecture

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Web Browser    │───▶│   PHP Backend   │───▶│  IFTTT Webhooks │
│  (SvelteKit)    │◀───│   REST API      │     └────────┬────────┘
└─────────────────┘     └────────┬────────┘              │
                                 │                        ▼
                                 │              ┌─────────────────┐
                                 │              │ SmartLife/Tuya  │
                        ┌────────▼────────┐     │ Smart Relays    │
                        │  ESP32 Sensor   │     └────────┬────────┘
                        │  (DS18B20)      │              │
                        └─────────────────┘              ▼
                                                ┌─────────────────┐
                                                │   Hot Tub       │
                                                │   Equipment     │
                                                └─────────────────┘

Features

Core Controls

  • Mobile-First Web UI - Dark-themed, responsive interface optimized for phones
  • One-Tap Controls - Quick buttons for heater on/off and pump activation with active glow state
  • Equipment Status Display - Control buttons illuminate when equipment is active
  • Cancel Feedback - Buttons show "Cancelling..." state with visual dimming during API calls

Heat-to-Target

  • Automatic Temperature Control - Set a target temperature (80-110°F) and the system manages the heater automatically
  • Stall Detection - Detects when water temperature stops rising (e.g., heater malfunction) and shuts down safely
  • Configurable Stall Parameters - Adjust grace period and timeout for stall detection

Scheduling

  • Quick Scheduling - Pre-configured buttons to heat "In 30 min", "In 1 hour", etc.
  • Full Scheduler - Create one-time or recurring daily heating schedules
  • "Ready By" Mode - Schedule by desired ready time; the system uses cooling and heating models to calculate the optimal start time
  • Auto Heat-Off - Automatically schedule heater shutdown after configurable duration
  • Skip/Unskip - Temporarily skip the next occurrence of a recurring job and resume later
  • Auto-Refresh - Schedule panel automatically refreshes when a job's scheduled time passes

Heating Analysis

  • Heating Characteristics - Analyzes past heating sessions to determine heating velocity (°F/min), startup lag, and overshoot
  • Newton's Law Cooling Model - Fits a cooling curve to historical data to predict temperature decay over time
  • Precision Start Calculation - "Ready By" mode uses heating and cooling models to project the optimal time to begin heating

Temperature Monitoring

  • Real-Time Display - Water and ambient temperature from ESP32 sensors
  • Multi-Sensor Support - Configure multiple DS18B20 sensors with assigned roles (water, ambient)
  • Sensor Calibration - Per-sensor calibration offsets for accuracy
  • Dynamic Reporting Intervals - ESP32 reports every 5 minutes normally, switching to 1-minute intervals when the heater is active for tighter temperature control
  • Temperature History - Daily temperature logs for analysis
  • OTA Firmware Updates - ESP32 firmware updated over-the-air via HTTP; the device checks for updates each time it reports temperature

Users & Authentication

  • JWT Authentication - Secure login with httpOnly cookie support
  • Three User Roles - Admin (full access), User (controls + scheduling), Basic (controls + temperature only)
  • User Management - Admin interface for creating, managing, and deleting users
  • Password Management - Admin can reset user passwords

Monitoring & Maintenance

  • Healthchecks.io Integration - Optional monitoring for cron job alerts
  • Request Logging - All API requests logged in JSON Lines format with automatic rotation
  • Equipment Event Log - Timestamped record of all heater/pump actions and stall events
  • Crontab Backups - Automatic timestamped backups before crontab modifications
  • Orphan Job Cleanup - Maintenance endpoint to clean up stale job files

Hardware Requirements

Temperature Monitoring

ESP32 with DS18B20

  • ESP32 Development Board - WiFi-enabled microcontroller
    • ESP32-WROOM-32 based board (NodeMCU, DevKit, etc.) such as this one.
  • DS18B20 Temperature Sensor - Waterproof digital thermometer
    • 1-Wire interface, accurate to 0.5C
    • 4.7K pull-up resistor required (connect between data and VCC) such as this one
  • PlatformIO - For firmware development and flashing
    • See esp32/ directory for firmware code

Equipment Control

  • Smart Relay Controller - IFTTT-compatible device for pump and heater control
    • Compatible: SmartLife or Tuya-based smart switches/relays such as this one
    • Example: 4-channel WiFi relay modules with smartphone app
  • IFTTT Account - Free tier works fine for webhook integrations

IFTTT Webhook Setup

The system requires IFTTT webhook events connected to SmartLife/Tuya "scenes". Since IFTTT cannot directly control individual SmartLife switches, you create scenes that IFTTT can trigger.

If you use other smart home control systems, they will work fine as long as IFTTT can trigger the appropriate events associated with the Webhooks described below.

Required Webhooks

Webhook Event Purpose SmartLife Scene
hot-tub-heat-on Start heating Turn on pump, wait 60s, turn on heater
hot-tub-heat-off Stop heating Turn off heater, wait 90s, turn off pump
cycle_hot_tub_ionizer Run pump/ionizer Turn on pump for 2 hours

SmartLife Scene Configuration

Heat On Scene (hot-tub-heat-on):

1. Turn ON hot tub water pump
2. Wait 60 seconds (allows water circulation)
3. Turn ON hot tub heater

Heat Off Scene (hot-tub-heat-off):

1. Turn OFF hot tub heater
2. Wait 90 seconds (cooling circulation)
3. Turn OFF hot tub water pump

This sequencing protects heating elements by ensuring proper water flow during operation.

Creating the IFTTT Applets

  1. Go to IFTTT Create
  2. If This: Choose "Webhooks" → "Receive a web request"
  3. Event Name: Enter exactly hot-tub-heat-on (case-sensitive)
  4. Then That: Choose your controller system such as "SmartLife" → "Activate scene"
  5. Scene: Select your "Heat On" scene or however your backend controller works
  6. Save and repeat for other webhooks

Get your webhook key from: https://ifttt.com/maker_webhooks/settings

Quick Start

Backend Setup

cd backend
composer install

# Copy development config (uses stub mode - no real hardware triggers)
cp config/env.development .env

# For production, use:
# cp config/env.production.example .env
# Then edit .env to add your real API keys

# Start development server
php -S localhost:8080 -t public

Frontend Setup

cd frontend
npm install
npm run dev    # Starts on http://localhost:5173

Default login: admin / password (change in production!)

ESP32 Firmware Setup

For temperature sensing:

cd esp32

# Create .env file with your WiFi and API credentials
cat > .env << 'EOF'
WIFI_SSID=your-wifi-network
WIFI_PASSWORD=your-wifi-password
ESP32_API_KEY=your-secure-api-key
API_ENDPOINT=http://your-server.com/api/esp32/temperature
EOF

# Install PlatformIO (if not already installed)
# pip install platformio

# Build and upload firmware
pio run --target upload

# Monitor serial output
pio device monitor

The ESP32 will:

  • Connect to WiFi and read temperature from DS18B20 sensors (GPIO 4)
  • Sync time via NTP for coordinated reporting, aligned to the :53 second mark
  • POST temperature readings every 5 minutes normally, or every 1 minute when the heater is active (interval is server-controlled)
  • Check for OTA firmware updates on each report and install automatically
  • Use exponential backoff on failures (10s to 5 min)
  • Auto-reboot after 30 minutes of continuous failures

Hardware wiring:

  • DS18B20 VCC to ESP32 3.3V
  • DS18B20 GND to ESP32 GND
  • DS18B20 DATA to ESP32 GPIO 4
  • 4.7K resistor between DATA and VCC

ESP32 Remote Debugging (Telnet)

The ESP32 includes a telnet debugger for remote diagnostics without physical access:

# Connect to ESP32 (default port 23)
telnet <esp32-ip-address>

# Or using netcat
nc <esp32-ip-address> 23

Available commands:

Command Description
help Show available commands
diag Full diagnostics (WiFi, sensors, readings)
scan Scan OneWire bus for sensors
read Read all sensor temperatures
info Show firmware version, IP, uptime
ota Show OTA update status

Example diagnostic output:

--- Connection Info ---
Firmware: 1.3.0
WiFi SSID: your-network
IP Address: 192.168.1.100
Signal Strength: -74 dBm
Uptime: 3600 seconds

--- Sensor Readings ---
Sensor 0 (28:B4:51:02:00:00:00:9A):
  Temperature: 39.38 C / 102.88 F
  Status: OK

ESP32 HTTP OTA Firmware Updates

The ESP32 supports over-the-air firmware updates via HTTP. Updates are pulled automatically when the device reports temperature to the API.

How it works:

  1. ESP32 reports its current firmware version when posting temperature data
  2. Server responds with new firmware info if an update is available
  3. ESP32 downloads and installs the update automatically
  4. Device reboots into new firmware

Deploying a new firmware version:

cd esp32

# 1. Update version number in src/main.cpp
#    #define FIRMWARE_VERSION "1.4.0"

# 2. Build the firmware
pio run

# 3. Copy binary to backend storage
cp .pio/build/esp32dev/firmware.bin \
   ../backend/storage/firmware/firmware-1.4.0.bin

# 4. Update firmware config
cat > ../backend/storage/firmware/config.json << 'EOF'
{
    "version": "1.4.0",
    "filename": "firmware-1.4.0.bin",
    "updated_at": "2026-01-22T12:00:00-08:00",
    "notes": "Description of changes"
}
EOF

# 5. Commit and deploy via PR to production
git add -A
git commit -m "Deploy ESP32 firmware v1.4.0"
git push origin main
# Create PR from main to production and merge

Manual USB flash (if OTA unavailable):

cd esp32
pio run -t upload
pio device monitor  # View boot logs

Firmware storage structure:

backend/storage/firmware/
├── config.json           # Current version metadata
├── firmware-1.3.0.bin    # Firmware binary (tracked in git)
└── firmware-1.2.0.bin    # Previous versions (optional)

Configuration

All configuration is via backend/.env:

# Application mode
APP_ENV=development

# External API Mode - controls ALL external services (IFTTT, Healthchecks.io)
EXTERNAL_API_MODE=stub       # stub (safe) or live (requires keys)

# IFTTT webhook integration
IFTTT_WEBHOOK_KEY=your-key   # From IFTTT Maker Webhooks settings

# ESP32 temperature sensor
ESP32_API_KEY=your-secure-api-key        # Secure API key for ESP32 authentication

# Healthchecks.io monitoring (optional)
HEALTHCHECKS_IO_KEY=your-key             # API key for cron job monitoring
HEALTHCHECKS_IO_CHANNEL=your-channel     # Notification channel slug

# Authentication
AUTH_ADMIN_USERNAME=admin
AUTH_ADMIN_PASSWORD=change-this!
JWT_SECRET=generate-a-secure-random-string

# API Base URL - Required for scheduled jobs (used by cron)
API_BASE_URL=https://your-server.com/path/to/backend/public

External API Mode

Mode Description
stub Simulates all API calls - safe for development/testing
live Makes real API calls to IFTTT and Healthchecks.io

The unified EXTERNAL_API_MODE defaults to stub for fail-safe behavior.

User Roles

Role Capabilities
admin Full access: equipment control, scheduling, settings, user management, heating analysis
user Standard access: equipment control, scheduling, settings
basic Simplified UI: equipment control and temperature display only

The basic role is useful for household members who just need to turn the hot tub on/off without the complexity of scheduling features.

Running Tests

# Run ALL tests (backend + frontend + ESP32 + E2E)
./scripts/test.sh

# Individual suites
./scripts/test.sh backend      # PHP backend tests
./scripts/test.sh frontend     # SvelteKit unit tests
./scripts/test.sh esp32        # ESP32 native tests
./scripts/test.sh e2e          # Playwright E2E tests

Project Structure

hot-tub-controller/
├── backend/                 # PHP REST API
│   ├── public/              # Web root (index.php entry point)
│   ├── src/
│   │   ├── Controllers/     # API endpoint handlers
│   │   ├── Services/        # Business logic, external API clients
│   │   ├── Middleware/      # Auth, CORS
│   │   └── Routing/         # URL router
│   ├── storage/             # Job files, logs, user data, firmware
│   └── tests/               # PHPUnit tests
│
├── frontend/                # SvelteKit web UI
│   ├── src/
│   │   ├── lib/components/  # UI components
│   │   ├── lib/stores/      # State management
│   │   └── routes/          # Pages
│   └── e2e/                 # Playwright E2E tests
│
├── esp32/                   # ESP32 firmware (PlatformIO)
│   ├── src/                 # Main firmware code
│   ├── lib/                 # ApiClient library
│   └── test/                # Unity unit tests
│
└── CLAUDE.md                # Development guidelines

API Endpoints

Public

Method Endpoint Description
GET /api/health Health check with equipment status and heat-target settings
POST /api/auth/login Login (sets httpOnly cookie)
POST /api/auth/logout Logout (clears cookie)

Authenticated

Method Endpoint Description
GET /api/auth/me Current user info
POST /api/equipment/heater/on Turn heater on via IFTTT
POST /api/equipment/heater/off Turn heater off via IFTTT
POST /api/equipment/pump/run Run pump cycle via IFTTT
POST /api/equipment/heat-to-target Start heat-to-target session
GET /api/equipment/heat-to-target Get heat-to-target status
DELETE /api/equipment/heat-to-target Cancel heat-to-target session
GET /api/temperature Get current water/ambient temperatures
GET /api/settings/heat-target Get heat-to-target settings
POST /api/schedule Schedule a job (one-time or recurring)
GET /api/schedule List scheduled jobs
DELETE /api/schedule/{id} Cancel a scheduled job
POST /api/schedule/{id}/skip Skip next occurrence of recurring job
DELETE /api/schedule/{id}/skip Unskip a recurring job
GET /api/esp32/sensors List ESP32 sensors with config
PUT /api/esp32/sensors/{address} Update sensor role/calibration

Admin Only

Method Endpoint Description
PUT /api/settings/heat-target Update heat-to-target settings
GET /api/admin/heating-characteristics Get heating/cooling analysis
POST /api/admin/heating-characteristics/generate Generate heating analysis
GET /api/admin/crontab View current crontab entries
GET /api/users List users
POST /api/users Create user
DELETE /api/users/{username} Delete user
PUT /api/users/{username}/password Reset user password

ESP32 / Cron

Method Endpoint Description
POST /api/esp32/temperature Receive temperature data from ESP32 (API key auth)
POST /api/maintenance/logs/rotate Rotate log files
POST /api/maintenance/jobs/cleanup Clean up orphaned job files
POST /api/maintenance/heat-target-check Heat-to-target temperature check
POST /api/maintenance/dtdt-wakeup DTDT ready-by wakeup handler

Safety Considerations

This system controls real electrical equipment. Safety features include:

  • Test Mode: Always develop with EXTERNAL_API_MODE=stub
  • Equipment Sequencing: Pump starts before heater, heater stops before pump
  • Stall Detection: Heat-to-target automatically shuts down if temperature stops rising
  • Authentication Required: All control endpoints require login
  • Audit Logging: All API requests and equipment events are logged with timestamps
  • File Locking: Concurrent heat-to-target operations are prevented via flock

Never deploy to production without:

  1. Changing default passwords
  2. Setting a secure JWT_SECRET
  3. Configuring HTTPS
  4. Testing equipment sequencing with your actual setup

Deployment Notes

The system is designed to run on "low cost web hosting services" such as cPanel hosts. The main requirement is that your host must allow cron job scheduling (and scheduled crons must execute reliably). Otherwise it uses only basic HTTP APIs between front and backend. The ESP32 thermometer integration is "receive only" - so the ESP32 device sends temperature on a schedule, which the backend can alter whenever the device phones in (to increase or decrease the frequency of reporting).

FTP Deploy Action Tool for FTP+SSL - Thanks Sam!

Due to the low cost host, "real" deployment techniques may not be available. Currently I'm deploying this to my host via FTP+SSL in a Github Action, which is working perfectly thanks to this free tool:

Website Deployed for Free with FTP Deploy Action

Logging & Monitoring

Request Logging

All API requests are logged in JSON Lines format to backend/storage/logs/api-requests-*.jsonl:

  • Timestamp, IP address, HTTP method, URI
  • Response status and duration
  • Authenticated username (if applicable)

Equipment Event Logging

All heater/pump actions are recorded to backend/storage/logs/equipment-events.log:

  • Heater on/off events with water temperature at time of action
  • Stall detection events
  • Used by the heating characteristics analysis

Log Rotation

Automated log rotation runs monthly via cron:

  • Compress logs older than 30 days
  • Delete compressed logs older than 6 months
  • Crontab backups follow the same retention policy

Healthchecks.io Integration (Optional)

When HEALTHCHECKS_IO_KEY is configured, scheduled jobs are monitored:

  • Health check created when job is scheduled
  • Alert triggered if job fails to execute within timeout
  • Check deleted on successful completion

This provides proactive notification if a scheduled heating job fails to fire.

Contributing

This project uses Test-Driven Development (TDD):

  1. RED: Write a failing test first
  2. GREEN: Write minimal code to pass
  3. REFACTOR: Clean up while tests stay green

See CLAUDE.md for detailed development guidelines.

License

Licensed under the Apache License, Version 2.0.

Author

Stephen Midgley

About

Hot tub heating scheduler and controller with mobile-first responsive interface

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors