Unofficial companion viewer for cameras running the open-source thingino firmware.
日本語版は README.ja.md をどうぞ。
A tiny self-hosted web app — installable on your phone as a PWA — that gives you live view, pan/tilt control, push-to-talk and day/night switching for a thingino camera. One container, one config file. No cloud account, no subscription, no port forwarding.
[Phone PWA] ──HTTPS──▶ (optional Cloudflare Tunnel + Access)
│
[thingino-viewer container]
├─ bundled go2rtc … live video (WebRTC/MSE), talk backchannel
└─ viewer app … UI/PWA, PTZ & camera control over SSH
│ RTSP / SSH (LAN only)
[thingino camera]
- Live view — sub-second WebRTC on the LAN, MSE through tunnels; HD / SD / video-off selector to keep mobile data usage down (SD ≈ 2–4 MB/min)
- PTZ pad — hold-to-move with software end-stop protection (prevents the
motor desync that bricks tilt on cheap cameras until reboot) and a one-tap
recalibration button (
motors -r) - Push-to-talk — hold, speak, release; plays on the camera speaker via the RTSP backchannel (go2rtc + thingino audio daemon)
- Day / Night / Auto — switches the IR-cut mode over SSH
- PWA — "Add to Home Screen" gives a full-screen app; app name and UI
language (
en/ja) are configurable - Light — measured 43 MiB RAM / 0.3 % CPU on a Raspberry Pi 3B+ (1 GB) running alongside a VPN server; works on a Pi 3 / Zero 2 W, a NAS, or any amd64/arm64 Docker host
- A camera running thingino with RTSP enabled (
/ch0main,/ch1sub) and SSH reachable from the Docker host (same LAN) - For push-to-talk: the camera's audio output (speaker) enabled in thingino
- Docker + Compose on the host
mkdir thingino-viewer && cd thingino-viewer
curl -LO https://raw.githubusercontent.com/Amakata/thingino-viewer/main/docker-compose.example.yml
mv docker-compose.example.yml docker-compose.yml
docker compose up -d # first start writes ./config/config.json (template)
vi config/config.json # set camera_ip, RTSP & SSH credentials
docker compose restart thingino-viewerOpen http://<host>:8091. You should see live video; try the PTZ pad.
| Key | Meaning | Default |
|---|---|---|
app_name |
Name shown in the UI and on the phone home screen | thingino-viewer |
lang |
UI language: en or ja |
en |
camera_ip |
Camera address on your LAN | — |
camera_rtsp_user / camera_rtsp_pass |
RTSP credentials (thingino web UI → Streamer) | thingino |
ssh_user / ssh_pass |
Camera SSH login (root + your thingino password) | — |
webrtc_candidates |
e.g. ["192.168.1.20:8555"] — host LAN IP for low-latency WebRTC |
[] |
daynight_commands |
Shell commands used for Color / Mono / Auto | daynight … |
power_commands.reset |
Command behind the ⟳ Reset button | motors -r |
ptz.* |
See PTZ calibration below | Cinnado D1 defaults |
app_user / app_pass |
Optional HTTP Basic auth for the LAN port | off |
To use your own home-screen icon, mount PNGs over the defaults in
docker-compose.yml:
- ./my-icon-192.png:/app/static/icon-192.png (and the 512px one).
The recommended setup is Cloudflare Tunnel + Access: viewers just open a URL and enter a one-time email code — no VPN app, no Cloudflare account for them.
- Add a domain you own to Cloudflare (free plan)
- Zero Trust → Networks → Tunnels → Create a tunnel, copy the token
- Uncomment the
cloudflaredservice indocker-compose.yml, paste the token,docker compose up -d - Tunnel → Public Hostname:
cam.your.domain→ HTTP →thingino-viewer:8091 - Access → Applications → Self-hosted app for
cam.your.domain→ policyAllow/ Include Emails (list the viewers; include yourself). Set Session Duration to 1 month - Login method: add One-time PIN (Integrations → Identity providers →
Add → One-time PIN) and select only it for the app.
⚠️ By default only the "Cloudflare" member sign-in exists, which locks out everyone but you - On the phone: open
https://cam.your.domain, sign in with the email code, then Chrome menu → Add to Home Screen / Install app
Notes: the mic (push-to-talk) only works over HTTPS, i.e. through the tunnel or another TLS front. WebRTC does not traverse the tunnel — remote viewing uses MSE (1–3 s latency); LAN viewing stays low-latency.
Cheap pan/tilt cameras track position in motor steps. Driving into a physical end stop desyncs the counter and the axis stops responding until recalibration — thingino-viewer prevents this with software limits, but it needs to know your camera's geometry. Defaults match the Cinnado D1.
GET /api/ptz/infoshows the live position counter and detected rangex_max/y_max:-1= unlimited axis (e.g. 360° pan),0= try auto-detect frommotors -i, or a measured number (drive gently to the end, read/api/ptz/info, set that value)margin: how many steps before the physical end to stop (default 80)- One-tap direction test: read
ypos, tap ▼ once, read again — if the camera moved down and the counter increased, down is counter-positive (default); flipinvert_yif your arrows are reversed mirror_x/mirror_y: some thingino builds interpret absolute position commands mirrored (commandingTmoves to2·pos−T). If a single tap moves the camera correctly but the counter goes the opposite way to the command, set mirror for that axis (Cinnado D1:mirror_y: true)- Stuck anyway? Tap ⟳ Reset (runs
motors -r), or setpower_commands.resettorebootfor a full camera reboot
| Symptom | Check |
|---|---|
| No video | docker logs thingino-viewer — go2rtc child should be running; verify RTSP creds with VLC: rtsp://user:pass@cam:554/ch0 |
| PTZ moves the wrong way | flip ptz.invert_x / ptz.invert_y |
| Tilt dies at the end of travel | set a measured ptz.y_max, keep margin ≥ 60, use ⟳ Reset to recover |
| Talk button does nothing | HTTPS required for the mic; check thingino audio output volume; see the body field in the /api/talk response |
| "Install app" missing on Android | must be opened via HTTPS; reload once (the manifest is fetched with credentials for Access compatibility) |
| Day/night fails | run daynight on the camera over SSH and align daynight_commands with its usage |
Not affiliated with the thingino project — this is a community companion tool.
Video engine: go2rtc by @AlexxIT (MIT,
bundled — license text included in licenses/ and in the image at
/licenses). Remote access: cloudflared
(Apache-2.0, separate official container). See licenses/NOTICE.md
for all bundled third-party components.
MIT — see LICENSE.
