Control Daemon — Installation (Raspberry Pi)
Install the control daemon on the Pi 3 controller. Design details: README.md.
Prerequisites
- Raspberry Pi OS with Python 3
-
Redis running locally (
redis-server) -
Temperature daemon recommended (
hot-tub-temps) sotemps:latestis fresh - Heat relay on GPIO22, filter relay on GPIO23 (see hardware-setup.md)
- IoT relay modules wired; confirm
active_highin config matches your modules
1. Stop conflicting GPIO users
Do not run src/control_poc.py or other relay experiments while the control service is enabled — they share the same pins.
# If you were testing the PoC:
pkill -f control_poc.py || true
2. Install service
From the hot-tub repository on the Pi:
cd /path/to/hot-tub
sudo deploy/control/install.sh
Optional fixed install root:
sudo INSTALL_ROOT=/opt/hot-tub deploy/control/install.sh
The script installs python3-redis, python3-gpiozero, adds user hot-tub to group gpio, installs /etc/hot-tub/control.json, systemd unit hot-tub-control, and logrotate for /var/log/hot-tub/control.log.
3. Configure
Edit /etc/hot-tub/control.json if needed (GPIO pins, maintain band, filter auto schedule, target limits).
Persisted operator state lives in /var/lib/hot-tub/control-state.json (heat_mode, filter_mode, target_f, latched startup error).
The installed unit uses Type=notify and WatchdogSec=10: Control runs a 1 s wait-with-timeout loop and pings systemd each pass. Full loop behavior is documented in README.md.
4. Verify
systemctl status hot-tub-control
tail -f /var/log/hot-tub/control.log
redis-cli GET control:latest
redis-cli GET health:control
redis-cli SUBSCRIBE control.state
Send test commands:
redis-cli PUBLISH control.command '{"op":"set_target_temp","args":{"target_f":102},"id":"1","source":"cli"}'
redis-cli PUBLISH control.command '{"op":"set_heat_mode","args":{"mode":"maintain"},"id":"2","source":"cli"}'
redis-cli GET control:latest
Expect heat to stay off until temps:latest is valid and fresh (default stale threshold 15 s).
5. Development (off the Pi or dry-run)
cd src
PYTHONPATH=. python3 -m control --dry-run --once
PYTHONPATH=. python3 -m control --no-gpio # Redis live, GPIO faked (stack test with temp_sim + web)
PYTHONPATH=. python3 -m unittest control.tests.test_commands control.tests.test_safety \
control.tests.test_filter_policy control.tests.test_heat_fsm control.tests.test_redis_io -v
Dry-run uses /tmp/hot-tub-control.log and /tmp/hot-tub-control-state.json when system paths are not writable.
File locations
| Path | Purpose |
|---|---|
/etc/hot-tub/control.json |
Daemon config |
/var/lib/hot-tub/control-state.json |
Persisted modes / target / latched error |
/var/log/hot-tub/control.log |
Application log |
/etc/systemd/system/hot-tub-control.service |
systemd unit |
/etc/logrotate.d/hot-tub-control |
Log rotation |
Uninstall
sudo systemctl disable --now hot-tub-control
sudo rm /etc/systemd/system/hot-tub-control.service
sudo rm /etc/logrotate.d/hot-tub-control
sudo systemctl daemon-reload
Config and state under /etc/hot-tub and /var/lib/hot-tub are left in place unless you remove them manually.