Skip to content

Conversation

@dirixmjm
Copy link
Contributor

@dirixmjm dirixmjm commented Aug 4, 2025

Improve C+ registry collection and node discovery

  • remove unused C+ registry address propagation to the nodes
  • call discover_node for all cached nodes during startup
  • C+ scanning pace is determined by the size of cached (<4 nodes)
    Improve handling of SED and Scan configuration data
  • Remove new variables
  • Add dirty flag when configuration has changed but node has not been awake yet
  • Improve handling of default values when cache is empty

Summary by CodeRabbit

  • New Features

    • SED and SCAN devices use a durable "dirty" flag to track and apply pending configuration changes; registry now operates by MAC addresses only; cache pruning added.
  • Bug Fixes

    • Improved Circle+ registry collection and node discovery reliability.
  • Refactor

    • Simplified, unified node/registry discovery, scanning and configuration flows; standardized cache keys and behavior.
  • Tests

    • Tests updated to await scan completion and reflect revised defaults and cache expectations.
  • Documentation

    • Changelog entry added for v0.44.10.
  • Chores

    • Package version bumped to 0.44.10.

@codecov
Copy link

codecov bot commented Aug 4, 2025

Codecov Report

❌ Patch coverage is 86.41189% with 64 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.63%. Comparing base (a0f61c6) to head (86207db).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
plugwise_usb/network/registry.py 71.42% 22 Missing ⚠️
plugwise_usb/network/__init__.py 59.09% 9 Missing ⚠️
plugwise_usb/nodes/scan.py 89.77% 9 Missing ⚠️
plugwise_usb/nodes/sed.py 92.04% 7 Missing ⚠️
tests/test_usb.py 96.34% 6 Missing ⚠️
plugwise_usb/nodes/sense.py 0.00% 4 Missing ⚠️
plugwise_usb/nodes/node.py 50.00% 3 Missing ⚠️
plugwise_usb/nodes/switch.py 25.00% 3 Missing ⚠️
plugwise_usb/network/cache.py 88.88% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #302      +/-   ##
==========================================
- Coverage   80.92%   80.63%   -0.30%     
==========================================
  Files          36       36              
  Lines        7593     7540      -53     
==========================================
- Hits         6145     6080      -65     
- Misses       1448     1460      +12     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai
Copy link

coderabbitai bot commented Aug 4, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Registry and discovery were refactored to use MAC-only registrations; node constructors and network/address APIs had address parameters removed; SED/SCAN configs gained dirty flags with cache/key renames and immediate update flow; network cache pruning and callback-driven discovery were added; tests and package version/changelog updated.

Changes

Cohort / File(s) Change Summary
Changelog & Version
CHANGELOG.md, pyproject.toml
Added changelog entry for v0.44.10 and bumped package version to 0.44.10.
API Data Structures
plugwise_usb/api.py
MotionSensitivity converted to IntEnum with explicit integers; added dirty: bool to BatteryConfig and MotionConfig; removed zigbee_address from NodeInfo; sensitivity_level changed to `int
Network core
plugwise_usb/network/__init__.py
Registry moved from address-keyed dict to MAC-only list; removed address params from discovery/create methods; start() now accepts load: bool and sets register callbacks; simplified straggler handling.
Network registry & cache
plugwise_usb/network/registry.py, plugwise_usb/network/cache.py
Registry API migrated to list[str]; added scan pacing constants and unified Circle+ scan (update_missing_registrations_circleplus); added start_node_discover and scan_completed callbacks; added load_registrations_from_cache, remove_network_registration, update_network_nodetype; introduced prune_cache(registry: list[str]).
Node factory & base node
plugwise_usb/nodes/__init__.py, plugwise_usb/nodes/node.py
Removed address from factory and base node constructors; removed network_address property; added _get_cache_as_bool helper.
Node types: Circle / SED / Scan / Sense / Switch
plugwise_usb/nodes/circle.py, plugwise_usb/nodes/sed.py, plugwise_usb/nodes/scan.py, plugwise_usb/nodes/sense.py, plugwise_usb/nodes/switch.py
Removed address from constructors; consolidated SED/Scan config state into single objects with dirty flags; renamed cache keys (CACHE_SED_*, CACHE_SCAN_*); removed deferred send-queue/locks and some decorators; immediate config-update flow when dirty; minor load/default adjustments and additional scheduling flags for SED node-info updates.
Tests
tests/test_usb.py
Added _wait_for_scan helper and awaited scan completion in tests; updated expected node counts; switched to new cache keys/boolean values; assert on *.dirty flags instead of removed scheduling flags; removed address/zigbee assertions; added debug logging.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Test Suite
    participant Stick as StickNetwork
    participant Reg as StickNetworkRegister
    participant Cache as NetworkRegistrationCache
    participant Node as PlugwiseNode

    Test->>Stick: start(load=True)
    Stick->>Reg: register start-node-discover callback
    Stick->>Reg: register scan-completed callback
    Stick->>Cache: load_registrations_from_cache()
    Cache-->>Reg: return cached MAC list
    Reg->>Stick: _exec_node_discover_callback(mac, node_type, ping_first)
    Stick->>Node: _create_node_object(mac, node_type)
    Reg->>Reg: update_missing_registrations_circleplus() (scan addresses)
    Reg->>Reg: update_network_registration(mac) -> added?
    Reg->>Cache: prune_cache(registry_list)
    Reg->>Stick: invoke scan-completed callback
    Stick-->>Test: scan completed signal
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • bouwew
  • brefra
  • CoMPaTech

Poem

"I hopped through lists of MACs today,
Left old addresses along the way.
Dirty flags tucked in my little paw,
I nudged the cache and fixed a flaw.
A scan, a ping — the nodes all play. 🐇"


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e01db3 and 413c998.

📒 Files selected for processing (1)
  • CHANGELOG.md (1 hunks)
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mdi_CPRegistry

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 10

🔭 Outside diff range comments (2)
plugwise_usb/network/registry.py (2)

93-95: Fix type mismatch in registry property

The property signature returns a dictionary but _registry is now a list. This will cause a runtime error.

Either update the return type or modify the implementation:

     @property
-    def registry(self) -> dict[int, tuple[str, NodeType | None]]:
+    def registry(self) -> list[str]:
         """Return dictionary with all joined nodes."""
         return deepcopy(self._registry)

258-261: Fix incorrect registry iteration

The code tries to call values() on _registry which is now a list, not a dictionary.

         mac_registered = False
-        for registration in self._registry.values():
-            if mac == registration[0]:
-                mac_registered = True
-                break
+        mac_registered = mac in self._registry
♻️ Duplicate comments (6)
tests/test_usb.py (5)

695-696: Duplicate synchronization pattern

Same synchronization approach as mentioned earlier. Consider refactoring both occurrences.


771-772: Another instance of synchronization pattern

Third occurrence of the same synchronization approach.


910-911: Fourth instance of synchronization pattern

Same synchronization approach repeated.


2153-2153: Consistent boolean cache support

Same monkeypatch pattern as used earlier.


2442-2443: Synchronization pattern repeated

Same synchronization approach as noted earlier.

plugwise_usb/network/registry.py (1)

223-223: Same callback signature mismatch

This has the same issue as line 202 - passing 3 arguments to what should be a single-argument callback.

🧹 Nitpick comments (6)
plugwise_usb/nodes/circle.py (1)

913-916: Clarify complex conditional logic

The conditional logic could be misinterpreted due to operator precedence. Consider adding parentheses or restructuring for clarity.

-            if (
-                not self._available
-                and not await self.is_online()
-                or await self.node_info_update() is None
-            ):
+            if (not self._available and not await self.is_online()) or (await self.node_info_update() is None):

Alternatively, for better readability:

-            if (
-                not self._available
-                and not await self.is_online()
-                or await self.node_info_update() is None
-            ):
+            node_info = await self.node_info_update()
+            if (not self._available and not await self.is_online()) or node_info is None:
plugwise_usb/network/__init__.py (2)

281-283: Simplify complex conditional logic

The nested conditions make the code harder to read and maintain.

-        if (
-            self._register.update_node_registration(mac)
-            and self._nodes.get(mac) is None
-        ):
+        registration_updated = self._register.update_node_registration(mac)
+        node_not_exists = self._nodes.get(mac) is None
+        
+        if registration_updated and node_not_exists:

507-511: Simplify task creation condition

The condition for creating the discover stragglers task is duplicated and could be extracted.

-        if len(self._registry_stragglers) > 0 and (
-            self._discover_stragglers_task is None
-            or self._discover_stragglers_task.done()
-        ):
-            self._discover_stragglers_task = create_task(self._discover_stragglers())
+        if len(self._registry_stragglers) > 0:
+            if self._discover_stragglers_task is None or self._discover_stragglers_task.done():
+                self._discover_stragglers_task = create_task(self._discover_stragglers())

Consider extracting this pattern into a helper method since it's also used at line 485-488 for _load_stragglers_task.

tests/test_usb.py (2)

504-504: Fix typo in debug log message

-        _LOGGER.debug("Node %s has even %s", mac, str(pw_api.NodeEvent))
+        _LOGGER.debug("Node %s has event %s", mac, str(pw_api.NodeEvent))

567-568: Consider improving synchronization approach

The current approach accesses private attributes and uses a polling loop. Consider:

  1. Adding a public method or property to check scan completion status
  2. Using an event or future for more efficient synchronization
-        while not stick._network._register.scan_completed:
-            await asyncio.sleep(0.1)
+        # Consider adding a public method like:
+        # await stick.wait_for_discovery_completion()
plugwise_usb/nodes/sed.py (1)

193-196: Remove debug logging

The debug log appears to be leftover development code and should be removed.

     def _clock_sync_from_cache(self) -> bool | None:
         """Load clock sync state from cache."""
-        _LOGGER.debug(
-            "MDI: Bool: %s", str(self._get_cache_as_bool(CACHE_SED_CLOCK_SYNC))
-        )
         return self._get_cache_as_bool(CACHE_SED_CLOCK_SYNC)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0825a1 and e6a37a2.

📒 Files selected for processing (14)
  • CHANGELOG.md (1 hunks)
  • plugwise_usb/api.py (3 hunks)
  • plugwise_usb/network/__init__.py (11 hunks)
  • plugwise_usb/network/cache.py (1 hunks)
  • plugwise_usb/network/registry.py (8 hunks)
  • plugwise_usb/nodes/__init__.py (0 hunks)
  • plugwise_usb/nodes/circle.py (2 hunks)
  • plugwise_usb/nodes/node.py (1 hunks)
  • plugwise_usb/nodes/scan.py (11 hunks)
  • plugwise_usb/nodes/sed.py (16 hunks)
  • plugwise_usb/nodes/sense.py (2 hunks)
  • plugwise_usb/nodes/switch.py (2 hunks)
  • pyproject.toml (1 hunks)
  • tests/test_usb.py (28 hunks)
💤 Files with no reviewable changes (1)
  • plugwise_usb/nodes/init.py
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: in the plugwise_usb codebase, bouwew prefers not to add excessive validation decorators or protectiv...
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#252
File: plugwise_usb/__init__.py:0-0
Timestamp: 2025-06-03T09:58:44.611Z
Learning: In the plugwise_usb codebase, bouwew prefers not to add excessive validation decorators or protective error handling when they would amount to "too much protection". The existing validation level is considered sufficient.

Applied to files:

  • pyproject.toml
📚 Learning: in plugwise_usb/nodes/circle.py, the timestamp comparison `prev_address_timestamp - self._last_colle...
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#255
File: plugwise_usb/nodes/circle.py:477-482
Timestamp: 2025-06-19T06:38:04.702Z
Learning: In plugwise_usb/nodes/circle.py, the timestamp comparison `prev_address_timestamp - self._last_collected_energy_timestamp` in the `get_missing_energy_logs` method is intentional. The code iterates backwards through time-ordered energy log addresses, where `prev_address_timestamp` contains the more recent timestamp from the previous iteration, and this subtraction correctly calculates the time gap to determine when to stop collecting outdated data.

