Skip to content

Feat/ha integration support - #2

Merged
Lash-L merged 4 commits into
mainfrom
feat/ha-integration-support
Jul 7, 2026
Merged

Feat/ha integration support#2
Lash-L merged 4 commits into
mainfrom
feat/ha-integration-support

Conversation

@Lash-L

@Lash-L Lash-L commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

HarborCameraConfig gains cert_pem/key_pem fields so consumers can pass
certificate material directly instead of persisting private keys to
disk. cert_path/key_path/cert_dir are now optional; the config validates
that one complete pair is provided. Since ssl.SSLContext.load_cert_chain
only accepts file paths, PEM data is staged in a private temp directory
(0600 files) that is removed before build_ssl_context returns, so the
temp-file lifecycle is fully encapsulated in the library.

The ssl_context_cache is now keyed off the certificate material (SHA-256
of the PEM pair, or the path pair for path-based configs) via the new
get_ssl_cache_key(), so rotated credentials never reuse a stale context.

The test fixture in tests/data/certs.py is a throwaway self-signed cert
generated for the suite; it is excluded from detect-private-key.

Summary by CodeRabbit

  • New Features

    • Camera connections now support certificate material from either file paths or direct PEM values.
    • Added smoother connection status updates, reducing noisy disconnect/reconnect flapping.
    • Camera state values are now normalized for more consistent display and handling.
  • Bug Fixes

    • Improved handling of unexpected camera state values.
    • SSL contexts and connection state tracking are now more reliable across reconnects and shutdowns.

Lash-L added 3 commits July 6, 2026 07:54
HarborCameraConfig gains cert_pem/key_pem fields so consumers can pass
certificate material directly instead of persisting private keys to
disk. cert_path/key_path/cert_dir are now optional; the config validates
that one complete pair is provided. Since ssl.SSLContext.load_cert_chain
only accepts file paths, PEM data is staged in a private temp directory
(0600 files) that is removed before build_ssl_context returns, so the
temp-file lifecycle is fully encapsulated in the library.

The ssl_context_cache is now keyed off the certificate material (SHA-256
of the PEM pair, or the path pair for path-based configs) via the new
get_ssl_cache_key(), so rotated credentials never reuse a stale context.

The test fixture in tests/data/certs.py is a throwaway self-signed cert
generated for the suite; it is excluded from detect-private-key.
…eriod

Harbor cameras frequently cycle their TCP connection, which previously
caused on_connection_change to fire on every raw connect/disconnect and
forced consumers to run their own grace timers.

HarborMQTTClient now takes connection_grace_period (default 90 seconds,
exported as DEFAULT_CONNECTION_GRACE_PERIOD) and only reports stable
transitions: connects are reported immediately, while a disconnect is
held back and silently dropped if the client reconnects within the
window. Setting the grace period to 0 restores raw per-transition
reporting. An explicit stop() skips the grace window and reports the
disconnect immediately. The async callback signature is unchanged, and
client.connected still reflects the raw transport state.

SSL cache invalidation in the reconnect loop now goes through
get_ssl_cache_key() to match the material-keyed cache.
The device reports enum-ish fields in mixed/upper case (e.g. PLAYING,
GOOD). These are now normalized to stable lowercase strings before
landing in HarborDeviceState.values, so consumers no longer need to
lowercase them for enum options.

The documented value sets are exported from the package root:
SPEAKER_STATES (idle, muted, off, paused, playing, unknown) and
STREAM_QUALITIES (excellent, fair, good, poor, unknown). Values outside
these sets are still stored lowercased and logged as a warning once per
value, so the option lists can be extended when new values appear.
@Lash-L
Lash-L requested review from afgarcia86 and Copilot July 6, 2026 11:59
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Lash-L, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 47 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: d6120286-cb27-4d6d-b84d-0152da6e8f6d

📥 Commits

Reviewing files that changed from the base of the PR and between ebda385 and a992186.

📒 Files selected for processing (3)
  • harbor/config.py
  • harbor/mqtt.py
  • harbor/utils.py
📝 Walkthrough

Walkthrough

Introduces PEM-string or file-path based camera certificate configuration in HarborCameraConfig with validation, adds lowercase normalization for camera enum fields (speaker_state, stream_quality), reworks SSL context caching to key off certificate material, and adds debounced disconnect reporting to the MQTT client.

Changes

Camera cert/enum and MQTT debounce changes

