Maestro task 37d5e806-8176-4360-a240-5583c50d31ba — API client module for the integration (Feature 18: HA Integration as API Client).
Done-condition
The integration gains an API client module (custom_components/nestquest/api_client.py) that talks to the NestQuest API over HTTP: base URL and panel service token read from config, a get_snapshot() returning the parsed snapshot, and a complete_instance(id, actor_child_id) call. Transport failures and non-2xx responses raise a typed error. Tests exercise it against a stub transport (no network) for success, 401, 404 and a connection failure.
What this does
custom_components/nestquest/api_client.py: async panel-plane client — get_snapshot(), complete_instance(instance_id, actor_child_id), typed NestQuestApiError (status + is_transport_error, cause chained), one asyncio.timeout per request, no retries, no logging (the token never reaches a log line). The aiohttp session is constructor-injected (stub transport in tests); production builds via client_from_entry(hass, entry) using the shared HA aiohttp session (async_get_clientsession, deferred import so the module imports without HA/aiohttp).
Config plumbing: CONF_API_BASE_URL / CONF_PANEL_TOKEN (+ defaults) in core/const.py; wired into the options flow schema/validation/save path like every other section (raw submits without the keys keep stored values); strings.json + translations/en.json updated identically. The token is a secret: never logged, never in error messages.
Tests: new tests/test_api_client.py (17 tests, stub transport, no network): success (snapshot parsed; complete posts the right body + Bearer header), 401, 404, 500, malformed JSON, connection failure, timeout — all typed with expected status/kind; plus config resolution and the production factory. Existing options-flow tests updated for the two new keys (FULL_INPUT + exact-dict expectations); 5 new flow tests.
Checks
TZ=UTC uv run pytest -q → 1796 passed in 15.19s (baseline 1774 + 22 new)
TZ=UTC uv run pytest -q tests/test_api_client.py → 17 passed in 0.08s
Coordinator wiring (reading the client in the refresh cycle) is the next task; this change adds only the client module and its config plumbing.
## Task
Maestro task 37d5e806-8176-4360-a240-5583c50d31ba — API client module for the integration (Feature 18: HA Integration as API Client).
## Done-condition
The integration gains an API client module (custom_components/nestquest/api_client.py) that talks to the NestQuest API over HTTP: base URL and panel service token read from config, a get_snapshot() returning the parsed snapshot, and a complete_instance(id, actor_child_id) call. Transport failures and non-2xx responses raise a typed error. Tests exercise it against a stub transport (no network) for success, 401, 404 and a connection failure.
## What this does
- `custom_components/nestquest/api_client.py`: async panel-plane client — `get_snapshot()`, `complete_instance(instance_id, actor_child_id)`, typed `NestQuestApiError` (`status` + `is_transport_error`, cause chained), one `asyncio.timeout` per request, no retries, no logging (the token never reaches a log line). The aiohttp session is constructor-injected (stub transport in tests); production builds via `client_from_entry(hass, entry)` using the shared HA aiohttp session (`async_get_clientsession`, deferred import so the module imports without HA/aiohttp).
- Config plumbing: `CONF_API_BASE_URL` / `CONF_PANEL_TOKEN` (+ defaults) in `core/const.py`; wired into the options flow schema/validation/save path like every other section (raw submits without the keys keep stored values); strings.json + translations/en.json updated identically. The token is a secret: never logged, never in error messages.
- Tests: new `tests/test_api_client.py` (17 tests, stub transport, no network): success (snapshot parsed; complete posts the right body + Bearer header), 401, 404, 500, malformed JSON, connection failure, timeout — all typed with expected status/kind; plus config resolution and the production factory. Existing options-flow tests updated for the two new keys (FULL_INPUT + exact-dict expectations); 5 new flow tests.
## Checks
- `TZ=UTC uv run pytest -q` → **1796 passed** in 15.19s (baseline 1774 + 22 new)
- `TZ=UTC uv run pytest -q tests/test_api_client.py` → **17 passed** in 0.08s
Coordinator wiring (reading the client in the refresh cycle) is the next task; this change adds only the client module and its config plumbing.
Head: 2b6b7431b5e4128b8f0f8bfee01ecbbb777730a4
The integration will consume the NestQuest API service (Feature 16) over
HTTP instead of reading the database. This adds the one module that
speaks the panel plane: api_client.py with get_snapshot() and
complete_instance(), a typed NestQuestApiError for transport failures
and non-2xx responses (carrying the HTTP status so 401/404 are
distinguishable), one timeout per request and no retry loops. The HTTP
session is injected, so tests exercise the client against a stub
transport with no network and no aiohttp; the production factory hands
in HA's shared aiohttp session and resolves the base URL and panel
service token off the config entry (options, then entry data, then
defaults). The two new settings are wired into the options flow like
every other section (validation mirrored with the client's constructor
rules); the token is a secret and is never logged or echoed into error
messages. Coordinator wiring is the next task.
The const.py comment claimed an empty stored token yields a client that
answers 401 until configured, but NestQuestApiClient raises ValueError
at construction for an empty/whitespace token. Keep the fail-fast guard,
correct the documentation to state the actual contract (default token is
the empty string; options flow validates non-empty; the coordinator must
configure a token first or handle the construction error), and add a
test pinning that client_from_entry with an unconfigured entry raises at
construction instead of building a client that can only 401.
The comment block rewritten by the last commit claimed the options flow
validates that a saved token is non-empty (the user-facing guard). That
is false: options_flow.py treats an EMPTY token as valid ("not configured
yet") and only rejects malformed values (padded / whitespace-only /
non-string), pinned by test_options_flow.py
(test_options_flow_api_section_defaults_saved asserts a bare submit saves
CONF_PANEL_TOKEN == ""; test_options_flow_panel_token_validation
documents the empty-token-is-valid contract).
Comment-only correction, no code or test changes: the clause now says the
options flow accepts an empty token and the actual non-emptiness guard is
enforced at NestQuestApiClient construction.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Task
Maestro task 37d5e806-8176-4360-a240-5583c50d31ba — API client module for the integration (Feature 18: HA Integration as API Client).
Done-condition
The integration gains an API client module (custom_components/nestquest/api_client.py) that talks to the NestQuest API over HTTP: base URL and panel service token read from config, a get_snapshot() returning the parsed snapshot, and a complete_instance(id, actor_child_id) call. Transport failures and non-2xx responses raise a typed error. Tests exercise it against a stub transport (no network) for success, 401, 404 and a connection failure.
What this does
custom_components/nestquest/api_client.py: async panel-plane client —get_snapshot(),complete_instance(instance_id, actor_child_id), typedNestQuestApiError(status+is_transport_error, cause chained), oneasyncio.timeoutper request, no retries, no logging (the token never reaches a log line). The aiohttp session is constructor-injected (stub transport in tests); production builds viaclient_from_entry(hass, entry)using the shared HA aiohttp session (async_get_clientsession, deferred import so the module imports without HA/aiohttp).CONF_API_BASE_URL/CONF_PANEL_TOKEN(+ defaults) incore/const.py; wired into the options flow schema/validation/save path like every other section (raw submits without the keys keep stored values); strings.json + translations/en.json updated identically. The token is a secret: never logged, never in error messages.tests/test_api_client.py(17 tests, stub transport, no network): success (snapshot parsed; complete posts the right body + Bearer header), 401, 404, 500, malformed JSON, connection failure, timeout — all typed with expected status/kind; plus config resolution and the production factory. Existing options-flow tests updated for the two new keys (FULL_INPUT + exact-dict expectations); 5 new flow tests.Checks
TZ=UTC uv run pytest -q→ 1796 passed in 15.19s (baseline 1774 + 22 new)TZ=UTC uv run pytest -q tests/test_api_client.py→ 17 passed in 0.08sCoordinator wiring (reading the client in the refresh cycle) is the next task; this change adds only the client module and its config plumbing.
Head:
2b6b7431b5The comment block rewritten by the last commit claimed the options flow validates that a saved token is non-empty (the user-facing guard). That is false: options_flow.py treats an EMPTY token as valid ("not configured yet") and only rejects malformed values (padded / whitespace-only / non-string), pinned by test_options_flow.py (test_options_flow_api_section_defaults_saved asserts a bare submit saves CONF_PANEL_TOKEN == ""; test_options_flow_panel_token_validation documents the empty-token-is-valid contract). Comment-only correction, no code or test changes: the clause now says the options flow accepts an empty token and the actual non-emptiness guard is enforced at NestQuestApiClient construction.Independent Claude Sonnet 5 review completed for this exact commit.