feat(home-assistant): add REST coverage for config CRUD, history, and calendars - #239
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
Summary by CodeRabbit
WalkthroughHome Assistant adds REST actions for entity history, logbook entries, calendars, calendar events, and error logs. It also adds automation, script, and scene configuration CRUD actions plus configuration validation. Shared schemas, request typing, key validation, result normalization, follow-up action chains, and reverse-proxy-compatible URL construction support these actions. Configuration handlers are registered alongside existing REST and WebSocket handlers. Sequence Diagram(s)sequenceDiagram
participant ProviderExecutor
participant HomeAssistantActionHandlers
participant HomeAssistantConfigActionHandlers
participant HomeAssistantAPI
ProviderExecutor->>HomeAssistantActionHandlers: invoke REST action
HomeAssistantActionHandlers->>HomeAssistantAPI: request history, calendar, logbook, or error log data
HomeAssistantAPI-->>HomeAssistantActionHandlers: return response
ProviderExecutor->>HomeAssistantConfigActionHandlers: invoke configuration action
HomeAssistantConfigActionHandlers->>HomeAssistantAPI: get, save, delete, or check configuration
HomeAssistantAPI-->>HomeAssistantConfigActionHandlers: return normalized result
Possibly related PRs
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/providers/home_assistant/actions.ts`:
- Around line 225-245: The get_history output schema currently requires fields
that Home Assistant omits from minimal intermediate history entries. Update the
history item schema used by the get_history output, near the history output
definition, so entity_id, attributes, and last_updated are optional while
preserving required fields and existing stateSchema usage elsewhere; use a
dedicated loose history-state schema if stateSchema is shared.
In `@src/providers/home_assistant/runtime.ts`:
- Around line 130-137: Prevent empty or whitespace-only entity IDs from
producing an unfiltered history query. Add a nearby readHistoryEntityIds helper
that parses entityIds with requiredStringArray, trims and removes blank values,
throws badHomeAssistantRequest when none remain, and returns the comma-joined
IDs; update the history query’s filter_entity_id in queryParams to use this
helper.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 76e459b0-3b64-4734-83ff-04c13e58d380
📒 Files selected for processing (4)
src/providers/home_assistant/actions.tssrc/providers/home_assistant/executors.tssrc/providers/home_assistant/runtime-config.tssrc/providers/home_assistant/runtime.ts
…quest URLs normalizeBaseUrl accepts an instance URL with a path, so a Home Assistant behind a reverse proxy at /ha is a supported configuration. buildHomeAssistantUrl assigned the endpoint path over url.pathname, which dropped that prefix and sent every request to /api/... at the origin instead of /ha/api/.... Append to the base path instead. The WebSocket URL builder added alongside the registry actions already did this, so the two transports now agree.
…ctions The provider could read current state but had no time dimension: an agent could see that a sensor reads 21 degrees, not how it got there or what triggered a switch overnight. These five endpoints are documented in Home Assistant's REST API and were simply never covered. get_history exposes minimalResponse and noAttributes because a full-attribute history over several entities is large enough to matter for an agent's context budget. Home Assistant reads minimal_response, no_attributes, and skip_initial_state by presence rather than by value, so false omits the parameter instead of sending 0.
Home Assistant serves its editable config store over REST at /api/config/<component>/config/<key>, not over the WebSocket API. It is absent from the published REST API reference, which is why the provider covered "run this automation" but never "write this automation". The three domains differ only in the path segment, the input field carrying the key, and whether Home Assistant validates that key as a slug, so one descriptor parameterises shared read/write/delete handlers rather than nine near-copies. Together with validate_config this closes the automation loop an agent needs: discover device capabilities, validate a candidate config, write it, then read the logbook to confirm it fired. followUpActions wires that chain into the catalog so the sequence is discoverable. Both endpoints require an admin token and only see entries Home Assistant manages itself; the action descriptions say so, since a non-admin token fails at the provider rather than at input validation.
…tity filter The get_history output schema reused the /api/states shape, which requires entity_id and last_updated. Home Assistant does not return those for the rows between the first and last of a period once minimal_response is set, so the declared contract rejected real responses. A live 7846-point response validates against the corrected schema and fails against the previous one. History rows now use their own schema where only state and last_changed are required, and /api/states keeps the strict shape. Also normalize the entity filter before sending it. Home Assistant reads a missing filter_entity_id as every entity and queryParams drops empty strings, so a list that normalizes to nothing would silently widen a scoped query into a whole-instance dump. The schema's minItems already rejects an empty array, but the request should not depend on that alone, and a whitespace-only id passes the schema while still being unusable.
dcb3ffc to
6f94c16
Compare
Summary
/api/config/<component>/config/<key>buildHomeAssistantUrldropping the instance base pathFifteen actions in total, all REST. No WebSocket code is touched.
Problem
Two separate gaps, both in the REST surface.
The editable config store was missing entirely. Home Assistant serves automation, script, and scene configuration over REST, but at
/api/config/<component>/config/<key>rather than under the endpoints listed in the published REST API reference. The provider could therefore run an automation but never write one. That endpoint family is absent from the reference page, which is the most likely reason it was never covered — the eight actions the provider started with map exactly onto that page and nothing outside it.There was no time dimension.
/api/statesanswers "what is the temperature now"; nothing answered "how did it get there" or "what tripped that switch overnight".history,logbook,calendars, anderror_logare all on the published reference page and were simply never implemented.Config CRUD
automation,script, andscenediffer only in three respects: the path segment, the input field carrying the key, and whether Home Assistant validates that key as a slug (cv.slugfor script,cv.stringfor automation and scene). One descriptor captures those differences and parameterises shared read/write/delete handlers, rather than nine near-identical copies.The key travels in the path and Home Assistant injects it into the stored entry itself, so the request body is the bare config object.
POSTis create-or-update: writing to an unused key creates the entry, which is how the UI creates automations.Both endpoints require an admin token, and they only see entries Home Assistant manages itself — an automation defined in a separate YAML file or a package is not visible and returns not found. The action descriptions say so, because a non-admin token fails at the provider rather than at input validation.
This pairs with
validate_configfrom #227 to close the loop an agent needs for automation work: discover what a device can do, validate a candidate config, write it, then read the logbook to confirm it fired.followUpActionswires that sequence into the catalog so it is discoverable rather than something the caller has to know.History and diagnostics
get_historyexposesminimalResponseandnoAttributesbecause a full-attribute history across several entities is large enough to matter for an agent's context budget. Against a real instance, one entity over 24 hours returned 7219 points; the compact form drops each row from the full state object to{"state", "last_changed"}.Home Assistant reads
minimal_response,no_attributes, andskip_initial_stateby presence rather than by value, sofalseomits the parameter instead of sending0.significant_changes_onlyis read by value and defaults to true, so it is encoded normally.get_error_logneeds one caveat: Home Assistant registers that view only when the instance runs with file logging, so it can report not found on an otherwise healthy instance. Rather than surface a bare "Not Found", the handler maps that case to a message explaining why, and the action description repeats it.Base path fix
normalizeBaseUrlaccepts an instance URL with a path, so a Home Assistant behind a reverse proxy at/hais a supported configuration.buildHomeAssistantUrlassigned the endpoint path overurl.pathname, which dropped that prefix and sent every request to/api/...at the origin instead of/ha/api/....It now appends to the base path. The WebSocket URL builder added in #227 already did this, so the two transports agree. This is a separate commit; it predates both PRs, and the fifteen new paths here would have multiplied it.
Actions
get_automation_config/save_automation_config/delete_automation_configGET/POST/DELETE /api/config/automation/config/{id}get_script_config/save_script_config/delete_script_configGET/POST/DELETE /api/config/script/config/{key}get_scene_config/save_scene_config/delete_scene_configGET/POST/DELETE /api/config/scene/config/{id}check_configPOST /api/config/core/check_configget_historyGET /api/history/period/{timestamp}get_logbookGET /api/logbook/{timestamp}list_calendarsGET /api/calendarslist_calendar_eventsGET /api/calendars/{entity_id}get_error_logGET /api/error_logReference: https://developers.home-assistant.io/docs/api/rest
Verification
npm run fix-check,npm test(584 passing)check_configreturned valid;get_historyreturned 7219 points for one entity over 24 hours with the compact form visibly applied;get_logbookreturned 4417 entries;list_calendarsreturned an empty list on an instance with no calendar integration;get_error_logreturned the explanatory 404 described aboveoc_smoke_testentry in each domain: create returnedok, the read-back showed the key injected into the stored entry, delete returnedok, and each subsequent read reported not found. A non-slug script key was rejected at the provider before any request. A following scan of 3830 entities found nothing left behind.