Layer / File(s) Summary
Config PEM/path certificate support
harbor/config.py, tests/test_config.py
HarborCameraConfig now accepts either PEM strings or file paths for certs, with __post_init__ validation enforcing paired inputs and requiring at least one source; tests cover accept/reject scenarios.
SSL context and cache key from PEM/paths
harbor/utils.py, harbor/mqtt.py, tests/data/certs.py, tests/test_config.py
Adds get_ssl_cache_key and _write_private_file, updates build_ssl_context/get_ssl_context to support PEM material, and centralizes SSL cache invalidation in mqtt.py via _invalidate_ssl_cache.
Camera heartbeat enum normalization
harbor/devices/camera.py, harbor/__init__.py, tests/test_camera_state.py
Adds SPEAKER_STATES/STREAM_QUALITIES constants (re-exported from the package) and a _normalize_enum_value helper that lowercases and validates heartbeat enum fields, warning once per unexpected value.
MQTT debounced connection-change notifications
harbor/mqtt.py, tests/test_mqtt.py
Adds connection_grace_period param, debounces disconnect notifications via a grace-period task, and flushes pending disconnects on stop().
Pre-commit exclusion
.pre-commit-config.yaml
Excludes tests/data/certs.py from the detect-private-key hook.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Transport
  participant HarborMQTTClient
  participant GraceTask as _disconnect_after_grace
  participant Listener as on_connection_change

  Transport->>HarborMQTTClient: _set_connected(True)
  HarborMQTTClient->>Listener: notify connected=True
  Transport->>HarborMQTTClient: _set_connected(False)
  HarborMQTTClient->>GraceTask: schedule grace period task
  alt reconnect within grace period
    Transport->>HarborMQTTClient: _set_connected(True)
    HarborMQTTClient->>GraceTask: cancel pending task
  else grace period elapses
    GraceTask->>Listener: notify connected=False
  end
  HarborMQTTClient->>HarborMQTTClient: stop() cancels/flushes pending task
Loading
sequenceDiagram
  participant Caller
  participant get_ssl_context
  participant get_ssl_cache_key
  participant build_ssl_context

  Caller->>get_ssl_context: request SSL context for camera_config
  get_ssl_context->>get_ssl_cache_key: derive cache key from PEM or path material
  get_ssl_context->>build_ssl_context: build on cache miss
  build_ssl_context-->>get_ssl_context: SSLContext (from PEM temp files or paths)
  get_ssl_context-->>Caller: cached or newly built SSLContext
Loading

Poem

A rabbit hops through certs and keys,
PEM or path, whichever he sees 🐇
Enum values now lowercase and neat,
MQTT waits before crying "disconnect"! 🔌
Thump thump — the burrow's tests all pass,
Hooray for code as clean as grass! 🌿


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds in-memory PEM certificate support (to avoid requiring callers to persist private keys to disk) and updates MQTT/camera behavior to better support integrations (HA) via more stable connection-state signaling and normalized camera state values.

Changes:

  • Extend HarborCameraConfig to accept cert_pem/key_pem and validate that a complete certificate pair is provided.
  • Add SSL-context cache keying via certificate material (get_ssl_cache_key) and support building an SSLContext from PEM strings by staging into a private temp directory.
  • Add debounced MQTT connection-change notifications and normalize certain camera enum state fields to lowercase, exporting known-value sets.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/test_mqtt.py Adds tests for debounced connection-change reporting and stop-time disconnect flushing.
tests/test_config.py Adds tests for config validation, SSL context building from PEM, and cache key behavior.
tests/test_camera_state.py Adds tests ensuring camera enum state values are normalized and stable across updates.
tests/data/certs.py Provides throwaway self-signed cert/key PEM fixtures for the test suite.
harbor/utils.py Implements PEM-based SSL context build, private temp-file staging, and SSL cache keying by material.
harbor/mqtt.py Adds connection-change debouncing and updates SSL cache invalidation logic.
harbor/devices/camera.py Normalizes known enum state fields to lowercase and logs unexpected values once.
harbor/config.py Makes cert path fields optional, adds PEM fields, and validates certificate-material completeness.
harbor/init.py Re-exports SPEAKER_STATES and STREAM_QUALITIES.
.pre-commit-config.yaml Excludes the test key fixture from detect-private-key.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread harbor/config.py Outdated
Comment thread harbor/utils.py Outdated
Comment thread harbor/mqtt.py Outdated
- Rebuild the SSL context inside the reconnect loop so cache
  invalidations in the error handlers actually take effect on the next
  attempt instead of reusing the initial context forever; unchanged
  material remains a cheap cache hit.
- Write staged PEM temp files with explicit UTF-8 encoding and LF
  newlines to avoid platform-dependent newline translation on Windows.
- Correct the HarborCameraConfig docstring to accurately describe the
  short-lived temp-file staging instead of claiming PEM data is never
  written to disk.

@afgarcia86 afgarcia86 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.

Lgtm

@Lash-L
Lash-L merged commit 8e68dc0 into main Jul 7, 2026
8 checks passed
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.

3 participants