Applied to files:

  • plugwise_usb/nodes/circle.py
  • plugwise_usb/nodes/sense.py
  • plugwise_usb/nodes/node.py
  • plugwise_usb/network/__init__.py
  • plugwise_usb/nodes/scan.py
  • tests/test_usb.py
📚 Learning: in the plugwise_usb/nodes/circle.py file, the `log_addresses_missing` property being `none` means th...
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#255
File: plugwise_usb/nodes/circle.py:615-627
Timestamp: 2025-06-20T07:58:41.534Z
Learning: In the plugwise_usb/nodes/circle.py file, the `log_addresses_missing` property being `None` means the parameter has not been initialized yet, not that all logs are present. When `log_addresses_missing` is `None`, returning `False` from cache loading methods is correct because the system is not properly initialized and further execution would be useless.

Applied to files:

  • plugwise_usb/nodes/circle.py
  • plugwise_usb/nodes/switch.py
  • plugwise_usb/nodes/sense.py
  • plugwise_usb/nodes/node.py
  • plugwise_usb/nodes/scan.py
  • tests/test_usb.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). (1)
  • GitHub Check: Check commit
🔇 Additional comments (39)
pyproject.toml (1)

7-7: LGTM! Appropriate alpha version bump.

The version increment to "0.44.10a0" correctly indicates this is an alpha release with significant changes, aligning with the extensive refactoring described in the PR objectives.

CHANGELOG.md (1)

3-7: LGTM! Clear documentation of ongoing changes.

The "Ongoing" section effectively documents the current improvements, with entries that accurately reflect the PR objectives for registry collection, node discovery, and configuration handling enhancements.

plugwise_usb/network/cache.py (1)

82-90: LGTM! Well-implemented cache pruning logic.

The method correctly filters the cache to maintain only entries for MAC addresses present in the registry, properly handles empty MAC addresses, and persists the changes. This aligns well with the unified registry management approach.

plugwise_usb/api.py (2)

121-122: LGTM! Improved configuration state tracking.

The addition of the dirty flag to BatteryConfig provides a clean way to track when configuration changes are pending device synchronization, replacing the previous _new_ variable approach mentioned in the PR objectives.

Also applies to: 130-130


237-238: LGTM! Consistent configuration management.

The dirty flag in MotionConfig follows the same improved pattern as BatteryConfig, providing consistent configuration state tracking across different node types.

Also applies to: 244-244

plugwise_usb/nodes/sense.py (2)

49-49: LGTM! Consistent with MAC-only identification.

The removal of the address parameter aligns with the broader refactoring to use MAC addresses exclusively for node identification, simplifying the constructor interface.


97-97: LGTM! Improved node info update scheduling.

Setting _sed_node_info_update_task_scheduled = True after assigning default values ensures that these changes are properly synchronized with the device, improving the reliability of the node initialization process.

Also applies to: 100-100, 103-103

