Skip to content

Coordinator: add _async_setup() and collect set of existing devices#982

Merged
bouwew merged 29 commits intomainfrom
coord-restore-state
Dec 5, 2025
Merged

Coordinator: add _async_setup() and collect set of existing devices#982
bouwew merged 29 commits intomainfrom
coord-restore-state

Conversation

@bouwew
Copy link
Contributor

@bouwew bouwew commented Dec 2, 2025

Summary by CodeRabbit

  • Refactor

    • Added a pre-setup stage that preloads registry-tracked devices and maintains per-instance device state; coordinator setup exposed as a distinct lifecycle step.
  • Bug Fixes

    • Improved connection and update flow with clearer error translation and retry behavior.
    • Safer detection and cleanup of removed/orphaned devices; update intervals now adjust and are logged.
  • Tests

    • Added tests for coordinator connection error mapping and cleanup of removed devices.
  • Chore

    • Changelog updated to note improved automatic device deletion.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 2, 2025

Warning

Rate limit exceeded

@bouwew has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 23 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between ebc5d3b and 6367335.

📒 Files selected for processing (2)
  • custom_components/plugwise/coordinator.py (5 hunks)
  • tests/components/plugwise/test_init.py (7 hunks)

Walkthrough

Adds per-instance device-tracking attributes and a new async setup step to preload stored devices; moves connection establishment into the coordinator update flow using api.connect()/version; refactors device-delta computation and hardens device removal with gateway and via_device_id guards.

Changes

Cohort / File(s) Summary
Coordinator changes
custom_components/plugwise/coordinator.py
Added _current_devices: set[str], _stored_devices: set[str], new_devices: set[str]; added async def _async_setup(self) to preload stored devices from the device registry; _async_update_data now calls api.connect() to obtain version and adjust update interval, then api.async_update(); maps InvalidSetupError/auth/unsupported errors to ConfigEntryError and other errors to UpdateFailed; refactored _async_add_remove_devices to compute deltas against _current_devices/_stored_devices and call _async_remove_devices; tightened _async_remove_devices with gateway / via_device_id guards and defensive None checks.
Tests
tests/components/plugwise/test_init.py
Updated imports (added InvalidSetupError, PlugwiseDataUpdateCoordinator, ConfigEntryError, UpdateFailed); narrowed test_gateway_config_entry_not_ready parametrize; added test_coordinator_connect_exceptions to validate exception-to-error translations; added test_delete_removed_device to verify registry cleanup; updated type annotation to er.RegistryEntry.
Changelog
CHANGELOG.md
Added note about improved automatic deletion of devices removed from backend output referencing PR #982.

Sequence Diagram(s)

sequenceDiagram
  participant C as PlugwiseDataUpdateCoordinator
  participant DR as DeviceRegistry
  participant API as PlugwiseAPI
  participant RO as RegistryOps

  Note over C,DR: _async_setup() — preload stored devices
  C->>DR: query devices for config_entry (domain=plugwise)
  DR-->>C: return stored device IDs (_stored_devices)

  Note over C,API: periodic _async_update_data()
  C->>API: api.connect()
  alt connect success
    API-->>C: Version / connected
    C->>API: api.async_update()
    API-->>C: latest device data (ids)
    C->>C: derive current_devices (from _current/_stored), compute new_devices
    alt new devices present
      C->>RO: create/update entries for new_devices
    end
    alt devices removed
      C->>RO: remove orphaned devices via gateway (if gateway present & via_device_id known)
    end
  else connect failure
    API-->>C: error (InvalidAuthentication/InvalidSetup/Unsupported/Other)
    C-->>C: raise ConfigEntryError or UpdateFailed (mapped)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect custom_components/plugwise/coordinator.py for race conditions between _async_setup and _async_update_data.
  • Verify device-registry query/filter logic and correctness of removal targets (gateway / via_device_id).
  • Review exception mapping and the new/updated tests in tests/components/plugwise/test_init.py.

Possibly related PRs

Suggested reviewers

  • CoMPaTech

Poem

🐇 I hop through registries, counting every light,
I call the API, then tidy up at night.
Missing plugs vanish, new ones get their name,
I tidy the map and nibble on some fame. 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: adding _async_setup() method and implementing device set collection to track existing devices.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
custom_components/plugwise/coordinator.py (1)

95-99: Confirm/connect error handling now that _connect moved into _async_setup.

_async_setup calls await self._connect() directly. If self.api.connect() can raise ConnectionFailedError, InvalidAuthentication, etc., those will now bubble out of _async_setup instead of being translated to UpdateFailed / ConfigEntryError as in _async_update_data. That may change how setup failures are surfaced in HA.

