WIP: Build capacity map from get_connected_objects() (streaming prep, 1/5) #56

Manually merged
mat merged 0 commits from feat/capacity-map into main 2026-07-14 17:11:16 +00:00
Owner

Slice 1 of a 5-slice effort to bring real-time WebSocket streaming (power/switch/energy) to the Comwatt integration. This slice is pure prep — no behavior change.

What

Builds a capacityId -> (deviceId, nature, production) map on ComwattCoordinator by fetching get_connected_objects(site_id) each _fetch_all cycle and folding every capacity whose deviceId is non-null.

Why get_connected_objects and not get_devices

A live spike confirmed get_devices() hides the capacities of the grid meter and battery (their features are empty). get_connected_objects() returns every connected object (the co.{N} segment of each streamed capacityId) with all its capacities, each carrying deviceId. This is the only source that maps grid exchange (...withdrawal.data / ...injection.data) and battery (...battery_charge.data / ...battery_discharge.data) — confirmed by mapping withdrawal→23599, injection→23598, battery_charge→147223, battery_discharge→147224 on a real account.

Scope

  • coordinator.py: init capacity_map, reset once per _fetch_all, _try_fetch(get_connected_objects, site_id) + _fold_capacity_map.
  • conftest.py: mock default for get_connected_objects.
  • New test test_capacity_map_built_from_connected_objects.
  • No streaming, no entity/switch wiring, no manifest change — later slices consume the map.

Tests

44/44 passing, ruff + mypy clean. TDD (RED AttributeError: no attribute 'capacity_map' -> GREEN).

Stacking

This is slice 1/5. Slices 2-5 stack on this branch (feat/capacity-map).

Slice 1 of a 5-slice effort to bring real-time WebSocket streaming (power/switch/energy) to the Comwatt integration. This slice is pure prep — no behavior change. ## What Builds a `capacityId -> (deviceId, nature, production)` map on `ComwattCoordinator` by fetching `get_connected_objects(site_id)` each `_fetch_all` cycle and folding every capacity whose `deviceId` is non-null. ## Why get_connected_objects and not get_devices A live spike confirmed `get_devices()` hides the capacities of the grid meter and battery (their `features` are empty). `get_connected_objects()` returns every connected object (the `co.{N}` segment of each streamed `capacityId`) with all its capacities, each carrying `deviceId`. This is the only source that maps grid exchange (`...withdrawal.data` / `...injection.data`) and battery (`...battery_charge.data` / `...battery_discharge.data`) — confirmed by mapping withdrawal→23599, injection→23598, battery_charge→147223, battery_discharge→147224 on a real account. ## Scope - `coordinator.py`: init `capacity_map`, reset once per `_fetch_all`, `_try_fetch(get_connected_objects, site_id)` + `_fold_capacity_map`. - `conftest.py`: mock default for `get_connected_objects`. - New test `test_capacity_map_built_from_connected_objects`. - No streaming, no entity/switch wiring, no manifest change — later slices consume the map. ## Tests 44/44 passing, ruff + mypy clean. TDD (RED `AttributeError: no attribute 'capacity_map'` -> GREEN). ## Stacking This is slice 1/5. Slices 2-5 stack on this branch (`feat/capacity-map`).
Build capacity_map from get_connected_objects() on the coordinator
All checks were successful
Validate / lint-ruff (push) Successful in 7s
Validate / test-pytest (push) Successful in 3m3s
Validate / type-check-mypy (push) Successful in 3m8s
Validate / lint-ruff (pull_request) Successful in 7s
Validate / test-pytest (pull_request) Successful in 3m1s
Validate / type-check-mypy (pull_request) Successful in 3m8s
36828f43be
Slice 1 of the real-time streaming effort. get_devices() hides the
capacities of the grid meter and battery (their `features` are empty),
so get_connected_objects(site_id=...) is the only source that maps
every capacityId (including grid withdrawal/injection and battery
charge/discharge) to its owning deviceId.

Each _fetch_all cycle now fetches connected objects per site (via the
existing _try_fetch, so a failure yields None rather than crashing) and
folds their flat capacities into a fresh
`capacity_map: dict[str, tuple[device_id, nature, production]]` on the
coordinator, skipping capacities with no capacityId or a null deviceId
(unassigned physical clamps).

Pure prep: no streaming, no entity changes, nothing consumes the map
yet. Later slices route streamed measurements through it.

pytest: 44 passed; ruff + mypy clean.
mat manually merged commit 36828f43be into main 2026-07-14 17:11:16 +00:00
mat referenced this pull request from a commit 2026-07-25 16:37:12 +00:00
mat referenced this pull request from a commit 2026-07-25 16:40:44 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
mat/homeassistant-comwatt!56
No description provided.