Skip to content

Release v2: Async client, TLS enforcement, retries/backoff, and CI upgrades#3

Merged
devinslick merged 28 commits intomainfrom
v2-device-client
Nov 4, 2025
Merged

Release v2: Async client, TLS enforcement, retries/backoff, and CI upgrades#3
devinslick merged 28 commits intomainfrom
v2-device-client

Conversation

@devinslick
Copy link
Copy Markdown
Owner

Summary

This major release delivers a production-ready async client with robust TLS handling, resilient request logic (timeouts, retries, rate limits), and a fully wired CI pipeline (lint, type-check, tests, coverage). It replaces the legacy synchronous v1 client and prepares the project for Home Assistant and broader integration.

Highlights

  • Async client
    • New FmdClient with an async factory (await FmdClient.create(...)) and async context manager (async with ...).
    • Clean session lifecycle management and connection pooling controls.
  • TLS/SSL
    • HTTPS-only base_url enforcement (rejects plain HTTP).
    • Configurable validation: ssl=False (dev only) or pass a custom ssl.SSLContext.
    • Certificate pinning example included in docs.
  • Reliability
    • Timeouts applied across all HTTP requests.
    • Retries with exponential backoff and optional jitter for 5xx and connection errors.
    • 429 rate-limit handling with Retry-After support.
    • Safe behavior for command POSTs (no unsafe retries).
  • Features
    • Client-side export to ZIP (locations + pictures).
    • Device helper with convenience actions (e.g., request location, play sound, take picture).
  • Safety and ergonomics
    • Sanitized logging (no sensitive payloads); token masking helper.
    • Typed package (py.typed) and mypy-clean.
  • CI/CD
    • GitHub Actions: lint (flake8), type-check (mypy), unit tests matrix (Ubuntu/Windows; Py 3.8–3.12).
    • Coverage with branch analysis and Codecov upload + badges.
    • Publish workflows for TestPyPI (non-main) and PyPI (main or Release).

Breaking changes

  • API surface moved to async:
    • Before (v1, sync): client = FmdApi(...); client.authenticate(...); client.get_all_locations()
    • Now (v2, async):
      • client = await FmdClient.create(base_url, fmd_id, password, ...)
      • await client.get_locations(...), await client.get_pictures(...), await client.send_command(...)
      • Prefer: async with FmdClient.create(...) as client: ...
  • Transport requirements:
    • base_url must be HTTPS; plain HTTP raises ValueError.
  • Python versions:
    • Targets Python 3.8+ (3.7 removed from classifiers).

Migration guide

  • Replace FmdApi usage with the async FmdClient:
    • Use await FmdClient.create(...) and async with for safe resource management.
    • Update all calls to await the async methods.
  • TLS and self-signed setups:
    • For dev-only scenarios: pass ssl=False.
    • For proper self-signed use: pass a custom ssl.SSLContext.
    • For high-security setups: consider the certificate pinning example in README.
  • Connection tuning:
    • Optional: conn_limit, conn_limit_per_host, keepalive_timeout on the TCPConnector via client init.

Error handling and semantics

  • 401 triggers one automatic re-authenticate then retries the request.
  • 429 honors Retry-After, otherwise uses exponential backoff with jitter.
  • Transient 5xx/connection errors are retried (except unsafe command POST replays).
  • Exceptions are normalized to FmdApiException where appropriate; messages mask sensitive data.

Documentation and examples

  • README: TLS/self-signed guidance, warnings, and certificate pinning example.
  • Debugging: pin_cert_example.py demonstrates secure pinning and avoids CLI secrets.

CI/CD and release automation

  • Tests: unit suite expanded; functional tests run when credentials are available.
  • Coverage: ~83% overall; XML + branch coverage uploaded to Codecov (badges included).
  • Workflows:
    • test.yml: runs on push/PR for all branches (lint, mypy, unit tests matrix, coverage, optional functional tests).
    • publish.yml: builds on push/releases; publishes to TestPyPI for non-main pushes, PyPI on main or release.

Checklist

  • All unit tests pass
  • Flake8 clean
  • Mypy clean
  • Coverage collected and uploaded
  • README/docs updated (TLS, pinning, badges)
  • Packaging: sdist and wheel built; publish workflows configured

