Skip to content

fix(api): honour 429 retry-after instead of failing immediately - #187

Open
flupkede wants to merge 4 commits into
fustom:mainfrom
flupkede:fix/429-backoff
Open

flupkede wants to merge 4 commits into
fustom:mainfrom
flupkede:fix/429-backoff

Conversation

@flupkede

Copy link
Copy Markdown

Problem

When the Ariston cloud rate-limits a request it returns HTTP 429 with the block duration in the body (Requests are blocked for 66 seconds), but the client discarded that body and raised immediately. Home Assistant setups typically run multiple DataUpdateCoordinators against the same API (main state, bus errors, energy), so after the exception every coordinator kept polling on its normal interval — often still inside the block window — causing a storm of repeated 429s. The coordinators had no shared knowledge of a running block.

Solution

  • Parse the block duration with a case-insensitive bytes regex (blocked for (\d+) seconds).
  • Sleep N+1 seconds (sync and async paths), then retry exactly once via the existing is_retry mechanism. A second 429 raises — no loop.
  • Unparsable body falls back to a 5s wait, matching the existing generic error case.
  • Class-level AristonAPI._blocked_until: once one caller sees a 429, every caller fails fast locally (no network call) until the window has passed. The deliberate post-sleep retry (is_retry=True) bypasses the guard so it cannot deadlock against its own deadline.
  • The 405 (re-login) and 404 paths are untouched.

The second commit adds Velis holiday support, mirroring the existing Galevo holiday API:

  • set_velis_holiday / async_set_velis_holiday on AristonAPI and AristonVelisDevice.
  • POST velis/slpPlantData/{gw}/holiday with {"new": "YYYY-MM-DDT00:00:00"} to enable and {"new": null} to cancel. Verified against the live API: {"success":true}, and the holidayUntil field in velis/slpPlantData reflects the change on the next read.
  • The date format is centralized in a new HOLIDAY_DATE_FORMAT constant, now shared with the Galevo implementation.

Tests

10 unit tests covering duration parsing, the N+1 wait with a single retry, the 5s fallback, second-429 raise, the shared _blocked_until short-circuit (no network call inside the window), their async equivalents, and the holiday URL/payload/date-format contract.

Note: pyproject.toml is bumped to 0.19.9+patch2 (local-version suffix) because our deployment installs this from a wheel and pip needs a version bump to pick it up — happy to rebase onto whatever versioning you prefer.

The API returns the block duration in the response body but the client
discarded it and raised straight away, so the next poll often landed
inside the same window. Parse the value, wait it out, and share the
blocked-until deadline across all callers.

Closes: AGENTS_ariston-429-holiday.md
The app exposes holiday mode for Velis water heaters but the library had
no method for it. POST velis/slpPlantData/{gw}/holiday with {"new": date}
to enable (ISO midnight format) and {"new": null} to cancel, mirroring the
existing Galevo holiday API. Date format centralized in a shared constant.

Proven against the live API: POST returns {"success":true} and the field
holidayUntil appears in velis/slpPlantData afterwards.

Closes: AGENTS_ariston-429-holiday.md
The +patchN version suffix is only needed for our local wheel
deployment; release versioning belongs to the maintainer, so the PR
diff no longer touches pyproject.toml (this also clears the Sonar
S8565 lock-file finding from new code). Tests now use the monkeypatch
fixture for the shared _blocked_until state and keep a single
invocation inside pytest.raises blocks (S8997, S5778).
- Replace dict()/list() constructor calls with {}/[] literals
  across ariston_api, galevo_device, velis_device (Sonar S7498)
- Extract the 429 branch from __request/__async_request into
  __handle_rate_limit/__async_handle_rate_limit helpers plus a
  module-level _rate_limit_wait_seconds() (Sonar S3776)

No behavior change; full suite 10/10 green (ariston-test:py313).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant