A lightweight, Python client for the Flowerhub portal API with cookie-based JWT authentication.
Related Projects:
- Home Assistant Custom Integration (known consumer)
- Authentication: Cookie-based JWT with automatic token refresh and
AuthenticationErroron login failure - Async/await: Full async support via
aiohttpwith configurable timeouts and retries - API coverage: 12+ endpoints including assets, consumption, invoices, uptime metrics, revenue, and profiles
- Type-safe: Fully typed with
TypedDictresult types extendingStandardResultbase class - Designed for Home Assistant integrations and similar use cases with robust error handling
pip install flowerhub-portal-api-clientgit clone https://github.com/MichaelPihlblad/flowerhub-portal-api_python-lib.git
cd flowerhub-portal-api_python-lib
pip install -e ".[async]"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())- Online: Library documentation — hosted on GitHub Pages, auto-published on push to
main. - Local preview:
pip install -r dev-requirements.txt
mkdocs serveTo build the static site locally:
mkdocs build --strictexamples/secrets.json is in .gitignore — use it as a template for local development only. Always use environment variables or secure credential managers in production. The pre-commit hook detect-private-key will block commits containing suspected private keys.
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
- `result_queue` (an `asyncio.Queue`) will receive `FlowerHubStatus` objects via non-blocking `put_nowait`.
- `interval_seconds` must be >= 5 (default 60).- Per-call options: All public fetch methods accept
raise_on_error(defaultTrue),retry_5xx_attempts(controls 5xx/429 retries;Noneor0disables), andtimeout_total(per-callaiohttp.ClientTimeoutoverride; default is 10s,0/Nonedisables timeout). - Error handling:
AuthenticationErroris raised after a failed refresh retry on401;ApiErroris raised for other HTTP errors or validation issues whenraise_on_error=True. Useon_auth_failedandon_api_errorcallbacks for integration-specific handling. - Lifecycle: Prefer
async with AsyncFlowerhubClient(...)or callclose()to stop background tasks. The client does not close an injected session. - Concurrency / rate limiting: Call
set_max_concurrency(n)to limit in-flight requests with a semaphore; pass0/Noneto disable. - Retry/backoff: 5xx and 429 responses are retried with a small jitter; 429 honors
Retry-Afterwhen provided.
Authentication:
async_login(username, password)- Authenticate and establish session
Core Data Fetching:
async_fetch_asset_id()- Discover asset ID for the authenticated userasync_fetch_asset()- Fetch asset details and FlowerHub statusasync_readout_sequence()- Convenient readout of: asset ID → asset info → uptime pie
Financial Data:
async_fetch_invoices()- Fetch billing invoicesasync_fetch_consumption()- Fetch consumption recordsasync_fetch_revenue()- Fetch revenue/compensation data
Uptime Metrics:
async_fetch_uptime_pie(period=None)- Uptime distribution with automatic ratio calculationasync_fetch_uptime_history()- Monthly uptime percentagesasync_fetch_available_uptime_months()- Available month options for uptime queries
User & System:
async_fetch_asset_owner_profile()- Fetch owner profile with contact infoasync_fetch_asset_owner()- Fetch complete owner details (installer/distributor)async_fetch_electricity_agreement()- Fetch consumption/production agreement statesasync_fetch_system_notification()- System-level notifications (Flower/Zavann status)
Lifecycle & Monitoring:
start_periodic_asset_fetch(interval_seconds, ...)- Start background pollingstop_periodic_asset_fetch()- Stop background pollingis_asset_fetch_running()- Check polling status
Exceptions:
AuthenticationError- Raised on 401 authentication failures after refresh retryApiError- Raised for HTTP errors or validation issues (includesstatus_code,url,payload)
Result Types (all extend StandardResult):
- Base:
StandardResult- Common envelope withstatus_code,json,text,error - Asset:
AssetIdResult,AssetFetchResult - Financial:
InvoicesResult,ConsumptionResult,RevenueResult - Uptime:
UptimeAvailableMonthsResult,UptimeHistoryResult,UptimePieResult - User:
ProfileResult,AssetOwnerDetailsResult - Other:
AgreementResult,SystemNotificationResult(alias to StandardResult)
Data Classes:
- Status:
FlowerHubStatus(device status with age tracking) - Financial:
Invoice,InvoiceLine,ConsumptionRecord,Revenue - Uptime:
UptimeMonth,UptimeHistoryEntry - User:
AssetOwnerProfile,AssetOwnerDetails,SimpleInstaller,SimpleDistributor - Other:
ElectricityAgreement,AgreementState,Asset,Inverter,Battery
See types.py for complete type definitions.
- Callbacks:
on_auth_failed(refresh failed + second 401),on_api_error(before raising ApiError whenraise_on_error=True). - Concurrency:
set_max_concurrency(n)adds a semaphore-based rate limiter;0/Nonedisables. - Context manager:
async with AsyncFlowerhubClient(...)orclose()to stop periodic tasks; injected sessions are not closed by the client.
exceptions.py:AuthenticationError,ApiError(includesstatus_code,url,payload).types.py: dataclasses and TypedDicts used by the client (FlowerHubStatus,ElectricityAgreement,Invoice,ConsumptionRecord,AssetIdResult,AssetFetchResult, etc.).parsers.py: pure helpers to parse/validate API payloads (used by the client but can be imported directly for advanced/standalone parsing).
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.
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()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.