Skip to content

Conversation

@dirixmjm
Copy link
Contributor

@dirixmjm dirixmjm commented Aug 18, 2025

Summary by CodeRabbit

  • Refactor

    • Streamlined Circle+ startup so cached and fresh loads follow a single validation flow, removing redundant post-cache steps and simplifying control flow.
    • Consolidated online/device-info checks into one condition to better detect offline or unresponsive devices; failed checks now return early.
  • Chores

    • Standardized logging to reference Circle+ and include the device identifier.
    • Reduced noisy cache messages and removed redundant cache-failure/success noise.

@coderabbitai
Copy link

coderabbitai bot commented Aug 18, 2025

Walkthrough

On Circle+ node load, cache-hit path now sets _loaded and skips prior post-cache initialization and loaded-event emission; logs standardized to use internal _mac_in_str; online and node_info checks unified into one condition; _loaded is set before calling initialize(), with a single combined failure warning and simplified control flow.

Changes

Cohort / File(s) Summary of Changes
Circle+ load flow refactor
plugwise_usb/nodes/circle_plus.py
On cache hit set _loaded and bypass post-cache init and loaded-event emission; use internal _mac_in_str for log messages; remove explicit cache-failure log; unify online and node_info_update() checks into one condition and emit a single not-online/not-responding warning; set _loaded = True before calling initialize() and simplify success/failure returns.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant CirclePlus as CirclePlus.load()
  participant Cache
  participant Device

  Caller->>CirclePlus: load()
  CirclePlus->>Cache: try load from cache
  alt Cache hit
    Note right of CirclePlus#e6f5ff: set _loaded, skip post-cache init\n(do not emit loaded event)
  else Cache miss
    CirclePlus->>Device: retrieve node info / node_info_update()
  end
  CirclePlus->>Device: check online AND node_info_update()
  alt Not online or no response
    Note right of CirclePlus#ffe6e6: log single warning (not online/not responding)
    CirclePlus-->>Caller: return False
  else OK
    CirclePlus->>CirclePlus: set _loaded = True
    CirclePlus->>Device: initialize()
    alt initialize fails
      CirclePlus-->>Caller: return False
    else initialize succeeds
      CirclePlus-->>Caller: return True
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I twitched my nose at cached light,
One path now hums, the logs sit tight.
MAC whispers swapped, the checks align,
A single warning—one neat sign.
I hop away, the flow feels right. 🐇

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mdi_cp

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.
    • 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.
  • 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 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/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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: 1

🔭 Outside diff range comments (1)
plugwise_usb/nodes/circle_plus.py (1)

59-66: Don’t set _loaded = True before initialization; also roll back on init failure.

Currently _loaded is set to True before _setup_protocol + initialize(). If initialize() fails, the method returns False but _loaded remains True, leaving the object in an inconsistent “loaded” state and potentially bypassing @raise_not_loaded guards elsewhere. Also, setting _loaded early may allow concurrent callers to use the node before it’s ready.

Apply this diff to defer and guard the loaded flag:

-        self._loaded = True
-
         self._setup_protocol(CIRCLE_PLUS_FIRMWARE_SUPPORT, FEATURES_CIRCLE_PLUS)
-        if not await self.initialize():
-            return False
+        if not await self.initialize():
+            # Roll back a previously set _loaded (e.g., by cache) to avoid inconsistent state
+            self._loaded = False
+            return False
+
+        # Mark as loaded only after successful init (noop if already set by cache)
+        self._loaded = True
🧹 Nitpick comments (2)
plugwise_usb/nodes/circle_plus.py (2)

44-46: Nit: Logging style and identifier — use sentence case and self.mac.

Minor consistency/readability tweak: align message casing with the rest of the codebase and use the mac property instead of _mac_in_str.

-        if not self._loaded:
-            _LOGGER.debug("Retrieving Info For Circle+ node %s", self._mac_in_str)
+        if not self._loaded:
+            _LOGGER.debug("Retrieving info for Circle+ node %s", self.mac)

