Skip to content

Add multi-camera support - #433

Open
KapJI wants to merge 3 commits into
nlef:developmentfrom
KapJI:multi-camera
Open

Add multi-camera support#433
KapJI wants to merge 3 commits into
nlef:developmentfrom
KapJI:multi-camera

Conversation

@KapJI

@KapJI KapJI commented Mar 30, 2026

Copy link
Copy Markdown
Contributor

This is the first, intentionally small PR extracted from the larger multi-camera branch. The original branch also included status albums, notification albums, timelapse changes, and unfinished-lapse recovery, but those parts were removed from this PR and kept for follow-up PRs to make the review more manageable. This PR only adds the configuration and runtime plumbing needed to construct multiple cameras, then wires that into /video camera selection.

What changes

Multi-camera configuration

Existing single-camera configs keep working: [camera] is still the default camera.

Additional cameras can be configured with named sections:

[camera]
host: http://front-cam/stream
type: mjpeg

[camera bed]
host: http://bed-cam/stream
type: mjpeg

ConfigWrapper now exposes:

  • cameras: all parsed camera configs, keyed by camera name.
  • default_camera: the first configured camera, or None when no camera is configured.
  • status_cameras / timelapse_cameras: filtered camera config maps. This PR still keeps status notifications and timelapse single-camera, but they now pick the first available runtime camera from the corresponding filtered list.

[camera default] is rejected and ignored. The default camera remains [camera]; a named section called default would otherwise be ambiguous and could shadow the real default.

Camera sections without a host are parsed but no runtime Camera object is created for them. This lets users keep disabled/incomplete camera sections in config without making startup fail.

The old internal ConfigWrapper.camera compatibility alias is removed. Remaining single-camera consumers use selected runtime Camera objects instead. The existing config key remains fourcc, but the code exposes it as video_codec on CameraConfig / Camera because timelapse assembly reads it from the selected camera.

Camera construction

camera.py now has a create_camera(...) factory. It centralizes backend selection and skips unavailable cameras:

  • mjpeg, ffmpeg, and raw_stream are constructed from their own camera config.
  • opencv is skipped when OpenCV is not available.
  • disabled or host-less camera configs return None.

This replaces the single camera_wrap global in main.py with a cameras: dict[str, Camera] runtime map.

Single-camera code paths are preserved by passing one selected camera into existing Timelapse and Notifier constructors. For each path, the bot picks the first configured use_for_timelapse / use_for_status camera that was successfully constructed at runtime. Those constructors now accept Camera | None, so a no-camera config can start cleanly and fall back to text-only notifications/status where needed.

/video selection

/video keeps the old behavior when exactly one camera is available.

With multiple cameras:

  • /video shows an inline camera-selection keyboard.
  • /video <name> records from the named camera directly.
  • Confirmation callbacks include the selected camera name, so the confirmation step records from the same camera the user chose.

When no camera is available, /video replies with No camera is configured. Stale camera-selection callbacks also produce a user-facing message instead of only logging.

What this PR does not include

These pieces were present in the larger branch, but are intentionally removed from this PR and will be sent separately:

  • Multi-camera /status albums.
  • Multi-camera notifier albums during printing.
  • Multi-camera timelapse capture and assembly.
  • Unfinished-lapse recovery changes for multi-camera timelapse layouts.

Keeping those out makes this PR a small foundation layer rather than another all-in-one feature branch.

Test plan

Coverage includes:

  • Single [camera] config still registers as the default camera.
  • Multiple [camera <name>] sections are parsed and keyed by name.
  • [camera default] reports a parsing error and is ignored, both standalone and when [camera] is also present.
  • Missing [camera] section produces no runtime default camera.
  • fourcc is exposed internally as video_codec, and ConfigWrapper.camera is no longer present.
  • Runtime camera creation skips host-less configs and skips OpenCV when the dependency is unavailable.
  • /video shows camera-selection buttons with multiple cameras.
  • /video without available cameras reports that no camera is configured.
  • Notifications without an available camera fall back to text messages and do not call the photo path.

Implements #51

@KapJI
KapJI marked this pull request as draft March 30, 2026 09:02
@github-project-automation github-project-automation Bot moved this to Long-Term suggestions in main development Mar 30, 2026
@nlef nlef added documentation Improvements or additions to documentation Feature Request New feature or request Accepted suggestion A good suggestion for future versions labels Mar 30, 2026
@KapJI
KapJI force-pushed the multi-camera branch 4 times, most recently from 4de3d14 to 2fb2778 Compare April 8, 2026 15:04
Comment thread bot/notifications.py Outdated
self._status_message = sent_messages[0]
for photo in photos:
photo.seek(0)
await message.update_existing_media_group(sent_messages, photos)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like an update to a media group I just sent.
Perhaps this is unnecessary?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea here is that initial media group post doesn't have reply_markup to show Update button, I'm testing if it can be added by update. I'm still testing this.

@KapJI
KapJI marked this pull request as ready for review April 8, 2026 23:40
@KapJI
KapJI marked this pull request as draft April 9, 2026 07:46
@KapJI
KapJI marked this pull request as ready for review April 27, 2026 23:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Accepted suggestion A good suggestion for future versions documentation Improvements or additions to documentation Feature Request New feature or request

Projects

Status: Long-Term suggestions

Development

Successfully merging this pull request may close these issues.

2 participants