Consider mirroring the same exception mapping used around async_update() (or factoring that mapping into a shared helper) so connection failures during initial setup still produce the expected, translated errors.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e8759b and dd073f3.

📒 Files selected for processing (1)
  • custom_components/plugwise/coordinator.py (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-06-22T08:19:52.059Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Prepare and validate pre-commit

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error while testing for Development HA-core:

⚠️ Warning: Incompatible while testing against dev HA-core.
✔️ Success: No problem with testing against released HA-core.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dd073f3 and d309f67.

📒 Files selected for processing (1)
  • custom_components/plugwise/coordinator.py (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-06-22T08:19:52.059Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
📚 Learning: 2024-10-08T15:35:25.180Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.

Applied to files:

  • custom_components/plugwise/coordinator.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Setup for HA-core (release/master)
  • GitHub Check: Setup for HA-core (dev)
🔇 Additional comments (1)
custom_components/plugwise/coordinator.py (1)

100-109: Device registry logic correctly extracts Plugwise identifiers.

The set comprehension properly extracts identifier[1] (the string ID) from device entries, filtering by DOMAIN. This ensures _current_devices is a set[str] that aligns with set(data) in line 149, resolving the type mismatch noted in previous reviews.

Based on past review comments, the logic now correctly populates the set with string identifiers rather than DeviceEntry objects.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error while testing for Development HA-core:

⚠️ Warning: Incompatible while testing against dev HA-core.
✔️ Success: No problem with testing against released HA-core.

@bouwew bouwew force-pushed the coord-restore-state branch from 991ff61 to f739c02 Compare December 2, 2025 11:35
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error while testing for Development HA-core:

⚠️ Warning: Incompatible while testing against dev HA-core.
✔️ Success: No problem with testing against released HA-core.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
custom_components/plugwise/coordinator.py (1)

151-151: Consider adding a comment to clarify the first-run behavior.

The ternary expression correctly handles two scenarios:

  • First run (_current_devices is empty): Compare against _stored_devices to detect devices that exist in the registry but aren't reported by the API
  • Subsequent runs: Compare against _current_devices to detect devices that disappeared since the last update

While the logic is sound, the conditional is dense. A brief inline comment would help future maintainers understand the first-run special case.

-        removed_devices = (self._stored_devices - set(data)) if not self._current_devices else (self._current_devices - set(data))
+        # On first run use registry devices, otherwise use previous data snapshot
+        removed_devices = (self._stored_devices - set(data)) if not self._current_devices else (self._current_devices - set(data))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 991ff61 and f739c02.

📒 Files selected for processing (1)
  • custom_components/plugwise/coordinator.py (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-06-22T08:19:52.059Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
📚 Learning: 2024-10-08T15:35:25.180Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.

Applied to files:

  • custom_components/plugwise/coordinator.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Setup for HA-core (release/master)
  • GitHub Check: Setup for HA-core (dev)
🔇 Additional comments (1)
custom_components/plugwise/coordinator.py (1)

77-77: LGTM: Instance variable properly initialized.

The _stored_devices set is correctly typed and initialized, consistent with the existing device tracking patterns in the coordinator.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error while testing for Development HA-core:

⚠️ Warning: Incompatible while testing against dev HA-core.
✔️ Success: No problem with testing against released HA-core.

@bouwew bouwew force-pushed the coord-restore-state branch from 3965408 to bccae2a Compare December 2, 2025 13:32
@bouwew bouwew changed the title Coordinator: add _async_setup() and collect set of existing device Coordinator: add _async_setup() and collect set of existing devices Dec 2, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
custom_components/plugwise/coordinator.py (1)

114-126: _async_setup() is still not invoked anywhere.

The method implementation correctly extracts Plugwise device IDs from the registry, but it's never called. Without invoking this before the first data update, _stored_devices remains empty, and the removal logic at lines 153-157 cannot detect orphaned devices on startup.

Ensure this is called before async_config_entry_first_refresh() in your integration setup (typically in __init__.py).

#!/bin/bash
# Verify if _async_setup is called anywhere in the integration
rg -n '_async_setup' custom_components/plugwise/ --type=py
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3965408 and bccae2a.

📒 Files selected for processing (1)
  • custom_components/plugwise/coordinator.py (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-06-22T08:19:52.059Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
📚 Learning: 2024-10-08T15:35:25.180Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.

Applied to files:

  • custom_components/plugwise/coordinator.py
🪛 Ruff (0.14.7)
custom_components/plugwise/coordinator.py

40-40: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


41-41: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


42-42: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Setup for HA-core (dev)
  • GitHub Check: Setup for HA-core (release/master)
🔇 Additional comments (3)
custom_components/plugwise/coordinator.py (3)

101-112: LGTM!

The connection logic correctly sets _connected based on the API response and configures the update interval from defaults or custom options.


127-133: LGTM!

The lazy connection pattern and data fetch logic are sound. Connection errors properly propagate through _connect().


153-157: Removal logic depends on _async_setup() being called first.

The conditional logic correctly uses _stored_devices when _current_devices is empty (first run). However, since _async_setup() is never invoked, _stored_devices remains empty, so removed_devices will always be an empty set on the first update cycle.

This means orphaned devices (removed from the Plugwise system while HA was offline) won't be cleaned up on startup as intended.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error while testing for Development HA-core:

⚠️ Warning: Incompatible while testing against dev HA-core.
✔️ Success: No problem with testing against released HA-core.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error while testing for Development HA-core:

⚠️ Warning: Incompatible while testing against dev HA-core.
✔️ Success: No problem with testing against released HA-core.

@bouwew bouwew marked this pull request as ready for review December 2, 2025 18:43
@bouwew bouwew requested a review from a team as a code owner December 2, 2025 18:43
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae3df01 and 43a4a51.

📒 Files selected for processing (1)
  • custom_components/plugwise/coordinator.py (5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-06-22T08:19:52.059Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
📚 Learning: 2024-10-08T15:35:25.180Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.

Applied to files:

  • custom_components/plugwise/coordinator.py
🪛 GitHub Actions: Test PR against HA-core
custom_components/plugwise/coordinator.py

[error] 86-86: W293 Blank line contains whitespace. Remove trailing whitespace on the blank line.

🔇 Additional comments (4)
custom_components/plugwise/coordinator.py (4)

79-81: LGTM! Instance attribute initialization is correct.

The mutable class attributes have been properly moved to instance initialization, ensuring each coordinator instance has its own device tracking sets. This resolves the shared-state concern and the RUF012 static analysis warning.


133-152: Data update logic is sound.

The method correctly ensures connection before fetching data, with proper error handling that maps specific exceptions to appropriate Home Assistant error types. The debug logging on line 151 provides good visibility into the data fetch operation.

Note: This method depends on _async_setup() having been called first to properly populate _stored_devices for device removal detection.


154-162: Device delta computation is correctly implemented.

The logic properly:

  • Identifies new devices by comparing against _current_devices (line 158)
  • Falls back to _stored_devices on the first update when _current_devices is empty (line 159)
  • Updates the tracking state (line 160)
  • Triggers removal for devices that disappeared (line 161)

This approach ensures proper device lifecycle management across coordinator updates.


173-174: Good defensive programming.

The early return when the gateway device is missing prevents potential None reference errors on line 176 and ensures the removal operation only proceeds with a valid gateway reference. This is solid error prevention for edge cases where the gateway might be removed from the registry.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
tests/components/plugwise/test_init.py (1)

121-154: Exception-translation test covers all mapped _connect errors

This parametrized test neatly verifies that:

  • auth/setup/unsupported exceptions map to ConfigEntryError, and
  • connection/XML/response failures map to UpdateFailed.

Using exception classes as side_effect is valid with MagicMock/AsyncMock, and the membership check for computing expected_exception is fine.

If you want to make the test table-driven, you could parametrize the expected exception instead of computing it, but that’s purely stylistic.

custom_components/plugwise/coordinator.py (1)

85-129: Exception translation in _connect looks correct; consider chaining causes

The mapping from Plugwise exceptions to ConfigEntryError vs UpdateFailed matches the new tests and gives clear error semantics, and update_interval adjustment (including CONF_SCAN_INTERVAL override) preserves existing behavior.

Ruff B904 points out that these re-raises currently drop the original exception context. If you want better traceability (and to satisfy the linter), you can capture the original error and chain it:

-        except ConnectionFailedError:
-            raise UpdateFailed(
+        except ConnectionFailedError as err:
+            raise UpdateFailed(
                 translation_domain=DOMAIN,
                 translation_key="failed_to_connect",
-            )
-        except InvalidAuthentication:
-            raise ConfigEntryError(
+            ) from err
+        except InvalidAuthentication as err:
+            raise ConfigEntryError(
                 translation_domain=DOMAIN,
                 translation_key="authentication_failed",
-            )
-        except InvalidSetupError:
-            raise ConfigEntryError(
+            ) from err
+        except InvalidSetupError as err:
+            raise ConfigEntryError(
                 translation_domain=DOMAIN,
                 translation_key="invalid_setup",
-            )
-        except (InvalidXMLError, ResponseError):
-            raise UpdateFailed(
+            ) from err
+        except (InvalidXMLError, ResponseError) as err:
+            raise UpdateFailed(
                 translation_domain=DOMAIN,
                 translation_key="invalid_xml_data",
-            )
-        except UnsupportedDeviceError:
-            raise ConfigEntryError(
+            ) from err
+        except UnsupportedDeviceError as err:
+            raise ConfigEntryError(
                 translation_domain=DOMAIN,
                 translation_key="unsupported_firmware",
-            )
+            ) from err

If you prefer to hide implementation details from logs, you can instead use from None in these raises.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43a4a51 and d44278d.

📒 Files selected for processing (2)
  • custom_components/plugwise/coordinator.py (5 hunks)
  • tests/components/plugwise/test_init.py (6 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-06-22T08:19:52.059Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 982
File: custom_components/plugwise/coordinator.py:120-132
Timestamp: 2025-12-03T10:16:54.874Z
Learning: In Home Assistant integrations, when a coordinator extends DataUpdateCoordinator and defines an `_async_setup()` method, it is automatically called by the framework during `async_config_entry_first_refresh()` before the first data update. The framework calls `__wrap_async_setup()` which invokes `_async_setup()`, so there's no need to manually call it during coordinator initialization.
📚 Learning: 2025-08-23T16:26:34.700Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 906
File: tests/components/plugwise/test_binary_sensor.py:9-16
Timestamp: 2025-08-23T16:26:34.700Z
Learning: The Plugwise integration test files import utilities like `snapshot_platform`, `MockConfigEntry`, and `async_fire_time_changed` from `tests.common`, which is from the Home Assistant core repository at https://github.com/home-assistant/core/blob/master/tests/common.py, not from local test files.

Applied to files:

  • tests/components/plugwise/test_init.py
📚 Learning: 2024-10-08T15:35:25.180Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 734
File: tests/components/plugwise/test_config_flow.py:58-58
Timestamp: 2024-10-08T15:35:25.180Z
Learning: In `tests/components/plugwise/test_config_flow.py`, the versions are mocked firmware versions for testing purposes and do not need to match the software version in `manifest.json`.

Applied to files:

  • tests/components/plugwise/test_init.py
📚 Learning: 2025-12-03T10:16:54.874Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 982
File: custom_components/plugwise/coordinator.py:120-132
Timestamp: 2025-12-03T10:16:54.874Z
Learning: In Home Assistant integrations, when a coordinator extends DataUpdateCoordinator and defines an `_async_setup()` method, it is automatically called by the framework during `async_config_entry_first_refresh()` before the first data update. The framework calls `__wrap_async_setup()` which invokes `_async_setup()`, so there's no need to manually call it during coordinator initialization.

Applied to files:

  • tests/components/plugwise/test_init.py
  • custom_components/plugwise/coordinator.py
📚 Learning: 2024-10-08T15:35:25.180Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.

Applied to files:

  • custom_components/plugwise/coordinator.py
🪛 Ruff (0.14.7)
custom_components/plugwise/coordinator.py

93-96: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


98-101: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


103-106: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


108-111: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


113-116: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Prepare and validate pre-commit
🔇 Additional comments (9)
tests/components/plugwise/test_init.py (3)

6-34: New imports correctly reflect updated test surface

InvalidSetupError, PlugwiseDataUpdateCoordinator, ConfigEntryError, and UpdateFailed are all used in the new tests and correctly scoped. No issues here.


101-119: Gateway not-ready test now focused on transient PlugwiseError

Restricting side_effect to PlugwiseError and asserting ConfigEntryState.SETUP_RETRY matches the current flow (PlugwiseErrorUpdateFailed → coordinator retry). The expectations look correct and aligned with the new coordinator behavior.


181-193: RegistryEntry annotation swap is fine

Changing the annotation to er.RegistryEntry matches the imported alias and is type-equivalent. No behavioral impact.

custom_components/plugwise/coordinator.py (6)

5-14: New InvalidSetupError import is consistent

InvalidSetupError is now used in _connect’s exception mapping; the import is correct and necessary.


40-43: Per‑instance device sets fix shared-state risk

Defining _current_devices, _stored_devices, and new_devices as typed attributes and initializing them in __init__ gives each coordinator its own sets and removes the earlier shared mutable class-state concern. This also aligns with the prior Ruff RUF012 hint.

Also applies to: 80-82


130-141: _async_setup correctly preloads stored device ids

Using the device registry to build _stored_devices from (DOMAIN, identifier) pairs for this config entry provides the right baseline for detecting removed devices on the first update. This integrates well with the HA DataUpdateCoordinator lifecycle where _async_setup runs before the first refresh. Based on learnings, this matches the intended framework behavior.


143-157: Updated _async_update_data flow aligns connect + update responsibilities

Ensuring _connect() is called when not self._connected before api.async_update(), then delegating diffing to _async_add_remove_devices and surfacing a single UpdateFailed path for PlugwiseError keeps the coordinator responsibilities clear and matches the tests’ expectations for SETUP_RETRY behavior. The extra DEBUG log of the data is fine for troubleshooting.


159-167: Device delta computation is consistent with stored vs current devices

Using:

  • set_of_data = set(data) for current ids,
  • self.new_devices = set_of_data - self._current_devices, and
  • current_devices = self._stored_devices if not self._current_devices else self._current_devices

means:

  • on the first cycle, removals are detected relative to the registry (_stored_devices), and
  • on subsequent cycles, removals are detected relative to the previous update (_current_devices),

while always updating _current_devices to the latest keys. This matches the goal of cleaning up devices that disappear from the backend while still tracking newly seen ids in new_devices.


176-182: Gateway None‑guard in _async_remove_devices is a safe defensive check

Guarding against a missing gateway_device avoids a potential None dereference, and caching via_device_id keeps the later loop simpler. The subsequent filtering on via_device_id and identifier[1] not in data is unchanged in behavior.

@bouwew bouwew force-pushed the coord-restore-state branch from 35bf752 to ffd1d98 Compare December 3, 2025 15:28
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
custom_components/plugwise/coordinator.py (2)

9-11: _connect error mapping and interval handling are consistent; docstring could be tightened.

The new handling for InvalidSetupError and the mapping of XML/response issues to UpdateFailed with translation keys is aligned with HA’s error model, and deferring interval calculation to after a successful connect via DEFAULT_SCAN_INTERVAL plus optional CONF_SCAN_INTERVAL override is sound.

Minor nit: the docstring currently says “Returns a Version object when connection succeeds”, but the method does not return that object; consider rephrasing to describe the side effects (setting _connected/update_interval) instead.

Please double-check that the translation keys "invalid_setup" and "invalid_xml_data" are present in your integration’s translation files so the new errors surface correctly.

Also applies to: 86-111, 118-128


162-167: Consider basing new_devices on current_devices so _stored_devices affects first-run detection.

Right now:

set_of_data = set(data)
self.new_devices = set_of_data - self._current_devices
current_devices = self._stored_devices if not self._current_devices else self._current_devices
self._current_devices = set_of_data

On the first update, _current_devices is empty, so new_devices becomes set_of_data and _stored_devices only influences removal detection. If the intent of _stored_devices is also to avoid treating already-registered devices as “new” on the first cycle, you could instead compute new_devices from current_devices:

-        set_of_data = set(data)
-        self.new_devices = set_of_data - self._current_devices
-        current_devices = self._stored_devices if not self._current_devices else self._current_devices
-        self._current_devices = set_of_data
+        set_of_data = set(data)
+        current_devices = (
+            self._stored_devices if not self._current_devices else self._current_devices
+        )
+        self.new_devices = set_of_data - current_devices
+        self._current_devices = set_of_data

This preserves existing behavior on subsequent updates while letting _stored_devices influence new-device detection on the very first run.

Please confirm whether new_devices is meant to exclude pre-existing registry devices on the first update; if not, the current logic is fine and this change can be skipped.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d44278d and ffd1d98.

📒 Files selected for processing (1)
  • custom_components/plugwise/coordinator.py (5 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-06-22T08:19:52.059Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 982
File: custom_components/plugwise/coordinator.py:120-132
Timestamp: 2025-12-03T10:16:54.874Z
Learning: In Home Assistant integrations, when a coordinator extends DataUpdateCoordinator and defines an `_async_setup()` method, it is automatically called by the framework during `async_config_entry_first_refresh()` before the first data update. The framework calls `__wrap_async_setup()` which invokes `_async_setup()`, so there's no need to manually call it during coordinator initialization.
📚 Learning: 2025-12-03T10:16:54.874Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 982
File: custom_components/plugwise/coordinator.py:120-132
Timestamp: 2025-12-03T10:16:54.874Z
Learning: In Home Assistant integrations, when a coordinator extends DataUpdateCoordinator and defines an `_async_setup()` method, it is automatically called by the framework during `async_config_entry_first_refresh()` before the first data update. The framework calls `__wrap_async_setup()` which invokes `_async_setup()`, so there's no need to manually call it during coordinator initialization.

Applied to files:

  • custom_components/plugwise/coordinator.py
📚 Learning: 2024-10-08T15:35:25.180Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.

Applied to files:

  • custom_components/plugwise/coordinator.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Setup for HA-core (dev)
  • GitHub Check: Setup for HA-core (release/master)
🔇 Additional comments (4)
custom_components/plugwise/coordinator.py (4)

41-43: Per-instance device tracking sets look good.

Annotating _current_devices, _stored_devices, and new_devices at class scope and initializing them as fresh sets in __init__ correctly avoids shared mutable state between coordinator instances and resolves the earlier RUF012 concern.

Also applies to: 80-82


130-142: Registry-based _stored_devices initialization matches downstream set logic.

Building _stored_devices as the set of (DOMAIN, id) identifiers from device_entries ensures type compatibility with set(data) in _async_add_remove_devices and avoids the earlier DeviceEntry vs str mismatch.

Based on learnings, this will be invoked automatically via DataUpdateCoordinator.async_config_entry_first_refresh(), so no extra manual call is needed.


143-157: Connect-before-update flow in _async_update_data is appropriate.

Guarding on self._connected and calling _connect() once before api.async_update(), while mapping any PlugwiseError to UpdateFailed with a clear translation key, fits the coordinator pattern and keeps connection and data-fetch concerns separated. The delegated call to _async_add_remove_devices after a successful fetch is also clean.


178-182: Gateway None-guard in _async_remove_devices prevents crashes.

Early-returning when gateway_device is None and then caching via_device_id = gateway_device.id before the loop avoids a potential AttributeError and keeps the subsequent orphan-removal loop focused on valid gateway-linked devices only.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
custom_components/plugwise/coordinator.py (1)

85-89: Exception translation and interval handling in _connect() look correct; docstring is slightly misleading.

Mapping Plugwise connection errors to ConfigEntryError vs UpdateFailed matches Home Assistant expectations, and dynamically adjusting update_interval (with an options override) is coherent with the rest of the integration. Only nit: the docstring says “Returns a Version object when connection succeeds” while _connect() itself returns None and just uses the returned Version to set _connected and the interval—consider rephrasing to describe the side effects rather than a return value.

Also applies to: 91-111, 118-128

tests/components/plugwise/test_init.py (1)

121-153: test_coordinator_connect_exceptions accurately validates _connect()’s exception mapping.

Parametrizing over the Plugwise exception classes and checking for ConfigEntryError only for InvalidAuthentication, InvalidSetupError, and UnsupportedDeviceError mirrors the _connect() except blocks; the other exceptions appropriately map to UpdateFailed. If you ever want to make the expectation logic more explicit, you could replace the in (...) check with a small mapping dict {InvalidAuthentication: ConfigEntryError, ...} but that’s purely stylistic.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ffd1d98 and a983383.

📒 Files selected for processing (2)
  • custom_components/plugwise/coordinator.py (5 hunks)
  • tests/components/plugwise/test_init.py (6 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-06-22T08:19:52.059Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 982
File: custom_components/plugwise/coordinator.py:120-132
Timestamp: 2025-12-03T10:16:54.874Z
Learning: In Home Assistant integrations, when a coordinator extends DataUpdateCoordinator and defines an `_async_setup()` method, it is automatically called by the framework during `async_config_entry_first_refresh()` before the first data update. The framework calls `__wrap_async_setup()` which invokes `_async_setup()`, so there's no need to manually call it during coordinator initialization.
📚 Learning: 2025-12-03T10:16:54.874Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 982
File: custom_components/plugwise/coordinator.py:120-132
Timestamp: 2025-12-03T10:16:54.874Z
Learning: In Home Assistant integrations, when a coordinator extends DataUpdateCoordinator and defines an `_async_setup()` method, it is automatically called by the framework during `async_config_entry_first_refresh()` before the first data update. The framework calls `__wrap_async_setup()` which invokes `_async_setup()`, so there's no need to manually call it during coordinator initialization.

Applied to files:

  • custom_components/plugwise/coordinator.py
  • tests/components/plugwise/test_init.py
📚 Learning: 2024-10-08T15:35:25.180Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.

Applied to files:

  • custom_components/plugwise/coordinator.py
📚 Learning: 2025-08-23T16:26:34.700Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 906
File: tests/components/plugwise/test_binary_sensor.py:9-16
Timestamp: 2025-08-23T16:26:34.700Z
Learning: The Plugwise integration test files import utilities like `snapshot_platform`, `MockConfigEntry`, and `async_fire_time_changed` from `tests.common`, which is from the Home Assistant core repository at https://github.com/home-assistant/core/blob/master/tests/common.py, not from local test files.

Applied to files:

  • tests/components/plugwise/test_init.py
📚 Learning: 2024-10-08T15:35:25.180Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 734
File: tests/components/plugwise/test_config_flow.py:58-58
Timestamp: 2024-10-08T15:35:25.180Z
Learning: In `tests/components/plugwise/test_config_flow.py`, the versions are mocked firmware versions for testing purposes and do not need to match the software version in `manifest.json`.

Applied to files:

  • tests/components/plugwise/test_init.py
🧬 Code graph analysis (2)
custom_components/plugwise/coordinator.py (1)
custom_components/plugwise/select.py (1)
  • options (146-148)
tests/components/plugwise/test_init.py (1)
custom_components/plugwise/coordinator.py (1)
  • _connect (85-128)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Setup for HA-core (dev)
  • GitHub Check: Setup for HA-core (release/master)
🔇 Additional comments (5)
custom_components/plugwise/coordinator.py (3)

40-43: Per-instance device tracking state is correctly scoped to the coordinator.

Moving _current_devices, _stored_devices, and new_devices initialization into __init__ avoids shared mutable class state and aligns with how _async_add_remove_devices() uses these sets over the coordinator lifecycle. No further changes needed here.

Also applies to: 80-82


130-142: Preloading _stored_devices from the device registry and the updated refresh flow look consistent.

_async_setup() now builds _stored_devices as a set of Plugwise identifiers from DeviceEntry.identifiers, which matches the key type used in data and allows first-run removal detection to work. _async_update_data() correctly ensures a connection via _connect() before calling async_update() and normalizes any PlugwiseError into a single UpdateFailed("data_incomplete_or_missing"), which simplifies caller handling. This all fits the DataUpdateCoordinator pattern; no issues spotted.

Also applies to: 143-153


159-169: Device delta computation and guarded removal logic look robust.

Using set(data) for keys, self.new_devices = set_of_data - self._current_devices, and falling back to _stored_devices on the first run gives the expected behavior: all currently reported devices are “new” at startup, while removals are derived from previously stored identifiers. The _async_remove_devices() guard for a missing gateway device avoids a potential None-dereference, and filtering by via_device_id ensures only child devices are removed while the gateway itself is preserved. The implementation matches the intent described in the changelog and the associated tests.

Also applies to: 171-184

tests/components/plugwise/test_init.py (2)

6-14: Imports and type annotation changes line up with the new coordinator behavior.

Bringing in InvalidSetupError, PlugwiseDataUpdateCoordinator, ConfigEntryError, and UpdateFailed gives the tests direct access to the coordinator and its translated exceptions, and switching the migration helper annotation to er.RegistryEntry matches the existing entity_registry as er import. All of this is consistent and keeps the tests tightly coupled to the current APIs.

Also applies to: 19-20, 31-34, 191-191


101-104: Gateway “not ready” test now cleanly asserts the retry state.

Constraining side_effect to PlugwiseError and asserting ConfigEntryState.SETUP_RETRY directly matches the new _async_update_data() behavior (wrapping PlugwiseError in UpdateFailed and causing Home Assistant to retry setup). The test remains focused and readable.

Also applies to: 111-118

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a983383 and fb05702.

📒 Files selected for processing (2)
  • custom_components/plugwise/coordinator.py (5 hunks)
  • tests/components/plugwise/test_init.py (7 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-06-22T08:19:52.059Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 982
File: custom_components/plugwise/coordinator.py:120-132
Timestamp: 2025-12-03T10:16:54.874Z
Learning: In Home Assistant integrations, when a coordinator extends DataUpdateCoordinator and defines an `_async_setup()` method, it is automatically called by the framework during `async_config_entry_first_refresh()` before the first data update. The framework calls `__wrap_async_setup()` which invokes `_async_setup()`, so there's no need to manually call it during coordinator initialization.
📚 Learning: 2025-08-23T16:26:34.700Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 906
File: tests/components/plugwise/test_binary_sensor.py:9-16
Timestamp: 2025-08-23T16:26:34.700Z
Learning: The Plugwise integration test files import utilities like `snapshot_platform`, `MockConfigEntry`, and `async_fire_time_changed` from `tests.common`, which is from the Home Assistant core repository at https://github.com/home-assistant/core/blob/master/tests/common.py, not from local test files.

Applied to files:

  • tests/components/plugwise/test_init.py
📚 Learning: 2024-10-08T15:35:25.180Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 734
File: tests/components/plugwise/test_config_flow.py:58-58
Timestamp: 2024-10-08T15:35:25.180Z
Learning: In `tests/components/plugwise/test_config_flow.py`, the versions are mocked firmware versions for testing purposes and do not need to match the software version in `manifest.json`.

Applied to files:

  • tests/components/plugwise/test_init.py
📚 Learning: 2025-12-03T10:16:54.874Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 982
File: custom_components/plugwise/coordinator.py:120-132
Timestamp: 2025-12-03T10:16:54.874Z
Learning: In Home Assistant integrations, when a coordinator extends DataUpdateCoordinator and defines an `_async_setup()` method, it is automatically called by the framework during `async_config_entry_first_refresh()` before the first data update. The framework calls `__wrap_async_setup()` which invokes `_async_setup()`, so there's no need to manually call it during coordinator initialization.

Applied to files:

  • tests/components/plugwise/test_init.py
  • custom_components/plugwise/coordinator.py
📚 Learning: 2024-10-08T15:35:25.180Z
Learnt from: bouwew
Repo: plugwise/plugwise-beta PR: 671
File: custom_components/plugwise/binary_sensor.py:0-0
Timestamp: 2024-10-08T15:35:25.180Z
Learning: The suggestion to initialize `devices` directly in the setup function was reverted by user bouwew. Future suggestions should consider the project's specific requirements and preferences more closely.

Applied to files:

  • custom_components/plugwise/coordinator.py
🧬 Code graph analysis (1)
tests/components/plugwise/test_init.py (2)
custom_components/plugwise/coordinator.py (1)
  • _connect (85-128)
tests/components/plugwise/conftest.py (2)
  • mock_smile_anna (240-263)
  • mock_smile_adam_heat_cool (185-208)
🪛 GitHub Actions: Test PR against HA-core
tests/components/plugwise/test_init.py

[error] 406-406: Ruff: F841 Local variable 'data' is assigned to but never used.

🪛 GitHub Check: SonarCloud Code Analysis
tests/components/plugwise/test_init.py

[warning] 406-406: Remove the unused local variable "data".

See more on https://sonarcloud.io/project/issues?id=plugwise_plugwise-beta&issues=AZroQwFZ-XIEqsF-FXgA&open=AZroQwFZ-XIEqsF-FXgA&pullRequest=982

🪛 Ruff (0.14.7)
tests/components/plugwise/test_init.py

406-406: Local variable data is assigned to but never used

Remove assignment to unused variable data

(F841)

🔇 Additional comments (11)
tests/components/plugwise/test_init.py (6)

6-14: LGTM!

The imports correctly align with the new coordinator exception handling being tested. InvalidSetupError is now mapped to ConfigEntryError in the coordinator.


17-33: LGTM!

New imports for PlugwiseDataUpdateCoordinator, ConfigEntryError, and UpdateFailed are properly added to support the new exception translation tests.


103-118: LGTM!

Test correctly verifies that a PlugwiseError during async_update results in ConfigEntryState.SETUP_RETRY. The simplified parametrization is cleaner.


121-149: LGTM!

Comprehensive parameterized test that verifies all exception mappings in _connect():

  • ConnectionFailedErrorUpdateFailed
  • InvalidAuthentication, InvalidSetupError, UnsupportedDeviceErrorConfigEntryError
  • InvalidXMLError, ResponseErrorUpdateFailed

This aligns with the coordinator implementation in lines 92-116.


187-187: LGTM!

Type annotation correctly uses the er alias that's already imported, improving consistency with other usages in the file.


387-413: Test logic looks correct for verifying device cleanup.

The test correctly:

  1. Pre-creates an orphan device before setup
  2. Verifies it exists in the registry
  3. Runs component setup
  4. Verifies the orphan device is removed

This exercises the _async_remove_devices path in the coordinator.

custom_components/plugwise/coordinator.py (5)

41-43: LGTM!

Instance attributes are now properly declared as type annotations at class level and initialized in __init__, resolving the previous RUF012 concern about mutable class-level defaults.

Also applies to: 80-82


85-128: LGTM!

The _connect method correctly:

  • Maps exceptions to appropriate HA error types (ConfigEntryError for auth/setup/device issues, UpdateFailed for transient connection/parsing issues)
  • Sets _connected based on receiving a valid Version object
  • Configures update_interval from defaults or user options

130-141: LGTM!

The _async_setup method correctly extracts Plugwise device identifiers from the device registry, populating _stored_devices with set[str] values that are comparable to set(data). Based on learnings, this is automatically invoked by the HA framework during async_config_entry_first_refresh().


159-169: LGTM!

The device delta logic is well-structured:

  • new_devices correctly identifies devices in data but not in _current_devices
  • Falls back to _stored_devices on first run when _current_devices is empty
  • Triggers removal only when there are actual devices to remove

This properly handles both initial setup and subsequent updates.


180-183: Good defensive guard against missing gateway device.

The early return prevents potential AttributeError if the gateway device is unexpectedly absent from the registry.

@bouwew bouwew force-pushed the coord-restore-state branch from fb05702 to cf5c3a1 Compare December 4, 2025 14:54
@bouwew bouwew force-pushed the coord-restore-state branch from ebc5d3b to f88e383 Compare December 4, 2025 15:01
@bouwew bouwew force-pushed the coord-restore-state branch from f88e383 to d87cdc2 Compare December 4, 2025 15:05
@bouwew bouwew force-pushed the coord-restore-state branch from c53f211 to 6367335 Compare December 4, 2025 15:22
@sonarqubecloud
Copy link

sonarqubecloud bot commented Dec 4, 2025

@bouwew bouwew merged commit 3817e52 into main Dec 5, 2025
13 checks passed
@bouwew bouwew deleted the coord-restore-state branch December 5, 2025 07:01
@coderabbitai coderabbitai bot mentioned this pull request Dec 26, 2025
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.

2 participants