Fixed
set_bedtime with scope: weekly no longer fails with HTTP 400 on some accounts (#135)
The weekly write previously sent hardcoded slot IDs (CAEQAQ ... CAEQBw). Those IDs are account-specific, so on accounts whose bedtime slots use different IDs, Google rejected every weekly call with:
HTTP 400 [3,"Request contains an invalid argument."]
This resulted in 7 failures during a full-week sync.
The client now resolves the slot ID from the live schedule (people/{id}/timeLimit) before building the payload, falling back to the static IDs only if the lookup fails.
Huge thanks to @bedar89, who reported the issue, diagnosed the root cause, and validated a working patch on v1.2.11 before opening the issue.
Additional clarification
A live API capture revealed that CAEQ* and CAM* are not two mutually exclusive account-specific ID families. They coexist within the same account and the same response.
After decoding the protobuf, field 1 identifies the rule type:
| Type | IDs | Description |
|---|---|---|
| 1 | CAEQAQ ... CAEQBw
| Bedtime |
| 3 | CAMQASIk... (with embedded UUID)
| School time |
A third block reuses the bedtime IDs but stores daily limits instead of a bedtime window.
The previous approach of selecting the first CA* ID matching the requested day was therefore order-dependent and could accidentally associate a school-time ID with a bedtime payload, replacing an obvious HTTP 400 error with a silent schedule corruption.
Rows are now accepted only when:
- the decoded rule type is 1 (bedtime);
- the payload contains a valid
[hour, minute] -> [hour, minute]bedtime window.
This makes the lookup independent of row ordering and resilient to response structure changes between API versions.
To avoid redundant API requests, the fetched schedule is cached per account for 60 seconds, since writing a full week performs one request per day.
Changed
Schedule parsing extracted into schedules.py
Adapted from the module split out of api.py in @benkap's fork - thanks!
The schedule parsing logic has been extracted into schedules.py, replacing the previous positional-array parser.
The new implementation has been verified against live API responses and returns byte-identical day / start / end values, ensuring that existing entities remain unaffected.
Verification
Tested end-to-end on a live account (HA 2026.5.1):
- The write path correctly resolves
CAEQBAfor Thursday from the live schedule instead of using the static fallback. - Google accepts the request.
- Re-reading the schedule immediately afterwards produces a byte-identical result.
- The parser refactor has been validated in production, correctly parsing 7 bedtime schedules and 8 school-time schedules, matching the raw API response with no errors since restart.
Note
The HTTP 400 error itself could not be reproduced on the test account because it already uses compatible type-1 IDs, making the previous static mapping work correctly. Confirmation that the fix resolves the issue on affected accounts comes from @bedar89, who successfully validated the patch.
Full changelog: v1.2.11...v1.2.12