Turn a Raspberry Pi + camera into a standalone, web-controlled action / underwater camera. Everything is controlled from a phone browser over the Pi's local network — no screen, no buttons, no buzzer, no internet required.
ActionPi is built for the one thing that makes underwater use hard: the connection drops all the time, and that's fine. Recordings run entirely on the Pi, independent of your phone. If you walk out of Wi-Fi range mid-timelapse, the browser keeps estimating the time remaining and shows a disconnected badge; when you reconnect it re-syncs to the real camera state. Refreshing or reopening the page never starts a second recording.
- Photo, Video and Timelapse capture, started from the web UI.
- Non-blocking: starting a capture returns instantly; the camera runs in the background. Only one capture runs at a time, and starting is idempotent.
- Connection-loss friendly: live countdown of remaining recording time that keeps running while offline and re-syncs on reconnect.
- Gallery: browse photos, videos and timelapse folders. View full-size, play videos, step through timelapse frames, download or delete — individually or multi-select.
- Fast downloads: single files stream directly; folders and multi-selections stream as an uncompressed zip on the fly — no re-zipping, no temp files, easy on a Pi Zero.
- Self-contained: no CDNs. All CSS/JS is served from the Pi so it works with zero internet access.
- System status: battery level (UPS-Lite HAT), CPU temperature and disk space; shutdown / reboot buttons.
- Built-in Debug screen: a Debug button opens live diagnostics (camera, battery, Wi-Fi, system) and a streaming log console right in the browser, so you can see exactly what's happening — and why, say, the battery isn't reporting — without SSH.
- Battery monitor: reads percentage + voltage from a UPS-Lite fuel gauge over I2C and estimates charging / discharging.
- Wi-Fi self-healing (optional): disables Wi-Fi power-save and auto-reconnects if the Pi's Wi-Fi client link drops.
- A Raspberry Pi with Wi-Fi (a Pi Zero W works).
- A camera module supported by
libcamera(libcamera-still/libcamera-vid). - (Optional) a UPS-Lite V1.3 battery HAT for the battery indicator.
No GPIO wiring, sensors or buzzer needed.
Clone onto the Pi and install the dependencies:
git clone https://github.com/jakobkreft/ActionPi.git
cd ActionPi
# System camera + video tools (usually already present on Raspberry Pi OS)
sudo apt-get update
sudo apt-get install -y libcamera-apps ffmpeg python3-flask python3-pil
# Optional: battery monitoring on the UPS-Lite HAT
sudo apt-get install -y python3-smbus
# Or, if you prefer pip:
# pip3 install -r requirements.txtMedia is stored under ~/camera by default (photos/, videos/, timelapses/,
thumbnails/). Override with the ACTIONPI_DIR environment variable.
The battery indicator reads a UPS-Lite fuel gauge over I2C, so enable I2C once:
sudo raspi-config # Interface Options -> I2C -> EnableIf no UPS-Lite / I2C is present the app simply hides the battery indicator.
Set ACTIONPI_WIFI_WATCHDOG=1 (already set in actionpi.service) to have ActionPi
disable Wi-Fi power-save and automatically reconnect the Pi's Wi-Fi client link if
it drops. It uses passwordless sudo for a few network commands (default on Raspberry
Pi OS).
Only use this when the Pi connects to an existing Wi-Fi network. Do not enable it if the Pi hosts its own hotspot — it would bounce the connection your phone is on.
python3 server.pyThen open http://<pi-ip>:8000 from your phone on the same network (or the Pi's
Wi-Fi hotspot).
If
libcamera-stillisn't installed (e.g. you're testing on a laptop), ActionPi automatically runs in mock mode and generates placeholder media so you can try the whole interface without a camera.
An actionpi.service unit is included. Edit the User, WorkingDirectory and paths
if your username isn't pi, then:
sudo cp actionpi.service /etc/systemd/system/actionpi.service
sudo systemctl daemon-reload
sudo systemctl enable --now actionpi.serviceCheck logs with journalctl -u actionpi -f.
The Shutdown / Reboot buttons call sudo shutdown / sudo reboot. On Raspberry Pi OS
the default user already has passwordless sudo; otherwise add a sudoers rule for those
two commands.
Tap Debug at the bottom of the page (or open it directly) to see:
- Diagnostics cards for camera, battery, Wi-Fi and system, each with a status
badge. The Battery card shows precisely why it may not report — e.g.
smbus_module: —(installpython3-smbus),i2c_device_present: no(enable I2C), orreads_failedwith the exact error (UPS-Lite not detected; checki2cdetect -y 1). - A live log console with level filtering (Debug/Info/Warnings/Errors). Unhandled server errors appear here in red, with their full traceback.
The same data is available as JSON at /api/debug and /api/logs, and the full log
downloads from /api/logs/download. Everything is also written to the console
(journalctl -u actionpi -f) and to a rotating actionpi.log in the media directory.
| Part | File |
|---|---|
| Camera controller — background capture, state persistence, crash recovery | camera.py |
| Web server — REST API, media listing, streaming downloads, system status | server.py |
| Central logging — ring buffer + rotating file, shared by all modules | logsetup.py |
| Battery monitor — UPS-Lite fuel gauge over I2C, with diagnostics | battery.py |
| Wi-Fi reconnect watchdog | wifi.py |
| Single-page web UI (offline-resilient) with Debug screen | templates/index.html, static/ |
The browser polls GET /api/status, which returns the camera state together with the
server clock and the recording's ends_at timestamp. The UI uses these to count down
locally, so the estimate stays correct even with no connection.
