Lightweight Flowerhub portal client (synchronous and async)
Project description
Flowerhub Portal Python Client
A lightweight, Python client for the Flowerhub portal API with cookie-based JWT authentication.
Related Projects:
Features
- Cookie-based JWT authentication with automatic token refresh
- Async/await support via
aiohttp - Client provides asset, consumption, and invoice data from API endpoints
- Designed for Home Assistant integrations and similar use cases
Installation
From Source
git clone https://github.com/MichaelPihlblad/flowerhub-portal-api_python-lib.git
cd flowerhub-portal-api_python-lib
pip install -e ".[async]"
Quick Start
import asyncio
from flowerhub_portal_api_client import AsyncFlowerhubClient
async def main():
async with aiohttp.ClientSession() as session:
client = AsyncFlowerhubClient(session=session)
# Login
result = await client.async_login("user@example.com", "password")
# Fetch asset information
await client.async_readout_sequence()
# Access asset status
if client.flowerhub_status:
print(f"Status: {client.flowerhub_status.status}")
print(f"Message: {client.flowerhub_status.message}")
asyncio.run(main())
Authentication Error Handling
The client automatically handles token refresh on 401 Unauthorized responses. If the refresh fails and the request still returns 401, an AuthenticationError is raised, indicating that the user needs to login again.
Exception-Based Handling
from flowerhub_portal_api_client import AsyncFlowerhubClient, AuthenticationError
try:
await client.async_fetch_asset_id()
except AuthenticationError:
# Token refresh failed, re-authentication required
await client.async_login(username, password)
await client.async_fetch_asset_id()
Callback-Based Handling
For Home Assistant integrations or event-driven architectures:
def on_auth_failed():
"""Called when authentication fails and re-login is needed."""
print("Re-authentication required")
# Trigger reauth flow, set flag, etc.
client = AsyncFlowerhubClient(
session=session,
on_auth_failed=on_auth_failed
)
See examples/auth_error_handling.py for complete examples including Home Assistant patterns.
Development Setup
For contributors and local development:
# Clone and setup environment
git clone https://github.com/MichaelPihlblad/flowerhub-portal-api_python-lib.git
cd flowerhub-portal-api_python-lib
python -m venv .venv
source .venv/bin/activate
# Install dependencies
pip install -r requirements.txt
pip install -r dev-requirements.txt
# Install pre-commit hooks
pre-commit install
# Run all checks
pre-commit run --all-files
pytest tests --cov=flowerhub_portal_api_client
Running Examples
Set credentials via environment variables:
export FH_USER="you@example.com"
export FH_PASSWORD="your_password"
python examples/run_example.py
Or create examples/secrets.json:
{
"username": "you@example.com",
"password": "your_password"
}
API Documentation
For detailed information about the Flowerhub portal API endpoints used by this Python client library, such as request/response formats, and data structures, see API_DOCUMENTATION.md.
Periodic status refresh (background polling) 💡
The client provides a convenience helper to periodically refresh the asset's
flowerHubStatus in a background asyncio Task and deliver updates to your code:
start_periodic_asset_fetch(interval_seconds=60, run_immediately=False, on_update=None, result_queue=None)on_update(callable) will be invoked with aFlowerHubStatusobject on every successful refresh (called in the event loop).result_queue(anasyncio.Queue) will receiveFlowerHubStatusobjects via non-blockingput_nowait.interval_secondsmust be >= 5 (default 60).
Example (simple async usage):
import asyncio
import aiohttp
from flowerhub_portal_api_client import AsyncFlowerhubClient
async def main():
async with aiohttp.ClientSession() as session:
client = AsyncFlowerhubClient(base_url="https://api.portal.flowerhub.se", session=session)
await client.async_login("user@example.com", "password")
q = asyncio.Queue()
client.start_periodic_asset_fetch(60, run_immediately=True, result_queue=q)
try:
while True:
fhs = await q.get()
print("Queued update:", fhs.status, fhs.message, f"age={fhs.age_seconds():.1f}s")
finally:
client.stop_periodic_asset_fetch()
asyncio.run(main())
If you prefer a callback approach, pass on_update which will be called with
a FlowerHubStatus instance each time the status is refreshed. Avoid blocking
operations in the callback because it's executed in the event loop.
`FlowerHubStatus` fields:
- `status` — textual status (e.g., "Connected")
- `message` — human-friendly message
- `updated_at` — UTC datetime when the status was recorded
- `age_seconds()` — helper returning the age in seconds (float) or `None` if timestamp missing
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flowerhub_portal_api_client-0.3.0.tar.gz.
File metadata
- Download URL: flowerhub_portal_api_client-0.3.0.tar.gz
- Upload date:
- Size: 18.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f77d9df898ea38a62b8a64a26050c6cfeffc7a5d5b1830a46f3de61912c7040
|
|
| MD5 |
8b9bc0c714d7bfd4a7cfc5879b24c849
|
|
| BLAKE2b-256 |
94a07e67d611ebdcd87e542f29da01071936bceaec5179e5b10c9b255c90c847
|
File details
Details for the file flowerhub_portal_api_client-0.3.0-py3-none-any.whl.
File metadata
- Download URL: flowerhub_portal_api_client-0.3.0-py3-none-any.whl
- Upload date:
- Size: 14.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
63d8dd3c3dafdccd14f9bfcffb4a6aa7e1e61250543318f8409433516b20e5ef
|
|
| MD5 |
17e13b9cbc56eb4d7903c4e89e798c4b
|
|
| BLAKE2b-256 |
6cdae4616f747f945e338fc0dd31756d3d8617b9a46b67c71f0c56fb8babb51b
|