Notes

  • Use ssl=False sparingly and never in production.
  • Consider adding Dependabot and security scanning in a follow-up.
  • A CHANGELOG.md entry for 2.0.0 is recommended if not already included.

BREAKING CHANGE: v2 switches to an async client, enforces HTTPS, and drops Python 3.7; synchronous v1 usage must be migrated as noted above.

Critical fixes for Home Assistant integration:
- Add configurable HTTP request timeouts (default 30s) to all requests
- Fix version format inconsistency (_version.py now uses PEP 440 dot notation)
- Add py.typed marker file for PEP 561 compliance
- Reimplement export_data_zip for client-side packaging (no server endpoint)
- Extract pictures as actual image files with format detection
- Remove redundant encrypted blobs from exports

Changes:
- FmdClient: Add timeout parameter to __init__, create(), and _make_api_request()
- Export: Fetch locations/pictures via existing APIs, decrypt and package into ZIP
- Export: Include info.json, locations.json, pictures/*.jpg with manifest
- Tests: Add test_timeout_configuration() and update export tests
- Documentation: Update HOME_ASSISTANT_REVIEW.md with fix details
Copilot AI review requested due to automatic review settings November 4, 2025 22:37
@codecov-commenter
Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment

Thanks for integrating Codecov - We've got you covered ☂️

@devinslick devinslick changed the title Release v2.0.0: Async client, TLS enforcement, retries/backoff, and CI upgrades Release v2.0.1: Async client, TLS enforcement, retries/backoff, and CI upgrades Nov 4, 2025
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces fmd_api v2.0.1, a major refactoring from a monolithic module to a device-oriented async client architecture with comprehensive test coverage and CI/CD pipeline.

Purpose: Complete rewrite of the FMD API client with async/await support, device-oriented design, improved error handling, retry logic, and production-ready features for Home Assistant integration.

Key Changes:

  • Migrated from synchronous fmd_api.py module to async FmdClient and Device classes
  • Added 1,782 lines of unit tests with 82.6% coverage
  • Implemented HTTP retry logic with exponential backoff and rate limit handling
  • Added functional test suite with credential management utilities
  • Set up CI/CD with GitHub Actions (test, lint, type check, codecov integration)

Reviewed Changes

Copilot reviewed 52 out of 55 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
fmd_api/client.py New 718-line async client with authentication, retry logic, timeout handling
fmd_api/device.py New 149-line Device class wrapping per-device operations
fmd_api/models.py Data models for Location and PhotoResult
fmd_api/exceptions.py Typed exception hierarchy
tests/unit/test_client.py 1,029 lines of comprehensive unit tests for FmdClient
tests/unit/test_device.py 753 lines of unit tests for Device class
tests/functional/ 6 functional test scripts with credential utilities
pyproject.toml Updated to v2.0.1 with proper package configuration
.github/workflows/test.yml CI pipeline for lint, type check, and tests
setup.py Removed (replaced by pyproject.toml)
fmd_api.py Removed (replaced by package structure)
fmd_client.py Removed (functionality migrated to new structure)

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

Comment thread tests/utils/read_credentials.py Outdated
Comment thread pyproject.toml
{name = "Devin Slick", email = "fmd_client_github@devinslick.com"},
]
description = "A Python client for the FMD server API"
version = "2.0.1"
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

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

Version 2.0.1 suggests this is a patch release, but the PR description and changes indicate this is a major breaking rewrite (v1 to v2). Consider whether this should be 2.0.0 instead, as .1 typically indicates a bugfix on top of a stable 2.0.0 release. If 2.0.0 was already released, this is fine; otherwise, use 2.0.0 for the initial v2 release.

Copilot uses AI. Check for mistakes.
Comment thread docs/MIGRATE_FROM_V1.md Outdated
Comment thread tests/functional/test_locations.py Outdated
Comment thread tests/functional/test_locations.py Outdated
@devinslick devinslick changed the title Release v2.0.1: Async client, TLS enforcement, retries/backoff, and CI upgrades Release v2: Async client, TLS enforcement, retries/backoff, and CI upgrades Nov 4, 2025
@devinslick devinslick merged commit 9e231b8 into main Nov 4, 2025
24 of 25 checks passed
@devinslick devinslick deleted the v2-device-client branch November 4, 2025 23:03
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