plugwise_usb/nodes/switch.py (1)

85-91: _sed_node_info_update_task_scheduled Already Defined in Parent Class

The _sed_node_info_update_task_scheduled flag is initialized in NodeSED.__init__ (plugwise_usb/nodes/sed.py:93), so its usage in plugwise_usb/nodes/switch.py is valid. No changes are required.

tests/test_usb.py (11)

699-699: Test expectation updated correctly

The discovered nodes count of 7 aligns with the refactored discovery logic.


1922-1934: Cache key updates align with refactoring

The new cache key naming with CACHE_SED_ prefix and boolean return values improve type safety and organization.


1937-1937: Correct monkeypatch for boolean cache values

Adding _get_cache_as_bool monkeypatch is necessary for the new boolean cache values.


1979-2003: Well-implemented dirty flag mechanism

The battery configuration dirty flag correctly tracks pending changes and is cleared after successful application.


2028-2033: Validation range and dirty flag correctly implemented

The maintenance interval validation and dirty flag management are properly implemented.


2046-2068: Clock interval implementation consistent with refactoring

The clock interval cache keys and dirty flag management follow the established pattern.


2116-2150: Scan node cache implementation follows best practices

The CACHE_SCAN_ prefixed keys and motion config dirty flag are consistently implemented.


2185-2223: Motion config dirty flag properly implemented

The dirty flag mechanism for motion configuration is consistent with the battery configuration pattern.


2343-2343: Boolean cache support


2545-2545: Node loading assertion is appropriate

The assertion correctly verifies the node is loaded after discovery.


2328-2340: Switch node correctly uses CACHE_SED_ keys
The PlugwiseSwitch class inherits from NodeSED (plugwise_usb/nodes/switch.py:33), and there are no CACHE_SWITCH_ constants defined in the codebase. Therefore, using the CACHE_SED_–prefixed keys in tests is intentional and correct.

plugwise_usb/nodes/scan.py (9)

35-42: Cache key naming convention is consistent

The renaming of cache keys with the CACHE_SCAN_CONFIG_ prefix improves code organization and aligns with the similar naming pattern used in sed.py.


46-56: Good use of explicit typing for constants

The addition of Final type hints to the default constants improves type safety and makes the code more maintainable.


86-86: Constructor correctly updated to remove address parameter

The removal of the address parameter from the parent class initialization is consistent with the broader refactoring to use MAC addresses exclusively.


136-146: Clear tracking of node info updates needed

The use of _sed_node_info_update_task_scheduled flag and debug logging for cache misses improves observability and maintenance tracking.


179-218: Cache getter methods correctly updated

The cache getter methods properly use the new cache key names and handle missing values appropriately. The default True for the dirty flag ensures configuration updates when cache state is unknown.


301-347: Configuration setters correctly implement dirty flag pattern

The use of dataclasses.replace() with dirty=True and immediate cache updates provides a clean and consistent configuration management pattern.


426-431: Well-structured awake task execution

The method properly extends the parent's awake task execution and cleanly integrates configuration updates based on the dirty flag.


441-478: Configuration method correctly manages dirty flag lifecycle

The scan_configure method properly uses the stored configuration, handles responses appropriately, and clears the dirty flag only on successful configuration.


480-498: Cache update method provides comprehensive state persistence

The method correctly persists all configuration values including the dirty flag, ensuring consistent state across restarts.

plugwise_usb/network/registry.py (5)

26-28: Good use of constants for scan timing

The explicit constants for scan timing improve code readability and make it easier to tune the scanning behavior.


46-54: Registry data structure simplified appropriately

The change from dictionary to list simplifies the registry since addresses are no longer needed. The new callback names are more descriptive.


102-108: Clean callback registration interface

The simplified callback methods with clear single purposes improve the API design.


150-181: Registration methods correctly implement MAC-based tracking

The simplified registration methods properly manage the MAC list and include appropriate cache pruning on removal.


216-220: Smart optimization of scan timing based on cache size

Adjusting the scan delay when the cache has fewer than 4 nodes helps with faster initial discovery while being conservative with larger networks.

plugwise_usb/nodes/sed.py (6)

26-33: Consistent cache key naming with CACHE_SED_ prefix

The cache keys follow a clear naming convention that aligns with the pattern used in scan.py.


87-93: Constructor correctly updated for MAC-only identification

The removal of the address parameter and addition of the update task flag aligns with the overall refactoring pattern.


241-327: Configuration setters correctly implement dirty flag pattern

All setters consistently use dataclasses.replace() to update configuration with the dirty flag. Unlike scan.py, these don't trigger immediate cache updates, which is appropriate for battery-powered devices that update during awake periods.


547-557: Awake tasks properly handle both update types

The method correctly checks for both node info updates and configuration updates, executing them in the appropriate order.


558-595: Configuration method properly manages battery settings

The method correctly uses the battery configuration dataclass and appropriately clears the dirty flag only on successful configuration.


597-622: Comprehensive cache persistence for battery configuration

The method correctly saves all configuration values including the dirty flag, ensuring complete state recovery after restarts.

Copy link

@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

♻️ Duplicate comments (2)
tests/test_usb.py (2)

577-579: Remove redundant MAC address assignment.

The hardcoded assignment on line 578 overwrites the value obtained from the future, making the test less meaningful. This issue was previously identified.

         mac_awake_node = await self.test_node_awake
-        mac_awake_node = "5555555555555555"
         assert mac_awake_node == "5555555555555555"

1963-1967: Address commented test code.

This commented-out NodeInfo test code was previously identified and should either be implemented or removed to keep the codebase clean.

🧹 Nitpick comments (3)
tests/test_usb.py (1)

504-504: Fix typo and improve logging format.

There's a typo in the debug message: "has even" should be "has event". Also, the logging format could be improved to show the actual event value rather than the enum class.

-        _LOGGER.debug("Node %s has even %s", mac, str(pw_api.NodeEvent))
+        _LOGGER.debug("Node %s has event %s", mac, event)
plugwise_usb/nodes/sed.py (2)

145-147: Fix variable naming convention.

Variable LoadSuccess should follow Python naming conventions.

-        LoadSuccess = True
+        load_success = True
         if not await super()._load_from_cache():
-            LoadSuccess = False
+            load_success = False

And update the return statement:

-        return LoadSuccess
+        return load_success

330-332: Fix type annotation for dirty property.

The type annotation indicates int but the property returns a boolean value from self._battery_config.dirty.

     @property
-    def dirty(self) -> int:
+    def dirty(self) -> bool:
         """Battery configuration dirty flag."""
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1498e45 and 10f3dc9.

