Skip to content

feat(camera): Add camera signaling models, ICE parsing utilities, and payload serialization - #981

Open
Lash-L wants to merge 3 commits into
Python-roborock:mainfrom
Lash-L:camera-signaling
Open

Lash-L wants to merge 3 commits into
Python-roborock:mainfrom
Lash-L:camera-signaling

Conversation

@Lash-L

@Lash-L Lash-L commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements PR 2 of the decoupled Roborock vacuum live camera streaming series (following #980, #764, #738).

This PR introduces pure Python data structures, RFC-compliant ICE candidate parsing/formatting utilities, password hashing, and WebRTC signaling serializers for Roborock camera live streaming. It adds zero external runtime dependencies (aiortc is deferred to PR 4).

Scope & Components

  1. Camera Signaling Models:

    • CameraQuality (StrEnum: HD, SD).
    • CameraTurnServer dataclass (urls, username, credential with repr=False for security; includes from_dict helper validating TURN/STUN schemes).
    • CameraIceCandidate dataclass (encapsulates foundation, component, protocol, priority, ip, port, candidate_type, related_address, related_port, tcp_type, sdp_mid, sdp_mline_index, and arbitrary SDP extensions).
  2. RFC-Compliant ICE Candidate Parser & Formatter:

    • parse_ice_candidate: RFC 5245 / 8839 / 8445 compliant parser for SDP candidate attribute lines (candidate:...).
      • Supports IPv4, IPv6, and browser mDNS (.local) addresses.
      • Supports RFC 6544 TCP types (active, passive, so).
      • Validates component ranges (1 <= component <= 256), priority bounds (1 <= priority <= 2^31 - 1), and foundation syntax.
      • Preserves unrecognized SDP extensions (ufrag, generation, network-id).
    • Formatter methods: CameraIceCandidate.to_sdp() and to_signaling_dict().
  3. Security & Password Hashing:

    • hash_camera_password: Derives the lowercase hexadecimal MD5 digest of numeric camera PIN/pattern digits with strict ASCII validation and FIPS-compliant usedforsecurity=False.
  4. WebRTC Payload Serialization:

    • encode_sdp_offer & decode_sdp_answer: Compact base64-encoded UTF-8 JSON packaging for send_sdp_to_robot / get_device_sdp, handling "retry" pending states while preserving SDP CRLF line integrity.
    • encode_ice_candidate & decode_ice_candidates: Base64-encoded UTF-8 JSON packaging for send_ice_to_robot / get_device_ice, with schema validation and optional ignore_invalid resilience for remote candidate streams.
  5. Re-exports & Packaging:

    • Exposed under roborock.camera_signaling, roborock.data.camera, and roborock.data.

Validation

  • Unit tests: 37 tests covering happy paths, RFC compliance, boundary checks, edge cases, malformed payloads, and re-exports (uv run pytest tests/test_camera_signaling.py).
  • Code Quality: Passes uv run ruff check and uv run ruff format --check.
  • Typing: Passes uv run mypy.
  • Review: Refined through automated review iterations (Codex sol 6.1 and Claude opus 5.5).

@allenporter allenporter left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated code review by Gemini 4 Argon in Antigravity using the /review skill (.agents/skills/review/SKILL.md / AGENTS.md).

Summary

Great work breaking the camera livestream work into focused, zero-dependency increments! The RFC 5245/8839/8445 ICE candidate parsing, repr=False credential redaction on CameraTurnServer, and test coverage are well thought out.

Below are a few architectural and typing items to align with our repository conventions and conformance suite:

  1. Priority 1 — Module Placement & RoborockBase Conformance: Defining data models in top-level roborock/camera_signaling.py and re-exporting them in roborock/data/camera.py causes tests/conformance/test_model_conformance.py (discover_dataclasses(roborock.data)) to skip them because cls.__module__ does not start with roborock.data. As a result, CameraIceCandidate does not subclass RoborockBase. Defining the data models directly in roborock/data/ (or roborock/data/v1/) and placing the V1 signaling codecs in roborock/protocols/ keeps the top-level package clean and ensures conformance test coverage.
  2. Priority 1 — Trust Type Annotations & Concrete Signatures: Several functions perform defensive isinstance checks on statically typed parameters (CameraQuality.from_str, parse_ice_candidate, hash_camera_password, encode_ice_candidate), and outgoing encoders accept loose dict[str, Any] union inputs rather than concrete types.
  3. Priority 3 — Linear Wire Decoding in decode_ice_candidates: decode_ice_candidates mixes wire payload decoding (get_device_ice) with raw SDP string parsing via an item_str.startswith("ey") heuristic.

Comment thread roborock/camera_signaling.py Outdated
Comment thread roborock/camera_signaling.py Outdated
Comment thread roborock/camera_signaling.py Outdated
Comment thread roborock/camera_signaling.py Outdated
Comment thread roborock/camera_signaling.py Outdated
Comment thread roborock/camera_signaling.py Outdated
Comment thread roborock/camera_signaling.py Outdated
@Lash-L
Lash-L requested a review from allenporter October 22, 2026 00:28
@Lash-L

Lash-L commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

All should be fixed!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants