Skip to content

Releases: devinslick/fmd_api

v2.0.7

22 Nov 19:49
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

Full Changelog: 2.0.6...2.0.7

v2.0.6

22 Nov 19:20
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

Release v2.0.6

Overview

This release focuses on code quality, stability, and developer experience. We have implemented Phase 1 of our Strict Typing Enforcement Plan, ensuring the core library is fully type-safe. Additionally, we have achieved 100% test coverage, ensuring robust handling of edge cases and deprecated methods.

Key Changes

🛡️ Strict Typing (Phase 1)

  • Full Type Safety: The fmd_api core library (client.py, device.py, models.py) now passes strict mypy checks.
  • Improved Signatures: Public methods now have precise return types and generic type hints (e.g., List[Dict[str, Any]]), improving IDE autocompletion and static analysis for consumers.
  • Configuration: Updated pyproject.toml to enforce strict typing rules (disallowing untyped definitions and implicit optionals).

🧪 Test Coverage Improvements

  • Full Coverage: Increased overall test coverage to 100%.
  • Edge Case Handling: Added comprehensive tests for Location parsing, including scenarios with missing dates or invalid inputs.
  • Deprecation Verification: Added specific tests to ensure deprecated methods in Device (like take_front_photo, fetch_pictures) continue to function correctly while emitting appropriate warnings.
  • Command Sanitization: Verified that Device.lock() correctly sanitizes and truncates messages.

📚 Documentation

  • Roadmap: Added docs/strict_typing_enforcement_plan.md outlining the roadmap for maintaining type safety.

Upgrade Guide

This release is fully backward compatible with v2.0.5. No code changes are required for consumers, though you may notice improved type checking results if you use mypy in your own projects.

v2.0.5

19 Nov 02:24
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

Release v2.0.5

Overview

This release focuses on code quality, stability, and developer experience. We have implemented Phase 1 of our Strict Typing Enforcement Plan, ensuring the core library is fully type-safe. Additionally, we have significantly improved test coverage and updated documentation.

Key Changes

🛡️ Strict Typing (Phase 1)

  • Full Type Safety: The fmd_api core library (client.py, device.py, models.py) now passes strict mypy checks.
  • Improved Signatures: Public methods now have precise return types and generic type hints (e.g., List[Dict[str, Any]]), improving IDE autocompletion and static analysis for consumers.
  • Configuration: Updated pyproject.toml to enforce strict typing rules (disallowing untyped definitions and implicit optionals).

🧪 Test Coverage Improvements

  • Coverage Boost: Increased overall test coverage to 97%.
  • Edge Case Handling: Added comprehensive tests for Location parsing, including scenarios with missing dates or invalid inputs.
  • Deprecation Verification: Added specific tests to ensure deprecated methods in Device (like take_front_photo, fetch_pictures) continue to function correctly while emitting appropriate warnings.
  • Command Sanitization: Verified that Device.lock() correctly sanitizes and truncates messages.

