← All docs

Telycam manual control

A human-operated PTZ console for the Telycam Vision+ SE, paired with a command-history ledger for manual UI actions and machine API commands.
2026-05-13 In progress / RFC ~2–4 days Touches: Control intranet · local controller · Telycam VISCA/IP · command history

How to read this doc

Sections 1–3 define the operator UX. Sections 4–5 are the implementation contract: the local controller and command-history model. Section 8 is the phased build sequence. Section 11 is the paste-ready fresh-session prompt for the next implementation session. Markdown source: docs/devtasks/telycam-manual-control.md.

Contents

  1. Context
  2. Goal
  3. Product surface
  4. Controller architecture
  5. Command-history model
  6. UI behavior and ergonomics
  7. Security and safety
  8. Build sequence
  9. Verification
  10. Open questions
  11. Fresh-session implementation prompt
  12. Build journal

1. Context

The Sponic hardware inventory now tracks the Telycam Vision+ SE and links both the official manual and the network-control integration notes. The codebase also has the beginning of a Node-side Telycam integration in apps/control/src/lib/telycam/.

What is missing is an operational surface. Today, a human cannot open the intranet and manually steer the camera, recall presets, toggle tracking, or see what a script/agent has recently sent to the camera. That makes live testing hard and makes automation risky: if a machine command moves the camera, there is no first-class audit trail explaining what happened.

2. Goal

Build a manual Telycam control page in the intranet that supports human PTZ operation and shows a command history for both manual UI actions and machine API commands.

The page should feel like an operator console, not a marketing page. The first screen should be the working control surface: connection status, movement controls, presets, tracking/tally controls, command queue status, and recent commands.

3. Product surface

The first version should add a Telycam page under the intranet Devices area, or a focused detail action from the Telycam hardware entry.

  1. Connection panel — selected camera, IP/host, protocol, controller status, last heartbeat, and read-only hints when the local controller is offline.
  2. PTZ controls — directional pad, home, stop, speed controls for pan/tilt, zoom in/out, focus mode, focus near/far when manual focus is enabled.
  3. Presets — recall preset, save preset, label presets with human names, and mark safe defaults such as Home, Wide room, and Speaker close-up.
  4. Tracking and tally — toggle auto-tracking, set tally off/red/green, and show last known state.
  5. Command history — chronological feed of commands with actor, source, command type, arguments, transport, status, duration, response/error, and correlation id.
  6. Safety affordances — obvious stop button, disabled state when no camera is connected, and confirmation for destructive operations like overwriting a preset.

4. Controller architecture

The deployed Control app is statically exported, so it cannot send raw LAN camera packets from a hosted Next.js route. Camera control must go through a local controller process running on the same network as the Telycam.

Control intranet UI -> local controller API on LAN -> apps/control/src/lib/telycam client -> VISCA-over-IP / camera web endpoint

The controller should expose a narrow command API rather than arbitrary network access. The UI sends structured commands such as ptz.move, ptz.stop, preset.recall, preset.save, tracking.set, and tally.set. The controller validates arguments, sends the camera command, records the result, and returns a structured response.

5. Command-history model

Every command sent to the camera should produce a durable history record. The history is not only for the manual UI; it is the audit layer for future machine control, AI operators, scripts, and scheduled scenes.

FieldPurpose
idStable command id / correlation id.
camera_idHardware inventory id or stable camera slug.
actor_typehuman, system, agent, or script.
sourcemanual-ui, api, automation, test, etc.
commandCanonical command name such as ptz.move.
argsJSON arguments after validation.
statusqueued, sent, succeeded, failed, or timed-out.
result / errorNormalized response or error details.

For v1, storage can be local SQLite or Supabase depending on where the controller runs. The contract matters more than the final backing store: the UI should consume a command-history API that can later move without changing the operator surface.

6. UI behavior and ergonomics

The page should be built for repeated use during setup and live operations. Controls should be dense, stable, and hard to mis-click. Use icon buttons for directional movement and stop. Numeric speed should use a slider or stepper. Preset labels should be editable inline or in a small modal.

The command history should support filtering by command type, actor/source, status, and time window; expanding a row to see arguments and raw response/error details; copying a command as JSON; re-running safe commands with confirmation; and highlighting commands issued outside the manual UI.

7. Security and safety

  1. Only authorized intranet users can use the manual control page.
  2. The local controller accepts commands only from trusted origins or via a shared token configured outside the repo.
  3. Commands must be validated against an allowlist.
  4. A ptz.stop command should be available even when other controls are in a partially failed state.
  5. Machine-originated commands should identify their source clearly in the history feed.

8. Build sequence

Phase 1 — Controller contract

Define command names, request/response schemas, and history-event shape in shared files such as apps/control/src/lib/telycam/control-contract.ts and apps/control/src/lib/telycam/command-history.ts. Keep these importable by both the local controller and static UI.

type TelycamCommandName =
  | "ptz.move"
  | "ptz.stop"
  | "ptz.home"
  | "zoom.set"
  | "zoom.step"
  | "focus.setMode"
  | "focus.step"
  | "preset.recall"
  | "preset.save"
  | "tracking.set"
  | "tally.set";

Phase 2 — Local controller skeleton