📒 Files selected for processing (2)
  • plugwise_usb/nodes/sed.py (16 hunks)
  • tests/test_usb.py (29 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: in plugwise_usb/nodes/circle.py, the timestamp comparison `prev_address_timestamp - self._last_colle...
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#255
File: plugwise_usb/nodes/circle.py:477-482
Timestamp: 2025-06-19T06:38:04.702Z
Learning: In plugwise_usb/nodes/circle.py, the timestamp comparison `prev_address_timestamp - self._last_collected_energy_timestamp` in the `get_missing_energy_logs` method is intentional. The code iterates backwards through time-ordered energy log addresses, where `prev_address_timestamp` contains the more recent timestamp from the previous iteration, and this subtraction correctly calculates the time gap to determine when to stop collecting outdated data.

Applied to files:

  • tests/test_usb.py
📚 Learning: in the plugwise_usb/nodes/circle.py file, the `log_addresses_missing` property being `none` means th...
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#255
File: plugwise_usb/nodes/circle.py:615-627
Timestamp: 2025-06-20T07:58:41.534Z
Learning: In the plugwise_usb/nodes/circle.py file, the `log_addresses_missing` property being `None` means the parameter has not been initialized yet, not that all logs are present. When `log_addresses_missing` is `None`, returning `False` from cache loading methods is correct because the system is not properly initialized and further execution would be useless.

Applied to files:

  • tests/test_usb.py
🧬 Code Graph Analysis (1)
tests/test_usb.py (6)
plugwise_usb/nodes/node.py (13)
  • mac (235-237)
  • PlugwiseBaseNode (56-843)
  • battery_config (121-127)
  • set_awake_duration (736-742)
  • set_maintenance_interval (763-769)
  • set_clock_interval (745-751)
  • clock_sync (131-137)
  • set_clock_sync (754-760)
  • motion_config (249-255)
  • set_motion_reset_timer (781-787)
  • set_motion_daylight_mode (772-778)
  • set_motion_sensitivity_level (790-796)
  • is_loaded (208-210)
plugwise_usb/api.py (13)
  • mac (316-317)
  • NodeEvent (30-36)
  • battery_config (484-495)
  • set_awake_duration (526-549)
  • set_maintenance_interval (594-617)
  • set_clock_interval (551-572)
  • set_clock_sync (574-592)
  • motion_config (498-508)
  • set_motion_reset_timer (637-660)
  • set_motion_daylight_mode (619-635)
  • set_motion_sensitivity_level (662-679)
  • MotionSensitivity (22-27)
  • is_loaded (284-285)
plugwise_usb/network/registry.py (1)
  • scan_completed (98-100)
plugwise_usb/__init__.py (2)
  • joined_nodes (112-120)
  • nodes (95-99)
plugwise_usb/nodes/sed.py (11)
  • battery_config (342-351)
  • dirty (330-332)
  • awake_duration (335-339)
  • set_awake_duration (225-243)
  • _awake_response (402-458)
  • set_maintenance_interval (282-301)
  • maintenance_interval (368-375)
  • clock_interval (354-358)
  • set_clock_interval (245-264)
  • clock_sync (361-365)
  • set_clock_sync (266-280)
plugwise_usb/nodes/scan.py (7)
  • dirty (231-233)
  • reset_timer (272-276)
  • motion_config (262-269)
  • set_motion_reset_timer (309-329)
  • daylight_mode (236-240)
  • set_motion_daylight_mode (288-307)
  • set_motion_sensitivity_level (331-347)
⏰ 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: Run pytest using Python 3.13
🔇 Additional comments (17)
tests/test_usb.py (9)

567-568: LGTM! Proper synchronization with async discovery.

The addition of scan completion waiting is necessary to ensure tests properly synchronize with the new asynchronous node discovery flow. This prevents race conditions in test assertions.


695-699: LGTM! Proper scan synchronization and node count validation.

The scan completion wait ensures proper synchronization, and the node count assertions validate the expected discovery results. The differentiation between joined nodes (9) and discovered nodes (7) correctly reflects the network topology.


771-772: LGTM! Consistent scan synchronization.

Proper application of the scan completion wait pattern, ensuring consistency across test methods.


910-912: LGTM! Consistent scan synchronization in energy tests.

Proper synchronization pattern maintained across all test methods.


922-937: LGTM! Cache key updates align with architectural changes.

The cache key renaming to use CACHE_SED_ prefixes and the addition of _get_cache_as_bool patching properly reflect the configuration management improvements and cache handling refactoring mentioned in the PR objectives.


1979-2110: LGTM! Proper testing of dirty flag configuration system.

The battery configuration tests correctly verify the new dirty flag mechanism that tracks when configuration changes are pending application. The test flow properly validates:

  • Initial clean state
  • Dirty flag set on configuration changes
  • Dirty flag persistence through failed configurations
  • Dirty flag clearing after successful application

This aligns well with the improved SED configuration management described in the PR objectives.


2126-2272: LGTM! Proper testing of Scan motion configuration dirty flags.

The motion configuration tests correctly implement the same dirty flag testing pattern for Scan nodes, with appropriate cache key updates using CACHE_SCAN_ prefixes. The test flow properly validates the configuration state management for motion-related settings.


2420-2472: LGTM! Proper test setup for refactored node discovery.

The new fake_cache function correctly uses updated cache key naming conventions, and the test properly synchronizes with the async discovery process. The node count assertion and loaded state verification ensure proper test coverage of the discovery and loading functionality.


2572-2572: LGTM! Node loaded state verification.

Good addition to verify that the node loading process completed successfully.

plugwise_usb/nodes/sed.py (8)

5-5: LGTM - Import additions support new functionality.

The additional asyncio imports and BatteryConfig import align well with the dirty flag refactoring and enhanced async task management.

Also applies to: 12-12


26-33: LGTM - Improved cache key organization.

The CACHE_SED_ prefix provides better namespacing and the additional constants support the new dirty flag functionality and enhanced cache management.


87-87: LGTM - Constructor aligns with address parameter removal.

The removal of the address parameter from the super constructor call is consistent with the PR's goal of eliminating unused address propagation. The new task scheduling flag supports the enhanced awake task management.

Also applies to: 93-93


148-174: LGTM - Solid dirty flag handling logic.

The dirty flag logic correctly handles missing cache values by setting defaults and marking them as dirty. The bitwise OR operation properly combines with the cached dirty state, ensuring configuration updates are applied when needed.


179-223: LGTM - Consistent cache loading helpers.

The cache loading methods are well-structured with proper null handling and consistent return types. The new _sed_config_dirty_from_cache method correctly defaults to True when no cached dirty state exists, ensuring configuration is applied when uncertain.


238-325: LGTM - Clean configuration setter pattern.

The setter methods follow a consistent, immutable pattern using dataclass replace() with proper validation, change detection, and dirty flag management. This approach ensures configuration changes are properly tracked for later application.


388-400: LGTM - Improved task execution logic.

The updated _configure_sed_task properly checks the dirty flag before proceeding, and the new _run_awake_tasks method provides a clean consolidation of awake task handling. The logic flow is correct and improves maintainability.

Also applies to: 544-554


555-620: LGTM - Streamlined SED configuration management.

The configuration methods now directly use _battery_config fields, properly manage the dirty flag lifecycle, and use the correct cache constants. The separation of concerns between configuration application and cache updates improves code clarity.

Copy link

@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)
plugwise_usb/network/registry.py (1)

102-107: Update callback signature to match actual usage.

The callback signature suggests it takes (str, NodeType | None, bool) parameters, but looking at the usage in the codebase, it appears the callback is invoked differently. Please verify the signature matches the actual method being passed as callback.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 10f3dc9 and 16e7871.

📒 Files selected for processing (6)
  • plugwise_usb/network/__init__.py (11 hunks)
  • plugwise_usb/network/cache.py (1 hunks)
  • plugwise_usb/network/registry.py (8 hunks)
  • plugwise_usb/nodes/scan.py (11 hunks)
  • plugwise_usb/nodes/sed.py (16 hunks)
  • tests/test_usb.py (28 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • plugwise_usb/network/cache.py
  • plugwise_usb/nodes/scan.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: in plugwise_usb/nodes/circle.py, the timestamp comparison `prev_address_timestamp - self._last_colle...
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#255
File: plugwise_usb/nodes/circle.py:477-482
Timestamp: 2025-06-19T06:38:04.702Z
Learning: In plugwise_usb/nodes/circle.py, the timestamp comparison `prev_address_timestamp - self._last_collected_energy_timestamp` in the `get_missing_energy_logs` method is intentional. The code iterates backwards through time-ordered energy log addresses, where `prev_address_timestamp` contains the more recent timestamp from the previous iteration, and this subtraction correctly calculates the time gap to determine when to stop collecting outdated data.

Applied to files:

  • plugwise_usb/network/registry.py
  • tests/test_usb.py
  • plugwise_usb/network/__init__.py
📚 Learning: in the plugwise_usb/nodes/circle.py file, the `log_addresses_missing` property being `none` means th...
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#255
File: plugwise_usb/nodes/circle.py:615-627
Timestamp: 2025-06-20T07:58:41.534Z
Learning: In the plugwise_usb/nodes/circle.py file, the `log_addresses_missing` property being `None` means the parameter has not been initialized yet, not that all logs are present. When `log_addresses_missing` is `None`, returning `False` from cache loading methods is correct because the system is not properly initialized and further execution would be useless.

Applied to files:

  • tests/test_usb.py
  • plugwise_usb/network/__init__.py
🧬 Code Graph Analysis (1)
plugwise_usb/network/registry.py (3)
plugwise_usb/api.py (2)
  • NodeType (61-72)
  • mac (316-317)
plugwise_usb/exceptions.py (1)
  • NodeError (24-25)
plugwise_usb/network/cache.py (3)
  • update_nodetypes (62-75)
  • prune_cache (81-90)
  • nodetypes (23-25)
⏰ 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: Run pytest using Python 3.13
🔇 Additional comments (54)
tests/test_usb.py (12)

504-504: LGTM: Debug logging enhancement

Good addition for debugging node awake events. The log format correctly captures both MAC and event type.


567-568: LGTM: Proper test synchronization

Good addition to wait for scan completion before assertions. This properly synchronizes the test with the refactored asynchronous node discovery process.


694-698: LGTM: Updated discovery assertions

The scan completion wait and updated node count assertions properly reflect the refactored discovery process. The change from joined_nodes count to discovered nodes length is consistent with the codebase changes.


770-771: LGTM: Consistent test synchronization

Consistent application of scan completion synchronization across test methods.


913-936: LGTM: Updated cache handling for SED nodes

The cache method updates properly reflect the new cache key naming scheme with CACHE_SED_ prefixes and the introduction of boolean cache values. The addition of _get_cache_as_bool monkeypatch aligns with the cache restructuring.


972-982: LGTM: Updated configuration state testing

The shift from checking scheduling flags to testing battery_config.dirty properly reflects the new configuration state management with dirty flags introduced in the refactoring.


1021-1021: LGTM: Updated validation boundary

The maintenance interval validation limit change to 1500 reflects updated business logic constraints.


2109-2146: LGTM: Updated cache handling for Scan nodes

The cache method updates for Scan nodes properly implement the new CACHE_SCAN_ prefix naming scheme and boolean cache values, maintaining consistency with the SED node cache updates.


2178-2188: LGTM: Consistent motion configuration testing

The motion configuration dirty flag testing maintains consistency with the new configuration state management approach across different node types.


2311-2336: LGTM: Consistent Switch node cache updates

Switch node cache updates follow the established pattern, properly using the appropriate cache key prefixes and boolean value support.


2414-2439: LGTM: Comprehensive cache setup for discovery testing

The comprehensive fake_cache method ensures all necessary cache values are available for proper testing of the node discovery process with the new cache structure.


2565-2565: LGTM: Node loading validation

The assertion properly validates that the expected node is loaded during the discovery process, reflecting the updated discovery behavior.

plugwise_usb/network/__init__.py (12)

80-80: LGTM! Simplified stragglers tracking.

The change from a dictionary to a list for tracking registry stragglers is consistent with the MAC-only refactoring, removing the complexity of address-based tracking.


235-241: LGTM! Consistent MAC-only registration check.

The updated call to node_is_registered with MAC only is consistent with the refactored registry interface.


248-249: LGTM! Updated battery-powered node discovery.

The method call now correctly uses MAC-only parameters, aligning with the refactored discovery interface.


280-288: LGTM! Simplified node rejoin handling.

The rejoin logic now uses MAC-only operations, consistent with the registry refactoring. The logic correctly checks for successful registration update before triggering discovery.


334-335: LGTM! Simplified coordinator discovery.

The discovery call now uses MAC-only parameters, removing unnecessary address handling.


368-368: LGTM! Updated network node type registration.

The method call correctly uses MAC-only parameters for updating node types in the registry.


388-388: LGTM! Battery-powered node discovery simplified.

The discovery call now uses MAC-only parameters, consistent with the refactored interface.


413-413: LGTM! Simplified node object creation and stragglers handling.

The node creation and stragglers management now use MAC-only operations. The logic correctly appends the MAC to the stragglers list when node details cannot be retrieved.

Also applies to: 424-425


437-443: LGTM! Fixed list modification during iteration.

The stragglers discovery logic now correctly:

  • Creates a copy for safe iteration
  • Uses MAC-only discovery calls
  • Removes MACs from stragglers when discovery succeeds
  • Uses appropriate list operations

This addresses the critical bug mentioned in previous reviews.


485-489: LGTM! Proper task management for load stragglers.

The task creation logic correctly checks if a task is already running before creating a new one, preventing duplicate task execution.


498-512: LGTM! Enhanced start method with callback setup.

The start method improvements:

  • Accepts a load parameter for configuration
  • Properly sets up registry callbacks for node discovery and scan completion
  • Correctly manages stragglers discovery task creation
  • Uses appropriate task state checking

The callback setup aligns with the refactored registry interface.


517-517: LGTM! Proper parameter propagation.

The discover_nodes method correctly passes the load parameter to the start method, maintaining consistent behavior.

plugwise_usb/network/registry.py (13)

9-9: LGTM! Clean constant definitions.

The addition of Final import and scanning interval constants improves code maintainability and type safety.

Also applies to: 26-28


46-46: LGTM! Simplified registry structure.

The change from dictionary to list simplifies the registry by focusing solely on MAC addresses, consistent with the broader refactoring.


108-110: LGTM! Clean callback registration.

The scan completed callback registration is well-implemented with clear naming and documentation.


122-125: LGTM! Streamlined start method.

The updated start method properly loads registrations from cache and creates the unified scanning task. The task creation approach aligns with the comment from bouwew about using create_task for background processing.


152-158: LGTM! Simplified registration check.

The updated node_is_registered method correctly uses list membership checking, consistent with the new registry structure.


165-170: LGTM! Simplified node type update.

The method correctly updates node types in cache using MAC addresses only, consistent with the refactoring.


171-177: LGTM! Clean list-based registration.

The registration update method properly:

  • Checks for empty or duplicate MACs
  • Uses list append operation
  • Returns boolean indicating if registration was new

The logic is clear and correct for the list-based approach.


178-184: LGTM! Proper removal with cache pruning.

The removal method correctly:

  • Removes MAC from the registry list
  • Prunes cache to maintain consistency
  • Uses proper list operations

The cache pruning ensures no stale entries remain.


185-212: LGTM! Unified scanning approach.

The refactored scanning method effectively:

  • Scans all addresses 0-63 in a unified approach
  • Maintains local registry for cache pruning
  • Properly invokes discovery callbacks
  • Sets scan completion flag
  • Uses proper sleep intervals

This consolidation simplifies the scanning logic compared to separate quick/full scan methods.


213-230: LGTM! Adaptive cache loading.

The cache loading method effectively:

  • Adapts scanning pace based on cached node count (< 4 nodes uses quick scan)
  • Properly loads from cache with node type information
  • Invokes discovery callbacks with correct parameters
  • Handles scan completion callback

The adaptive approach optimizes performance based on cache content.


231-234: LGTM! Clean method delegation.

The simplified update_node_registration method properly delegates to the main registration update method.


251-253: LGTM! Updated registration flow.

The registration method correctly:

  • Uses the updated registration method
  • Invokes discovery callback on successful registration

The flow is consistent with the refactored approach.


278-280: LGTM! Updated unregistration flow.

The unregistration method correctly uses the updated registry methods for checking registration status and removing entries.

plugwise_usb/nodes/sed.py (17)

5-5: LGTM! Clean import optimization.

The import updates remove unused dependencies and focus on the required classes, improving code clarity.

Also applies to: 12-12


26-34: LGTM! Standardized cache key naming.

The new CACHE_SED_ prefixed constants provide clear, consistent naming for cache keys, improving maintainability and reducing naming conflicts.


87-87: LGTM! Constructor aligned with refactoring.

The super() call correctly omits the address parameter, consistent with the broader address removal refactoring.


93-93: LGTM! Simplified task scheduling.

The simplified task scheduling flag aligns with the new configuration management approach.


145-178: LGTM! Excellent cache loading refactoring.

The refactored _load_from_cache method provides significant improvements:

  • Returns boolean success flag for better error handling
  • Sets reasonable defaults when cache entries are missing
  • Properly marks configuration as dirty when defaults are used
  • Consolidates all battery configuration into a single object
  • Triggers immediate configuration update when dirty

This approach is much cleaner than the previous implementation.


179-223: LGTM! Consistent cache loading methods.

The individual cache loading methods correctly:

  • Use the new CACHE_SED_ prefixed keys
  • Return None when cache entries are missing
  • Handle type conversion properly
  • Maintain clean separation of concerns

The consistent approach makes the code more maintainable.


238-325: LGTM! Simplified setter methods with dirty flag.

The refactored setter methods provide several improvements:

  • Use replace() for immutable updates of battery configuration
  • Mark configuration as dirty when changes are made
  • Remove complex scheduling logic in favor of simple dirty tracking
  • Maintain consistent error handling and validation

This approach is much cleaner and more predictable than deferred scheduling.


330-333: LGTM! Clean dirty flag exposure.

The dirty property provides clean access to the configuration dirty state, enabling external monitoring of pending changes.


350-350: LGTM! Complete battery config state.

The battery_config property correctly includes the dirty flag, providing complete configuration state information.


390-400: LGTM! Simplified configuration task.

The _configure_sed_task method is now much cleaner:

  • Simple dirty flag check
  • Direct call to configuration method
  • Clear success/failure handling

This is more straightforward than the previous implementation.


410-410: LGTM! Updated cache key usage.

The awake timestamp caching correctly uses the new CACHE_SED_AWAKE_TIMESTAMP key.


423-424: LGTM! Simplified awake task execution.

The awake task execution is now cleaner and more direct.


436-456: LGTM! Consistent awake reason caching.

The awake reason handling correctly uses the new CACHE_SED_AWAKE_REASON key across all awake types, maintaining consistency.


486-486: LGTM! Updated maintenance interval caching.

The maintenance interval detection correctly uses the new CACHE_SED_MAINTENANCE_INTERVAL key across all code paths.

Also applies to: 493-493, 501-502


544-554: LGTM! Streamlined awake tasks execution.

The _run_awake_tasks method is now much cleaner:

  • Handles node info updates when scheduled
  • Executes configuration updates when dirty
  • Simple, clear logic flow

This is a significant improvement over complex task queuing.


555-592: LGTM! Improved SED configuration method.

The sed_configure method improvements:

  • Uses consolidated battery configuration properties
  • Clears dirty flag on successful configuration
  • Triggers configuration update to save cache
  • Maintains proper error handling and logging

The approach is cleaner and more reliable than the previous implementation.


594-620: LGTM! Comprehensive configuration update.

The _sed_configure_update method effectively:

  • Uses all new CACHE_SED_ prefixed keys
  • Saves complete battery configuration state including dirty flag
  • Publishes feature updates to subscribers
  • Uses proper async gathering for concurrent operations

The method ensures cache and subscribers stay synchronized with configuration changes.

Copy link

@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 8ee62f2 and 50bb816.

📒 Files selected for processing (1)
  • plugwise_usb/nodes/scan.py (10 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: in the plugwise_usb/nodes/circle.py file, the `log_addresses_missing` property being `none` means th...
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#255
File: plugwise_usb/nodes/circle.py:615-627
Timestamp: 2025-06-20T07:58:41.534Z
Learning: In the plugwise_usb/nodes/circle.py file, the `log_addresses_missing` property being `None` means the parameter has not been initialized yet, not that all logs are present. When `log_addresses_missing` is `None`, returning `False` from cache loading methods is correct because the system is not properly initialized and further execution would be useless.

Applied to files:

  • plugwise_usb/nodes/scan.py
📚 Learning: in plugwise_usb/nodes/circle.py, the timestamp comparison `prev_address_timestamp - self._last_colle...
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#255
File: plugwise_usb/nodes/circle.py:477-482
Timestamp: 2025-06-19T06:38:04.702Z
Learning: In plugwise_usb/nodes/circle.py, the timestamp comparison `prev_address_timestamp - self._last_collected_energy_timestamp` in the `get_missing_energy_logs` method is intentional. The code iterates backwards through time-ordered energy log addresses, where `prev_address_timestamp` contains the more recent timestamp from the previous iteration, and this subtraction correctly calculates the time gap to determine when to stop collecting outdated data.

Applied to files:

  • plugwise_usb/nodes/scan.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). (1)
  • GitHub Check: Run pytest using Python 3.13
🔇 Additional comments (10)
plugwise_usb/nodes/scan.py (10)

35-42: Cache key naming convention looks good.

The consistent use of CACHE_SCAN_ prefixes aligns well with the broader refactoring pattern mentioned in the summary. This standardization improves maintainability and reduces naming conflicts.


88-88: Constructor simplification is correct.

Removing the address parameter from the parent constructor call aligns with the broader shift away from address-based node identification mentioned in the AI summary.


152-179: Dirty flag initialization and cache loading logic is well-implemented.

The approach of initializing dirty = False and setting it to True when cache values are missing ensures proper configuration management when the cache is empty. The conditional call to _scan_configure_update() when dirty is set provides immediate configuration updates.


181-220: Cache loading methods have consistent null handling.

All cache loading methods properly handle None values and return appropriate defaults or None where expected. The _motion_config_dirty_from_cache() method correctly defaults to True when no cached value exists, which is the safe approach for ensuring configuration updates.


232-236: Clean dirty flag property implementation.

The property provides direct access to the motion config's dirty state, which simplifies external access to the configuration status.


303-309: Configuration update pattern is consistent and correct.

The use of replace() with dirty=True followed by _scan_configure_update() provides a clean pattern for tracking and applying configuration changes. This pattern is consistently applied across all configuration methods.


428-434: Awake task execution logic is sound.

The _run_awake_tasks() method properly calls the parent implementation and then handles motion configuration updates when the dirty flag is set. This ensures configuration changes are applied when the node becomes available.


436-443: Configuration task logic has early return optimization.

The early return when dirty is False avoids unnecessary configuration requests, which is an efficient approach.


445-477: Scan configuration method is robust.

The method properly handles different response types and includes appropriate logging for success, failure, and unexpected response cases. The dirty flag is correctly reset to False only upon successful configuration.


479-497: Cache update method is comprehensive.

All configuration values are properly cached with appropriate string conversions, and the method includes both feature update notifications and cache persistence.

Copy link

@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 50bb816 and 8f41b59.

📒 Files selected for processing (2)
  • plugwise_usb/nodes/scan.py (9 hunks)
  • tests/test_usb.py (30 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • plugwise_usb/nodes/scan.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: in plugwise_usb/nodes/circle.py, the timestamp comparison `prev_address_timestamp - self._last_colle...
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#255
File: plugwise_usb/nodes/circle.py:477-482
Timestamp: 2025-06-19T06:38:04.702Z
Learning: In plugwise_usb/nodes/circle.py, the timestamp comparison `prev_address_timestamp - self._last_collected_energy_timestamp` in the `get_missing_energy_logs` method is intentional. The code iterates backwards through time-ordered energy log addresses, where `prev_address_timestamp` contains the more recent timestamp from the previous iteration, and this subtraction correctly calculates the time gap to determine when to stop collecting outdated data.

Applied to files:

  • tests/test_usb.py
📚 Learning: in the plugwise_usb/nodes/circle.py file, the `log_addresses_missing` property being `none` means th...
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#255
File: plugwise_usb/nodes/circle.py:615-627
Timestamp: 2025-06-20T07:58:41.534Z
Learning: In the plugwise_usb/nodes/circle.py file, the `log_addresses_missing` property being `None` means the parameter has not been initialized yet, not that all logs are present. When `log_addresses_missing` is `None`, returning `False` from cache loading methods is correct because the system is not properly initialized and further execution would be useless.

Applied to files:

  • tests/test_usb.py
🪛 GitHub Actions: Latest commit
tests/test_usb.py

[error] 2236-2236: SyntaxError: Expected a statement at line 2236.


[error] 2236-2236: SyntaxError: Expected a statement at line 2236 (column 10).

🔇 Additional comments (1)
tests/test_usb.py (1)

504-504: Good addition for test observability

The debug log statement enhances test diagnostics by logging node MAC and event details during awake callbacks.

Copy link

@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

♻️ Duplicate comments (1)
plugwise_usb/nodes/scan.py (1)

136-146: Reset the update flag at the start of _load_defaults to avoid stale scheduling.

The flag _sed_node_info_update_task_scheduled should be reset at the start of the method to prevent carrying over from previous loads.

 async def _load_defaults(self) -> None:
     """Load default configuration settings."""
+    self._sed_node_info_update_task_scheduled = False
     await super()._load_defaults()
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f41b59 and 45918cc.

📒 Files selected for processing (1)
  • plugwise_usb/nodes/scan.py (10 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: in the plugwise_usb/nodes/circle.py file, the `log_addresses_missing` property being `none` means th...
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#255
File: plugwise_usb/nodes/circle.py:615-627
Timestamp: 2025-06-20T07:58:41.534Z
Learning: In the plugwise_usb/nodes/circle.py file, the `log_addresses_missing` property being `None` means the parameter has not been initialized yet, not that all logs are present. When `log_addresses_missing` is `None`, returning `False` from cache loading methods is correct because the system is not properly initialized and further execution would be useless.

Applied to files:

  • plugwise_usb/nodes/scan.py
📚 Learning: in plugwise_usb/nodes/circle.py, the timestamp comparison `prev_address_timestamp - self._last_colle...
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#255
File: plugwise_usb/nodes/circle.py:477-482
Timestamp: 2025-06-19T06:38:04.702Z
Learning: In plugwise_usb/nodes/circle.py, the timestamp comparison `prev_address_timestamp - self._last_collected_energy_timestamp` in the `get_missing_energy_logs` method is intentional. The code iterates backwards through time-ordered energy log addresses, where `prev_address_timestamp` contains the more recent timestamp from the previous iteration, and this subtraction correctly calculates the time gap to determine when to stop collecting outdated data.

Applied to files:

  • plugwise_usb/nodes/scan.py
🔇 Additional comments (10)
plugwise_usb/nodes/scan.py (10)

86-86: LGTM - Constructor updated correctly for address parameter removal.

The removal of the address parameter from the superclass call aligns with the broader refactor to eliminate address-based node handling.


28-34: LGTM - Improved cache key naming with consistent prefixes.

The renaming to CACHE_SCAN_* prefixes improves namespacing and consistency across the codebase.


148-177: LGTM - Proper dirty flag implementation with default handling.

The dirty flag logic correctly:

  • Tracks when defaults are used due to missing cache values
  • Combines dirty flags using bitwise OR
  • Triggers configuration updates when dirty
  • Properly tracks super method success

179-204: LGTM - Proper None return pattern for missing cache values.

Returning None when cache values are missing allows the caller to properly handle defaults and dirty flag logic.


230-233: LGTM - Clean dirty flag property implementation.

Simple and consistent property that provides access to the motion configuration dirty flag.


301-347: LGTM - Consistent configuration setter pattern with immediate updates.

All three setter methods properly:

  • Check for actual value changes before proceeding
  • Use immutable dataclass updates with replace()
  • Set the dirty flag when changes occur
  • Trigger immediate configuration updates

369-424: LGTM - Motion state update properly uses dirty flags and new cache keys.

The method correctly:

  • Uses the new CACHE_SCAN_* cache key constants
  • Sets dirty flags when configuration is modified
  • Calls configuration update after changes
  • Maintains proper motion detection logic

426-432: LGTM - Proper awake tasks implementation with dirty flag handling.

The method follows the correct pattern by calling the super method first and handling dirty configuration updates when the node is awake.


434-472: LGTM - Robust scan configuration with proper dirty flag management.

The configuration methods correctly:

  • Guard against unnecessary work by checking dirty flags
  • Handle different response types with appropriate logging
  • Clear dirty flag only on successful configuration
  • Persist changes through configuration update calls

474-492: LGTM - Clean configuration update method with proper cache management.

The method properly updates all configuration cache values using the new cache key constants, publishes updates to subscribers, and saves the cache efficiently using gather().

coderabbitai[bot]
coderabbitai bot previously requested changes Aug 11, 2025
Copy link

@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

♻️ Duplicate comments (1)
plugwise_usb/network/registry.py (1)

146-149: create_task is appropriate here to avoid blocking startup.

Spawning the full-scan as a background task aligns with the requirement to not block initialization when cache-driven discovery runs first. _registration_task keeps a handle for cancellation.

🧹 Nitpick comments (4)
plugwise_usb/network/registry.py (4)

46-46: Optional: use a set for _registry to ensure uniqueness under concurrency.

Concurrent adds (manual register + scan) can race and generate duplicates in a list. A set avoids this and keeps checks O(1) without changing the external API.

-        self._registry: list[str] = []
+        # Track unique MACs internally to prevent duplicates under concurrent updates
+        self._registry: set[str] = set()
-    def registry(self) -> list[str]:
-        """Return list with mac's of all joined nodes."""
-        return deepcopy(self._registry)
+    def registry(self) -> list[str]:
+        """Return list with MACs of all joined nodes."""
+        # Preserve external list semantics; detach from internal set
+        return list(self._registry)
-        if mac == "" or mac in self._registry:
+        if mac == "" or mac in self._registry:
             return False
-        self._registry.append(mac)
+        self._registry.add(mac)
         return True
-        if mac in self._registry:
-            self._registry.remove(mac)
+        if mac in self._registry:
+            self._registry.discard(mac)
             if self._network_cache is not None:
-                await self._network_cache.prune_cache(self._registry)
+                await self._network_cache.prune_cache(list(self._registry))

Note: membership checks elsewhere continue to work unchanged.

Also applies to: 93-95, 189-195, 196-202


176-182: Fix node_is_registered docstring to reflect return type and behavior.

The function returns a boolean, not an address.

-    def node_is_registered(self, mac: str) -> bool:
-        """Return the network registration address for given mac."""
+    def node_is_registered(self, mac: str) -> bool:
+        """Return True if the given MAC is registered in the network registry."""

142-144: Nit: improve start() docstring wording.

Current: “Initialize load the network registry.” Suggest clearer phrasing.

-        """Initialize load the network registry."""
+        """Initialize and load the network registry."""

26-28: Optional: clarify units in constant names.

Consider suffixing with _DELAY_S to reflect seconds and improve readability.

-CIRCLEPLUS_SCANREQUEST_MAINTENANCE: Final = 10
-CIRCLEPLUS_SCANREQUEST_QUICK: Final = 0.1
+CIRCLEPLUS_SCANREQUEST_MAINTENANCE_DELAY_S: Final = 10
+CIRCLEPLUS_SCANREQUEST_QUICK_DELAY_S: Final = 0.1

Follow-up: update usages at lines 51, 231, 234 accordingly.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between efafd7d and 445bb9a.

📒 Files selected for processing (1)
  • plugwise_usb/network/registry.py (7 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-08-09T16:24:49.853Z
Learnt from: dirixmjm
PR: plugwise/python-plugwise-usb#302
File: plugwise_usb/network/registry.py:49-52
Timestamp: 2025-08-09T16:24:49.853Z
Learning: In the plugwise_usb codebase, the `_start_node_discover` callback in `plugwise_usb/network/registry.py` must return `Awaitable[bool]`. The return value is used in `plugwise_usb/network/__init__.py` to track discovery success. In `_discover_battery_powered_node`, it checks `if not await self._discover_node(...)` to return False on failure. In the stragglers retry loop, it checks the return value to remove successfully discovered nodes from the retry list. This supports different handling approaches for regular nodes vs SED devices.

Applied to files:

  • plugwise_usb/network/registry.py
📚 Learning: 2025-08-09T16:24:49.853Z
Learnt from: dirixmjm
PR: plugwise/python-plugwise-usb#302
File: plugwise_usb/network/registry.py:49-52
Timestamp: 2025-08-09T16:24:49.853Z
Learning: In the plugwise_usb codebase, the `_start_node_discover` callback in `plugwise_usb/network/registry.py` must return `Awaitable[bool]`. The return value is used in `plugwise_usb/network/__init__.py` in the `node_awake_message` and `node_rejoin_message` handlers to determine whether to fall back to battery-powered node discovery. When `_discover_node` returns `False`, it triggers `_discover_battery_powered_node` for SED devices.

Applied to files:

  • plugwise_usb/network/registry.py
📚 Learning: 2025-08-09T16:24:49.853Z
Learnt from: dirixmjm
PR: plugwise/python-plugwise-usb#302
File: plugwise_usb/network/registry.py:49-52
Timestamp: 2025-08-09T16:24:49.853Z
Learning: In the plugwise_usb codebase, the `_start_node_discover` callback in `plugwise_usb/network/registry.py` must return `Awaitable[bool]`. The return value is used in `plugwise_usb/network/__init__.py` to track discovery success. The `_discover_battery_powered_node` method checks this return value to determine if discovery succeeded. There are different approaches for regular nodes (discovered through registry scans) and SED devices (discovered when they wake up through awake/rejoin handlers).

Applied to files:

  • plugwise_usb/network/registry.py
📚 Learning: 2025-08-07T17:50:29.163Z
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#302
File: plugwise_usb/nodes/scan.py:132-142
Timestamp: 2025-08-07T17:50:29.163Z
Learning: In the plugwise_usb codebase, the `_load_defaults` method in node classes (scan.py, switch.py, sense.py) is only called once during node initialization, not during runtime. Therefore, flags like `_sed_node_info_update_task_scheduled` don't need to be reset at the start of `_load_defaults` as there's no risk of stale state from previous loads.

Applied to files:

  • plugwise_usb/network/registry.py
📚 Learning: 2025-06-19T06:38:04.702Z
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#255
File: plugwise_usb/nodes/circle.py:477-482
Timestamp: 2025-06-19T06:38:04.702Z
Learning: In plugwise_usb/nodes/circle.py, the timestamp comparison `prev_address_timestamp - self._last_collected_energy_timestamp` in the `get_missing_energy_logs` method is intentional. The code iterates backwards through time-ordered energy log addresses, where `prev_address_timestamp` contains the more recent timestamp from the previous iteration, and this subtraction correctly calculates the time gap to determine when to stop collecting outdated data.

Applied to files:

  • plugwise_usb/network/registry.py
📚 Learning: 2025-06-20T07:58:41.534Z
Learnt from: bouwew
PR: plugwise/python-plugwise-usb#255
File: plugwise_usb/nodes/circle.py:615-627
Timestamp: 2025-06-20T07:58:41.534Z
Learning: In the plugwise_usb/nodes/circle.py file, the `log_addresses_missing` property being `None` means the parameter has not been initialized yet, not that all logs are present. When `log_addresses_missing` is `None`, returning `False` from cache loading methods is correct because the system is not properly initialized and further execution would be useless.

Applied to files:

  • plugwise_usb/network/registry.py
🧬 Code Graph Analysis (1)
plugwise_usb/network/registry.py (9)
plugwise_usb/api.py (2)
  • NodeType (61-72)
  • mac (316-317)
plugwise_usb/network/__init__.py (3)
  • registry (149-151)
  • start (498-511)
  • unregister_node (167-175)
plugwise_usb/nodes/node.py (1)
  • mac (235-237)
plugwise_usb/messages/__init__.py (1)
  • mac (53-57)
plugwise_usb/messages/responses.py (1)
  • node_type (633-635)
plugwise_usb/network/cache.py (3)
  • update_nodetypes (62-75)
  • prune_cache (81-90)
  • nodetypes (23-25)
plugwise_usb/__init__.py (1)
  • unregister_node (333-340)
plugwise_usb/helpers/util.py (1)
  • validate_mac (14-22)
plugwise_usb/exceptions.py (1)
  • NodeError (24-25)
🔇 Additional comments (3)
plugwise_usb/network/registry.py (3)

123-139: Good: guarded discovery-callback wrapper keeps background tasks resilient.

The _exec_node_discover_callback addition correctly guards against exceptions and re-raises CancelledError. This prevents a bad listener from crashing the scan.


223-226: Confirm semantics: scan_completed_callback is invoked after cache phase and after full scan.

This callback fires twice (once at the end of load_registrations_from_cache and once after full scan). Verify downstream listeners expect multiple invocations; otherwise consider distinct callbacks for “cache phase completed” vs “full scan finished”.

Also applies to: 242-244


228-245: LGTM: cache preload and adaptive scan pace.

  • Fast-scan when <4 cached nodes and steady pacing otherwise matches the PR intent.
  • Using the guarded _exec_node_discover_callback is correct.

@sonarqubecloud
Copy link

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.

3 participants