📚 Documentation

  • Roadmap: Added docs/strict_typing_enforcement_plan.md outlining the roadmap for maintaining type safety.
  • Community Updates: Updated the community instance URL in README.md to point to the new server location (https://server.fmd-foss.org/).

Upgrade Guide

This release is fully backward compatible with v2.0.4. No code changes are required for consumers, though you may notice improved type checking results if you use mypy in your own projects.

v2.0.4

10 Nov 01:06
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

Summary

v2.0.4 is a hardening + ergonomics release focused on secure password-free resume flows, clarified and modernized picture APIs, safer destructive actions, and significantly expanded test coverage (now ~98%). It remains fully backward compatible for consumers relying on deprecated method names; all new functionality is additive.

Highlights

1. Authentication Artifacts (Password-Free Resume)

  • New methods:
    • FmdClient.from_auth_artifacts(artifacts: dict) – Restore a client without the raw password.
    • FmdClient.resume(...) – Lower-level variant accepting explicit fields.
    • await client.export_auth_artifacts() – Export base_url, fmd_id, access_token, private_key (PEM), optional password_hash, session_duration, token_issued_at.
    • await client.drop_password() / drop_password=True in create() – Immediately discard raw password after onboarding.
  • 401 handling logic hierarchy:
    1. If raw password present → reauthenticate (existing flow)
    2. Else if password_hash present → hash-based token refresh (_reauth_with_hash())
    3. Else → raise FmdApiException (caller must re-onboard)
  • Private key load now supports both PEM and DER (fallback path tested).

2. Picture API Renaming & Deprecations

  • New canonical methods on Device:
    • get_picture_blobs() – fetch encrypted Base64 blobs.
    • decode_picture() – decrypt + decode into PhotoResult.
  • Deprecated (still functional, emit DeprecationWarning):
    • take_front_photo(), take_rear_photo() → use take_front_picture(), take_rear_picture()
    • fetch_pictures(), get_pictures() (Device) → use get_picture_blobs()
    • download_photo(), get_picture() (Device) → use decode_picture()
  • download_photo() now points directly to decode_picture() (avoids chained deprecated call).

3. Wipe (Factory Reset) Hardening

  • Device.wipe() now strictly requires:
    • confirm=True
    • pin argument present
    • PIN must be alphanumeric ASCII (no spaces). Future enforcement of 16+ length is noted (fmd-android MR 379).
  • Removed redundant space validation branch.

4. Lock Message Support

  • Device.lock(message=...) allows an optional user message; sanitized (quotes / backticks / semicolons removed, whitespace collapsed, 120 char cap). Falls back cleanly if server ignores payload.

5. Expanded Export Functionality & Robustness

  • export_data_zip() improvements:
    • Picture file extension detection: .png via magic bytes, default .jpg else.
    • Resilient manifest entries capturing per-item decryption errors (non-fatal).
    • Additional validation of location/picture list types with graceful fallbacks.

6. Coverage & Testing Improvements

  • Overall coverage ~98% (client.py ~98%, device.py mid/high 90s, models 100%).
  • New targeted tests exercise:
    • DER private key resume path
    • Missing artifact field errors
    • 401 reauth when neither password nor hash is available (expected exception)
    • Hash-based 401 reauth success path
    • PNG export branch + unknown image default .jpg
    • Deprecated wrapper warning emission
    • Non-dict JSON fallback handling & non-list picture responses
    • Error recording during export (location & picture failures)
    • Retry logic: 429 (Retry-After numeric/date/negative), 500/502 sequences, connection errors, backoff jitter/no-jitter paths
    • Masking helpers and retry-after parsing edge cases

7. Documentation Updates

  • README: Added password-free artifact usage, wipe PIN notes, lock message mention, community listing.
  • MIGRATE_FROM_V1.md: Corrected camera method naming, clarified wipe requirements, updated picture usage.
  • AUTH_ARTIFACTS_DESIGN.md: Formal specification of artifact-based resume workflow.

8. Internal / Quality Enhancements

  • Removed chained deprecation in Device.download_photo().
  • Simplified wipe() validation logic (single branch covers spaces & non-alphanumeric).
  • Eliminated redundant PIN space check.
  • Minor consistency and defensive branches now covered or documented.

Deprecations (No Immediate Removal)

Deprecated Replacement
Device.take_front_photo() Device.take_front_picture()
Device.take_rear_photo() Device.take_rear_picture()
Device.fetch_pictures() Device.get_picture_blobs()
Device.get_pictures() (Device wrapper) Device.get_picture_blobs()
Device.download_photo() Device.decode_picture()
Device.get_picture() Device.decode_picture()

Plan: Monitor usage; consider removal or formal EOL notice in a future minor/major once ecosystem migrates.

Security Considerations

  • Encourages immediate password discarding (drop_password=True), reducing exposure of raw credentials.
  • password_hash still sensitive—store using platform secret storage if possible.
  • Wipe PIN validation prevents accidental destructive actions with weak/empty inputs.
  • Lock message sanitization avoids command injection edge cases in future server parsing contexts.

Migration Notes (2.0.3 → 2.0.4)

  • Existing code continues to function; deprecation warnings guide picture API migration.
  • To adopt password-free resume:
    1. Onboard normally with create(..., drop_password=True).
    2. Persist await client.export_auth_artifacts() securely.
    3. Resume with await FmdClient.from_auth_artifacts(artifacts).

Example: Password-Free Cycle

client = await FmdClient.create(url, fmd_id, password, drop_password=True)
artifacts = await client.export_auth_artifacts()
# Persist artifacts securely...
await client.close()

client2 = await FmdClient.from_auth_artifacts(artifacts)
locations = await client2.get_locations(1)

Potential Follow-Ups (Not Included)

  • Enforce future 16+ PIN length once upstream server mandates it.
  • Add a CHANGELOG.md consolidating releases (this file can seed that entry).
  • Provide optional encrypted artifact export (password-protected ZIP or keyring integration).
  • Add coverage for final remaining defensive lines (currently low-risk).

Version

fmd_api.__version__ == "2.0.4"

Acknowledgements

Thanks to contributors and early testers providing feedback on artifact-based auth and API naming clarity.


Released: 2025-11-09

v2.0.3

06 Nov 19:14
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

🎯 Highlights

  • Test coverage increased from 81% to 98% with comprehensive unit tests
  • Pre-commit hooks for local code quality enforcement
  • Improved CI/CD workflows to prevent publish conflicts

✨ Testing Improvements

  • Added 45+ new unit tests covering edge cases and error handling paths in client.py and device.py
  • Comprehensive coverage of:
    • Authentication flows and password hashing
    • Connection error retry logic with backoff/jitter
    • HTTP 429/5xx retry handling with Retry-After header parsing
    • JSON parsing fallbacks and error handling
    • Export data ZIP generation with picture format detection
    • Device photo download and history decryption error paths
    • Stream response handling

🔧 Developer Experience

  • Pre-commit hooks configured with:
    • flake8 for linting (catch CI errors locally!)
    • black for code formatting
    • mypy for type checking
    • File cleanup (trailing whitespace, line endings, etc.)
  • Setup: pip install -e ".[dev]" then python -m pre_commit install

🚀 CI/CD Enhancements

  • Simplified publish workflow: Only publishes to PyPI from main branch or GitHub releases
  • Manual TestPyPI workflow: New workflow for controlled pre-release testing (trigger from Actions UI)
  • Fixed version conflicts: Removed automatic TestPyPI publishing from PRs to avoid duplicate version errors

📊 Coverage Metrics

  • Overall: 98% (512 statements, 10 missed)
  • client.py: 97%
  • device.py: 100%
  • All tests pass: 104 unit tests

🐛 Bug Fixes

  • Fixed packaging version alignment between pyproject.toml and _version.py
  • Resolved flake8 linting issues (import ordering, whitespace)

📦 What's Changed

  • Version bumped to 2.0.3
  • Added pre-commit to dev dependencies
  • Updated documentation and line endings normalization

v2.0.1

04 Nov 23:05
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

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.

0.1.0

23 Oct 01:53

Choose a tag to compare

Initial published release