Create a LAN-local Node process with GET /health, GET /cameras, GET /cameras/:cameraId/status, POST /cameras/:cameraId/commands, and GET /cameras/:cameraId/commands. Start with no-op command recording.

Phase 3 — Real Telycam command execution

Wire stop, home, movement, zoom, preset recall/save, tally, and tracking. Treat ptz.stop as highest priority, clamp all speeds, validate preset numbers, and record commands before and after execution.

Phase 4 — Manual control UI

Add compact operator components for status, PTZ controls, presets, tracking/tally, and offline states. Likely files include telycam-control-panel.tsx, telycam-command-history.tsx, telycam-presets.tsx, and telycam-status.tsx.

Phase 5 — Command history UI

Render recent history with visible time, actor/source, command, args summary, status, and duration. Expanded rows should show full JSON request, normalized result, raw response/error, correlation id, and transport details.

Phase 6 — Live-network test and hardening

Test against the real camera: home, directional movement, stop, zoom, presets, tracking, tally, and history completeness. Do not trust the page for live operations until physical stop behavior is verified.

9. Verification

  1. A human can move, stop, zoom, recall preset, save preset, toggle tracking, and set tally from the intranet UI.
  2. The UI fails closed when the local controller or camera is offline.
  3. Every manual command creates a history record.
  4. Every machine/API command creates a history record with source attribution.
  5. The history view can filter and expand commands without losing debugging detail.
  6. A physical stop test confirms that ptz.stop is reliable under repeated movement commands.

10. Open questions

  1. Which host should run the first local controller: ALPUCA, the Telycam-adjacent Mac, or another always-on LAN machine?
  2. Should command history live first in local SQLite, Supabase, or both?
  3. Do we need a live preview tile in v1, or is control/status/history enough?
  4. Should machine-originated commands require a separate permission role from human manual control?

11. Fresh-session implementation prompt

Paste this into the implementation session

The full prompt below is the intended handoff. It tells the next session what to read, what to build first, and what safety constraints matter.

We are implementing docs/devtasks/telycam-manual-control.md in the Sponic monorepo.

Goal: build a manual Telycam Vision+ SE control UX in the Control intranet and a
command-history/audit layer that records both human UI actions and machine/API
commands sent to the camera.

Read first:
- AGENTS.md
- docs/devtasks/telycam-manual-control.md
- apps/control/src/lib/telycam/
- apps/control/src/components/hardware/
- apps/control/src/components/devices/ or the current devices/hardware routing
- infra/runbook.md Cloudflare/Pages notes only if deploy behavior is relevant

Implementation constraints:
- The deployed Control app is statically exported. Do not put LAN camera control
  in a hosted Next.js API route.
- Add a local controller contract first. The UI talks to a local LAN controller;
  the local controller talks to the Telycam over VISCA/IP or other supported LAN
  protocol.
- Every camera command must create a command-history record, including failed
  commands and commands issued by scripts/agents.
- ptz.stop must remain available and prioritized.
- Keep UI operational and compact: no landing page, no hero, no decorative cards.
- Use existing Control design patterns and types.

Suggested first phase:
1. Create the shared Telycam command/request/history types.
2. Add a local-controller skeleton or mock API adapter that can record no-op
   commands.
3. Build the UI against that contract with realistic mock data.
4. Then wire real LAN execution and live testing.

Acceptance:
- A human can operate PTZ, zoom, presets, tracking, and tally from the intranet
  when a local controller is available.
- The UI clearly shows offline/disabled states when it is not available.
- Manual and machine commands appear in one command history feed with actor,
  source, status, args, duration, result/error, and correlation id.
- Live-network testing confirms stop behavior and history completeness.

Build journal

2026-05-13 · Codex · phase 6 latency hardening · codex/telycam-fast-path-600

Implemented the AA14 ttran latency review plan. Interactive manual commands (ptz.move, ptz.stop, zoom.step, and focus.step) now request a direct-redpanda transport mode from the Control UI. The Supabase telycam-control Edge Function proxies those commands to the configured Redpanda controller when TELYCAM_CONTROLLER_URL and TELYCAM_CONTROLLER_TOKEN are present, then upserts the returned command record into public.telycam_command_queue so direct commands still appear in the shared history. Durable commands keep using the existing queue path.

The Redpanda controller now gives ptz.stop priority, supersedes stale queued ptz.move commands before sending them, uses a persistent Node TCP socket for VISCA-over-TCP instead of spawning nc per command, and appends history records without rewriting the whole JSONL file. The UI now defaults the preview to the lower-latency telycam-low WebRTC stream and refreshes camera command status every 2 seconds.

Next step: deploy the Edge Function and restarted Redpanda service, then verify fast-path ptz.stop request wall time under 500 ms on the live page.

2026-05-11 · Codex · phases 1-5 · main

Implemented the shared Telycam command contract, browser-facing command-history model, local LAN controller script, and the Control intranet manual operator page under Devices → Telycam. The UI exposes connection status, selected camera, PTZ hold-to-move controls, stop/home, zoom, focus, presets, tracking, tally, and an expandable/filterable command history.

The local controller runs from apps/control with npm run telycam:controller. It defaults to dry-run history capture and sends real VISCA-over-IP packets when TELYCAM_CAMERA_HOST is configured.