Skip to content

refactor: move accessor from service to device#774

Open
lackas wants to merge 2 commits into
openviess:masterfrom
lackas:viacare-accessor-on-device
Open

refactor: move accessor from service to device#774
lackas wants to merge 2 commits into
openviess:masterfrom
lackas:viacare-accessor-on-device

Conversation

@lackas

@lackas lackas commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Splits the device-identity concern (installation/gateway/device IDs) from the HTTP/cache concern (OAuth, request building, caching). Service instances no longer hold an accessor — it lives on Device and PyViCareDeviceConfig and is passed at each call site.

Picked up from @CFenner's #628 on current master. The 8-month drift, especially #764's PACKAGE_NOT_PAID_FOR / DeviceCommunicationError / InternalServerError exception handling added to ViCareCachedService, made a fresh implementation cleaner than a rebase. Same design intent and API shape as #628closes #628.

Motivation

This is prep work for a gateway-scoped service that can serve multiple devices from a single bulk fetch (/features/installations/{id}/gateways/{serial}/features?includeDevicesFeatures=true). Today's per-device fetch_all_features() is called N times per coordinator refresh; with a gateway service it could collapse to 1.

Measured on my installation: the bulk endpoint covers every isEnabled=true feature on every device (deactivated features are excluded by default, but PyViCare's existing @handleNotSupported / isEnabled checks already make missing and disabled features behaviourally equivalent). So a per-gateway architecture is functionally lossless for HA's use case — see also home-assistant/core#173776 where this came up.

What changes

  • Device.__init__(accessor, service) — stores self.accessor, threads it through getProperty / setProperty
  • PyViCareDeviceConfig.__init__(accessor, service, ...)device_id derived from accessor.device_id; all as*() constructors pass self.accessor through
  • ViCareService and ViCareCachedService drop the accessor instance attribute; getProperty / setProperty / fetch_all_features / buildGetPropertyUrl / reboot_gateway take accessor as a parameter
  • get_available_burners(device) (free function in PyViCareHeatingDevice) takes a device instead of a service — its 4 callers updated accordingly
  • 21 sites in PyViCareRoomControl and 1 in PyViCareHeatingDevice that called self.service.getProperty/setProperty directly now go through the Device delegation (self.getProperty/setProperty), which was already the established pattern in the base class
  • ViCareCachedService keeps the full defensive try/except for PyViCareNotPaidForError, PyViCareDeviceCommunicationError, PyViCareInternalServerError introduced in fix: catch PyViCareNotPaidForError in auto-detection and diagnostics #764

What stays the same

Public API surface used by consumers (HA Core, custom integrations):

  • device.getProperty(name) / device.setProperty(name, action, data) — unchanged signatures
  • DeviceConfig.as*() accessors — unchanged signatures, same return types
  • DeviceConfig.getConfig() still returns the accessor

Code that constructs ViCareService / ViCareCachedService / PyViCareDeviceConfig directly will need the new signature. The PyViCare orchestrator itself handles the wiring.

Verification

  • All 729 tests pass (30 skipped, unchanged from master)
  • mypy PyViCare/ clean (the 3 missing-stub warnings are pre-existing env-only issues that CI handles)
  • ruff check PyViCare/ tests/ — all checks passed

Credit

Originally proposed by @CFenner in #628 — this PR carries the same design forward on current master.

closes #628

lackas added 2 commits June 15, 2026 16:58
Splits the device-identity concern (installation/gateway/device IDs) from
the HTTP/cache concern (OAuth, request building, caching). Service
instances no longer hold an accessor — it lives on Device and
PyViCareDeviceConfig and is passed at each call site.

Prep work for a future gateway-scoped service that can serve multiple
devices from a single bulk fetch.

Picked up from CFenner's openviess#628 on current master since the 8-month drift
(notably openviess#764's PACKAGE_NOT_PAID_FOR exception handling in
ViCareCachedService) made a fresh implementation cleaner than the
rebase. Same design and API shape — public surface
(device.getProperty/setProperty, DeviceConfig.as*) is unchanged.
- ViCareDeviceAccessor takes int as first arg per its annotation; pass 0
  instead of "[id]" in the typed setUp of test_PyViCareDeviceConfig
- Rename intentionally-unused mock params to _-prefix (accessor,
  requested_roles) so pylint doesn't flag unused-argument
@lackas lackas force-pushed the viacare-accessor-on-device branch from b969615 to 277fc78 Compare June 15, 2026 20:42
@lackas

lackas commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

Rebased to drop the deprecation-checker commit — it's now a separate single-purpose PR #775 since it's orthogonal to this refactor. The Format step will stay red here until #775 lands, same as on master since #707.

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