48-56: Clarify boolean precedence and avoid implicit double-initialize on cache hits.

  • Readability: The combined and/or with awaits relies on Python precedence; it’s easy to misread.
  • Behavior: When _loaded is True due to a cache hit and _available is False, is_online() will trigger initialize() internally (see node.py:576-584), and then initialize() is called again below — potentially performing a second init.

Two options:

Option A — keep logic, but make precedence explicit and use self.mac in logs. Low risk, improves readability:

-        # Check if node is online
-        if (
-            not self._available
-            and not await self.is_online()
-            or await self.node_info_update() is None
-        ):
-            _LOGGER.warning(
-                "Failed to load Circle+ node %s because it is not online or not responding",
-                self._mac_in_str,
-            )
+        # Check if node is online
+        if ((not self._available and not await self.is_online())
+            or (await self.node_info_update() is None)):
+            _LOGGER.warning(
+                "Failed to load Circle+ node %s because it is not online or not responding",
+                self.mac,
+            )
             return False

Option B — avoid double init by skipping is_online() when _loaded is already True (cache hit). This keeps the current semantics while preventing initialize() being invoked twice:

-        # Check if node is online
-        if (
-            not self._available
-            and not await self.is_online()
-            or await self.node_info_update() is None
-        ):
-            _LOGGER.warning(
-                "Failed to load Circle+ node %s because it is not online or not responding",
-                self._mac_in_str,
-            )
-            return False
+        # Ensure we can talk to the node and we have its info
+        if self._loaded:
+            # Avoid is_online() here to prevent a double initialize when loaded-from-cache
+            if await self.node_info_update() is None:
+                _LOGGER.warning(
+                    "Failed to load Circle+ node %s because it is not responding",
+                    self.mac,
+                )
+                return False
+        else:
+            if (not await self.is_online()) or (await self.node_info_update() is None):
+                _LOGGER.warning(
+                    "Failed to load Circle+ node %s because it is not online or not responding",
+                    self.mac,
+                )
+                return False

Note: Node.is_online at plugwise_usb/nodes/node.py:576-584 calls initialize() when _loaded is True and _initialized is False. If you prefer Option A, please confirm initialize() is idempotent.

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6be2724 and c9ea5b5.

📒 Files selected for processing (1)
  • plugwise_usb/nodes/circle_plus.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
plugwise_usb/nodes/circle_plus.py (2)
plugwise_usb/nodes/node.py (4)
  • mac (235-237)
  • _load_from_cache (396-410)
  • is_online (577-585)
  • node_info_update (452-465)
plugwise_usb/nodes/circle.py (2)
  • _load_from_cache (959-1002)
  • node_info_update (1052-1085)

@codecov
Copy link

codecov bot commented Aug 18, 2025

Codecov Report

❌ Patch coverage is 66.66667% with 2 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@6be2724). Learn more about missing BASE report.
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
plugwise_usb/nodes/circle_plus.py 66.66% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #322   +/-   ##
=======================================
  Coverage        ?   80.63%           
=======================================
  Files           ?       36           
  Lines           ?     7559           
  Branches        ?        0           
=======================================
  Hits            ?     6095           
  Misses          ?     1464           
  Partials        ?        0           

☔ 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.

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 (1)
plugwise_usb/nodes/circle_plus.py (1)

41-41: Prefer using the public mac property in logs for consistency and safety.

While _mac_in_str is safe here, the codebase predominantly logs self.mac. Using the property also preserves encapsulation.

-            _LOGGER.debug("Loading Circle+ node %s from cache", self._mac_in_str)
+            _LOGGER.debug("Loading Circle+ node %s from cache", self.mac)
🧹 Nitpick comments (3)
plugwise_usb/nodes/circle_plus.py (3)

44-46: Nit: Unify log style and use self.mac.

Lowercase the message for consistency and use the public mac property.

-            _LOGGER.debug("Retrieving Info For Circle+ node %s", self._mac_in_str)
+            _LOGGER.debug("Retrieving info for Circle+ node %s", self.mac)

48-57: Avoid ambiguous boolean precedence and unnecessary network calls on cache-hit.

The mixed and/or condition is easy to misread and currently forces node_info_update() even when _node_info is already loaded from cache (left side short-circuits to False if available). Refactor to compute the two checks explicitly and only call node_info_update() if _node_info is missing.

