This document tracks potential concerns that Home Assistant core developers may raise during integration review. Items are prioritized by severity.
- 🔴 CRITICAL - Must fix before HA submission
- 🟡 MAJOR - Should fix for production quality
- 🟢 MINOR - Nice to have improvements
Issue: pyproject.toml lists requests as a dependency but the code only uses aiohttp.
Location: pyproject.toml line 25
dependencies = [
"requests", # ❌ NOT USED
"argon2-cffi",
"cryptography",
"aiohttp",
]Fix: Remove requests from dependencies.
HA Rationale: Home Assistant requires minimal dependencies; unused dependencies will be rejected.
Status: ✅ FIXED
Issue: All aiohttp requests in _make_api_request() have no timeout configured.
Location: fmd_api/client.py line ~180
Risk: Can hang indefinitely, blocking Home Assistant's event loop.
Fix:
async def _make_api_request(self, ..., timeout: int = 30):
timeout_obj = aiohttp.ClientTimeout(total=timeout)
async with self._session.request(method, url, json=payload, timeout=timeout_obj) as resp:
# ...HA Rationale: All network calls MUST have timeouts. This is a hard requirement for HA integrations.
Status: ✅ FIXED
- Added
timeoutparameter toFmdClient.__init__()with default of 30 seconds - Applied timeout to all HTTP requests in
_make_api_request(),get_pictures(), andexport_data_zip() - Timeout can be overridden at client level or per-request
- Added test coverage with
test_timeout_configuration() - All 51 unit tests pass
Issue: Version is 2.0.0.dev8 - development versions not allowed for HA integrations.
Location: pyproject.toml line 3
Fix: Release as 2.0.0 stable before submitting to Home Assistant.
HA Rationale: Only stable, released versions accepted as integration dependencies.
Status: ✅ FIXED
- Bumped version to stable
2.0.0inpyproject.tomlandfmd_api/_version.py - Built sdist and wheel artifacts for release
- All unit tests passing after version bump
Issue: Version format differs between files:
pyproject.toml:2.0.0.dev8(PEP 440 compliant)_version.py:2.0.0-dev8(uses hyphen instead of dot)
Location:
pyproject.tomlline 3fmd_api/_version.pyline 1
Fix: Use consistent PEP 440 format: 2.0.0.dev8 everywhere, or 2.0.0 for stable.
HA Rationale: Version inconsistencies cause packaging and dependency resolution issues.
Status: ✅ FIXED
- Changed
_version.pyfrom "2.0.0-dev9" to "2.0.0.dev9" (PEP 440 compliant) - Both files now use consistent dot notation
Issue: No protection against hitting API rate limits. No handling for 429 (Too Many Requests) responses.
Location: fmd_api/client.py _make_api_request() method
Fix: Implement exponential backoff for rate limit responses:
if resp.status == 429:
retry_after = int(resp.headers.get('Retry-After', 60))
await asyncio.sleep(retry_after)
# retry logicHA Rationale: Production integrations must handle rate limits gracefully to avoid service disruption.
Status: ✅ FIXED
- Implemented 429 handling with Retry-After header support and exponential backoff with optional jitter
- Retries for transient 5xx (500/502/503/504) and connection errors
- Avoids unsafe retries for POST /api/v1/command, except on 401 re-auth or 429 with explicit Retry-After
- Configurable via
max_retries,backoff_base,backoff_max,jitter - Added unit tests:
test_rate_limit_retry_with_retry_after,test_server_error_retry_then_success - All unit tests passing
Issue: No py.typed file means type checkers won't recognize the package's type hints.
Location: Missing from fmd_api/ directory
Fix: Create empty file fmd_api/py.typed
HA Rationale: Type hints are required for HA integrations. The py.typed marker enables type checking for library users.
Status: ✅ FIXED
- Created empty
fmd_api/py.typedmarker file - Type checkers will now recognize the package's type hints per PEP 561
Issue: FmdClient requires manual close() call. If forgotten, aiohttp sessions leak.
Location: fmd_api/client.py class FmdClient
Fix: Implement __aenter__ and __aexit__:
class FmdClient:
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, tb):
await self.close()Usage:
async with await FmdClient.create(...) as client:
# auto-closes on exitHA Rationale: Context managers are the Python standard for resource management. HA prefers libraries that follow this pattern.
Status: ✅ FIXED
- Implemented
__aenter__and__aexit__onFmdClient - Usage supported:
async with FmdClient(base_url) as client:async with await FmdClient.create(base_url, fmd_id, password) as client:
- On exit, aiohttp session is closed automatically via
close() - Added unit tests verifying auto-close behavior
Issue: Classifiers include Python 3.7 but requires-python = ">=3.8"
Location: pyproject.toml lines 8-15
Fix: Remove "Programming Language :: Python :: 3.7" from classifiers.
HA Rationale: Misleading classifiers can cause installation issues.
Status: ✅ FIXED
- Removed Python 3.7 classifier;
requires-pythonis>=3.8
Issue: Debug logging may expose passwords, access tokens, or private keys.
Location: Multiple places in fmd_api/client.py
Example Issues:
- Line ~88: Logs may include auth details
- Line ~203: Logs full JSON responses which may contain tokens
Fix:
- Sanitize all log output
- Mask tokens:
log.debug(f"Token: {token[:8]}...") - Add guards:
if log.isEnabledFor(logging.DEBUG):
HA Rationale: Security and privacy requirement for production systems.
Status: ✅ FIXED
- Removed logging of full JSON responses (line ~278); now logs only dict keys
- Removed logging of response body text (line ~285); now logs only length
- Added
_mask_token()helper for safe token logging (shows first 8 chars) - Added comment to prevent signature logging in
send_command() - Auth flow logs only workflow steps, never actual credentials
- All 55 unit tests passing after sanitization
Issue: No way to configure SSL certificate verification. Users with self-signed certificates cannot disable verification.
Location: fmd_api/client.py _ensure_session() method
Fix: Add SSL parameter/connector configuration to constructor:
def __init__(..., ssl: Optional[ssl.SSLContext|bool] = None, ...):
self._ssl = ssl # None=default verify, False=disable, SSLContext=custom
async def _ensure_session(self):
connector = aiohttp.TCPConnector(ssl=self._ssl, ...)
self._session = aiohttp.ClientSession(connector=connector)HA Rationale: Enterprise users and development environments need SSL configuration flexibility.
Status: ✅ FIXED
- New constructor options:
ssl(None | False | SSLContext) - Verified by unit test
test_connector_configuration_applied - Works with self-signed certs when
ssl=False, or custom trust via SSLContext - HTTPS is explicitly enforced:
http://base URLs are rejected by the client
Issue: Single network failure causes operation to fail immediately. No retry for temporary 5xx errors.
Location: fmd_api/client.py _make_api_request() method
Fix: Implement retry with exponential backoff for 500, 502, 503, 504 errors.
HA Rationale: Improves reliability in production environments with occasional network issues.
Status: ✅ FIXED
- Covered by issue 5 implementation; includes exponential backoff for transient errors
Issue: Return types are too vague, reducing type safety benefits.
Examples:
get_pictures() -> List[Any]- what's in the list?_make_api_request() -> Any- what type is returned?
Location: Throughout fmd_api/client.py
Fix: Define proper types using TypedDict or dataclasses:
from typing import TypedDict
class PictureDict(TypedDict):
id: int
date: int
# other fields
async def get_pictures(self, num_to_get: int = -1) -> List[PictureDict]:HA Rationale: Strong typing helps catch bugs and improves IDE support.
Status: ❌ TODO
Issue: ClientSession created with default connection limits. May not be optimal for all use cases.
Location: fmd_api/client.py _ensure_session() method
Fix: Allow configuration:
def __init__(self, ..., max_connections: int = 10, max_connections_per_host: int = 5):
self.max_connections = max_connections
self.max_connections_per_host = max_connections_per_host
async def _ensure_session(self):
connector = aiohttp.TCPConnector(
limit=self.max_connections,
limit_per_host=self.max_connections_per_host
)
self._session = aiohttp.ClientSession(connector=connector)HA Rationale: Performance tuning capability for production deployments.
Status: ✅ FIXED
- New constructor options:
conn_limit,conn_limit_per_host,keepalive_timeout - Applied via
aiohttp.TCPConnectorin_ensure_session() - Verified by unit test
test_connector_configuration_applied
Issue: decrypt_data_blob() is synchronous and performs CPU-intensive RSA/AES operations.
Location: fmd_api/client.py decrypt_data_blob() method
Risk: Can block Home Assistant's event loop for 100ms+ with large blobs.
Fix: Run in executor for async compatibility:
async def decrypt_data_blob_async(self, data_b64: str) -> bytes:
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, self.decrypt_data_blob, data_b64)HA Rationale: Event loop blocking causes UI freezes and integration performance issues.
Status: ✅ FIXED
- Added
decrypt_data_blob_async()method that usesrun_in_executor() - Added test coverage for the async method
- Documented the sync vs async usage in docstrings
Issue: README has no build status, test coverage, or version badges.
Location: README.md
Fix: Add badges for:
- GitHub Actions build status
- Test coverage (codecov)
- PyPI version
- Python versions supported
HA Rationale: Demonstrates project health and maintenance status.
Status: ❌ TODO
Issue: Users can't see what changed between versions.
Location: Missing from repository root
Fix: Add CHANGELOG.md following Keep a Changelog format.
HA Rationale: Good practice for library maintenance and user communication.
Status: ✅ FIXED
- Added
CHANGELOG.mdfollowing Keep a Changelog format - Documents all releases from 2.0.0 to current
Issue: Documentation doesn't clearly state which exceptions can be raised and when.
Location: Docstrings in fmd_api/client.py and user documentation
Fix: Document exception hierarchy and when each is raised:
async def get_locations(...) -> List[str]:
"""
...
Raises:
AuthenticationError: If authentication fails
FmdApiException: If server returns error
asyncio.TimeoutError: If request times out
aiohttp.ClientError: For network errors
"""HA Rationale: Users need to know how to handle errors properly.
Status: ✅ FIXED
- Added Raises sections to all public API methods in client.py
- Documented ValueError, FmdApiException, aiohttp.ClientError, asyncio.TimeoutError
Issue: Test coverage percentage unknown. No coverage reports in CI.
Location: Test configuration
Fix:
- Add
pytest-covto dev dependencies - Configure coverage in
pyproject.toml - Add coverage reporting to CI workflow
HA Rationale: Demonstrates code quality and test thoroughness.
Status: ❌ TODO
Issue: Location and PhotoResult classes not in __all__ exports.
Location: fmd_api/__init__.py
Fix: Add to exports if users need them:
__all__ = [
"FmdClient",
"Device",
"Location",
"PhotoResult",
...
]HA Rationale: Makes API more discoverable and IDE-friendly.
Status: ✅ FIXED
- Added
Location,PhotoResult, andRateLimitErrorto__all__exports
Issue: No way to track API call success rates, latencies, or errors.
Location: Architecture design
Fix: Add optional callback hooks:
def __init__(self, ..., on_request=None, on_error=None):
self.on_request = on_request # callback(method, endpoint, duration)
self.on_error = on_error # callback(error, context)HA Rationale: Helpful for production monitoring and debugging.
Status: ❌ TODO
Issue: Password kept as plaintext string in self._password for automatic re-authentication.
Location: fmd_api/client.py line ~52
Note: Limited mitigation possible in Python due to string immutability.
Recommendation: Document this behavior in security documentation.
HA Rationale: Users should be aware of security implications.
Status: ❌ TODO (Documentation)
Issue: Can't pin server certificate for high-security deployments.
Location: SSL/TLS configuration
Fix: Add optional SSL context parameter:
def __init__(self, ..., ssl_context: Optional[ssl.SSLContext] = None):
self.ssl_context = ssl_contextHA Rationale: Nice to have for security-conscious deployments.
Status: ❌ TODO
Issue: Only unit tests with mocks. No tests against real FMD server.
Location: tests/ directory
Fix: Add integration tests (can be optional/manual with real credentials).
HA Rationale: Increases confidence in production reliability.
Status: ❌ TODO
Before HA Submission (Critical):
- Remove unused
requestsdependency - Add HTTP request timeouts
- Release stable 2.0.0 version
- Fix version string inconsistency
- Add
py.typedfile - Implement async context manager
- Add rate limit handling — DONE
For Production Quality (Major):
- Fix Python 3.7 classifier — DONE
- Sanitize logs (security) — DONE
- Add SSL verification control — DONE
- Improve type hints
- Add retry logic — DONE
- Configure connection pooling — DONE
- Make decryption async — DONE
For Best Practices (Minor):
- Add CI badges — PARTIAL (Added Tests + Codecov badges; PyPI/version badges pending)
- Create CHANGELOG.md — DONE
- Document exceptions — DONE
- Add test coverage reporting — DONE (100% branch coverage)
- Export all public models — DONE
Issue: CI workflow added to run unit tests on PRs/pushes.
Impact: Prevents merging broken code; validates before publish.
Fix: GitHub Actions workflow runs pytest on Python 3.8–3.12 across ubuntu/windows.
Status: ✅ FIXED
Issue: Linting now enforced with flake8.
Fix: Added flake8 step to CI workflow; configured .flake8 for ignores/excludes.
Status: ✅ FIXED
Issue: Type checking now enforced with mypy.
Fix: Added mypy step to CI workflow.
Status: ✅ FIXED
Issue: Coverage measurement now implemented; badge still pending.
Fix: Added pytest-cov with XML output and Codecov upload in CI (matrixed across OS/Python). Use --cov-branch for branch coverage.
Status: ✅ FIXED (Badge pending)
Issue: No vulnerability checks on dependencies.
Fix: Enable GitHub Dependabot or add safety checks to CI.
Status: ❌ TODO (Minor)
Before submitting to Home Assistant:
- All critical issues resolved
- Major security concerns addressed
- Type hints complete and accurate
- Documentation comprehensive
- Test coverage > 80% (Currently at 100%)
- CHANGELOG.md up to date
- Stable version released to PyPI
- Code passes
flake8andmypy - CI runs tests on all supported Python versions
- CI enforces linting and type checking