-        if (
-            not self._available
-            and not await self.is_online()
-            or await self.node_info_update() is None
-        ):
-            _LOGGER.warning(
-                "Failed to load Circle+ node %s because it is not online or not responding",
-                self._mac_in_str,
-            )
-            return False
+        # Check if node is online and that we have (or can retrieve) node info
+        online_ok = self._available or await self.is_online()
+        node_info_ok = self._node_info is not None or (await self.node_info_update()) is not None
+        if not (online_ok and node_info_ok):
+            _LOGGER.warning(
+                "Failed to load Circle+ node %s because it is not online or not responding",
+                self.mac,
+            )
+            return False

59-66: Prevent double-initialization when is_online() already initialized the node.

is_online() may call initialize() when _loaded is True and _initialized is False. The subsequent unconditional initialize here can run twice. Guard it on _initialized.

-        self._setup_protocol(CIRCLE_PLUS_FIRMWARE_SUPPORT, FEATURES_CIRCLE_PLUS)
-        if not await self.initialize():
-            return False
+        if not self._initialized:
+            self._setup_protocol(CIRCLE_PLUS_FIRMWARE_SUPPORT, FEATURES_CIRCLE_PLUS)
+            if not await self.initialize():
+                return False
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c9ea5b5 and b8440f3.

📒 Files selected for processing (1)
  • plugwise_usb/nodes/circle_plus.py (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 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/nodes/circle_plus.py
🧬 Code Graph Analysis (1)
plugwise_usb/nodes/circle_plus.py (2)
plugwise_usb/nodes/circle.py (2)
  • _load_from_cache (959-1002)
  • node_info_update (1052-1085)
plugwise_usb/nodes/node.py (3)
  • _load_from_cache (396-410)
  • is_online (577-585)
  • node_info_update (452-465)
⏰ 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 (1)
plugwise_usb/nodes/circle_plus.py (1)

65-66: LOADED event is still emitted (contrary to the summary).

_loaded_callback(NodeEvent.LOADED, ...) remains in place; if the intention was to remove it on cache-hit, confirm desired behavior.

@dirixmjm dirixmjm marked this pull request as ready for review August 18, 2025 15:38
@dirixmjm dirixmjm requested a review from a team as a code owner August 18, 2025 15:38
@sonarqubecloud
Copy link

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/nodes/circle_plus.py (1)

41-43: LGTM: cache-load log now uses a safe identifier and matches Circle's style

Using _mac_in_str avoids touching _node_info before it exists and aligns with logging in circle.py.

🧹 Nitpick comments (1)
plugwise_usb/nodes/circle_plus.py (1)

48-52: Clarify operator precedence in the combined online/info check

The current condition is correct, but and vs or precedence is non-trivial at a glance. Add parentheses to make the intent explicit and future-proof:

-            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)
+            ):
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b8440f3 and 2bd581a.

📒 Files selected for processing (1)
  • plugwise_usb/nodes/circle_plus.py (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 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/nodes/circle_plus.py
🧬 Code Graph Analysis (1)
plugwise_usb/nodes/circle_plus.py (5)
plugwise_usb/nodes/scan.py (1)
  • _load_from_cache (144-173)
plugwise_usb/nodes/sed.py (1)
  • _load_from_cache (146-180)
plugwise_usb/nodes/circle.py (2)
  • _load_from_cache (959-1002)
  • node_info_update (1052-1085)
plugwise_usb/nodes/node.py (3)
  • _load_from_cache (396-410)
  • is_online (577-585)
  • node_info_update (452-465)
plugwise_usb/api.py (1)
  • is_online (329-330)

Copy link
Contributor

@bouwew bouwew left a comment

Choose a reason for hiding this comment

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

Looking good to me!

@dirixmjm dirixmjm removed the request for review from a team August 24, 2025 09:27
@dirixmjm dirixmjm merged commit 6372799 into main Aug 24, 2025
17 checks passed
@dirixmjm dirixmjm deleted the mdi_cp branch August 24, 2025 09:27
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