Skip to content

Conversation

frenck
Copy link
Owner

@frenck frenck commented May 27, 2025

Description

Performance improvements, but introducing caching around getting all entity IDs; which is fetched by all repairs constantly.

Motivation and Context

fixes #637
closed #618

How has this been tested?

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Other

Checklist

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.

Copy link

coderabbitai bot commented May 27, 2025

📝 Walkthrough

Walkthrough

The changes introduce a caching mechanism for retrieving all entity IDs, with event-driven invalidation to keep the cache fresh. Sensor entities now debounce state updates, reducing redundant updates from rapid events. Integration setup is updated to initialize and clean up the cache invalidation logic during its lifecycle.

Changes

File(s) Change Summary
custom_components/spook/util.py Added cached retrieval of all entity IDs, event-driven cache invalidation, and new setup/teardown functions for cache lifecycle.
custom_components/spook/__init__.py Integrated cache invalidation setup/teardown into integration entry lifecycle.
custom_components/spook/ectoplasms/homeassistant/sensor.py Added debouncing for state updates, cleanup on entity removal, and managed scheduled update callbacks in sensor entity class.

Sequence Diagram(s)

sequenceDiagram
    participant HomeAssistant
    participant SpookIntegration
    participant EntityRegistry
    participant StateMachine
    participant SpookSensor

    HomeAssistant->>SpookIntegration: Setup Entry
    SpookIntegration->>SpookIntegration: async_setup_all_entity_ids_cache_invalidation()
    SpookIntegration->>EntityRegistry: Listen for updates
    SpookIntegration->>StateMachine: Listen for events
    Note over SpookIntegration: Cache invalidation listeners registered

    SpookSensor->>SpookSensor: Event triggers update
    SpookSensor->>SpookSensor: Cancel pending update if any
    SpookSensor->>SpookSensor: Schedule debounced update (5s delay)
    Note over SpookSensor: Only one update runs after debounce period

    HomeAssistant->>SpookIntegration: Unload Entry
    SpookIntegration->>SpookIntegration: Cleanup cache invalidation listeners
Loading

Assessment against linked issues

Objective Addressed Explanation
Reduce long state update times for sensor entities (#637)
Reduce setup timing and improve performance of Spook integration (#618)

Poem

In the warren of code, a cache now appears,
To speed up the hunt and quiet old fears.
Sensors now wait, they don’t hop in a rush,
Debouncing their updates—no need to blush!
With entry and exit, the cache is in tow,
This Spook’s running faster—just thought you should know!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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.
    • @coderabbitai modularize this function.
  • 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.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

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

@frenck frenck added the bugfix Inconsistencies or issues which will cause a problem for users or implementors. label May 27, 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: 0

🔭 Outside diff range comments (1)
custom_components/spook/util.py (1)

409-409: ⚠️ Potential issue

Fix incorrect function call.

This line calls async_get_all_label_ids but should call async_get_all_floor_ids based on the function name and context.

Apply this fix:

 def async_filter_known_floor_ids(
     hass: HomeAssistant,
     *,
     floor_ids: set[str],
     known_floor_ids: set[str] | None = None,
 ) -> set[str]:
     """Filter out known floor IDs."""
     if known_floor_ids is None:
-        known_floor_ids = async_get_all_label_ids(hass)
+        known_floor_ids = async_get_all_floor_ids(hass)
     return {
         floor_id
         for floor_id in floor_ids - known_floor_ids
         if floor_id and isinstance(floor_id, str)
     }
🧹 Nitpick comments (2)
custom_components/spook/ectoplasms/homeassistant/sensor.py (1)

584-603: Well-implemented debouncing mechanism!

The debouncing correctly prevents rapid updates by cancelling pending calls before scheduling new ones. This will effectively reduce the load from frequent entity registry or state change events.

Consider making the 5-second delay configurable to allow fine-tuning based on system requirements:

+# Debounce delay in seconds
+DEBOUNCE_DELAY = 5
+
 class HomeAssistantSpookSensorEntity(HomeAssistantSpookEntity, SensorEntity):
     """Spook sensor providig Home Asistant information."""

     entity_description: HomeAssistantSpookSensorEntityDescription
     _unsub_debouncer: Callable[[], None] | None = None

Then use it in line 602:

-            self._unsub_debouncer = async_call_later(self.hass, 5, _debounced_update)
+            self._unsub_debouncer = async_call_later(self.hass, DEBOUNCE_DELAY, _debounced_update)
custom_components/spook/util.py (1)

128-188: Well-designed caching mechanism with proper invalidation.

The implementation correctly:

  • Prevents duplicate listener setup
  • Clears cache on relevant events (entity registry updates, HA start, component loads)
  • Provides proper cleanup functionality
  • Includes helpful debug logging

While the global variable approach works, consider encapsulating the cache in a class for better maintainability:

class EntityIDCache:
    """Manages cached entity IDs with invalidation."""
    
    def __init__(self, hass: HomeAssistant):
        self.hass = hass
        self._cache: set[str] | None = None
        self._unsub_listeners: list[Callable[[], None]] = []
    
    def setup_invalidation(self) -> Callable[[], None]:
        """Set up cache invalidation listeners."""
        # Implementation here
        
    def get_all_entity_ids(self, include_all_none: bool = False) -> set[str]:
        """Get cached entity IDs."""
        # Implementation here

This would make testing easier and reduce reliance on global state.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 55bd1c2 and 782de7b.

📒 Files selected for processing (3)
  • custom_components/spook/__init__.py (2 hunks)
  • custom_components/spook/ectoplasms/homeassistant/sensor.py (3 hunks)
  • custom_components/spook/util.py (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Verify
🔇 Additional comments (3)
custom_components/spook/__init__.py (1)

103-105: LGTM! Cache invalidation is properly integrated.

The cache invalidation setup is correctly placed after platform setup and properly registered for cleanup on unload.

custom_components/spook/ectoplasms/homeassistant/sensor.py (1)

611-617: Proper cleanup implementation.

The cleanup method correctly cancels any pending debounced updates and calls the parent's cleanup method.

custom_components/spook/util.py (1)

190-224: Efficient caching implementation for entity ID retrieval.

The cache correctly:

  • Combines entity IDs from both registry and current states
  • Filters out ignored domains
  • Returns copies to prevent cache corruption
  • Includes optional ENTITY_MATCH_ALL/NONE constants

@frenck frenck merged commit 05b833f into main May 27, 2025
17 of 18 checks passed
@frenck frenck deleted the frenck-2025-0296 branch May 27, 2025 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugfix Inconsistencies or issues which will cause a problem for users or implementors.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Updating state took 1.101 seconds longer setup timings related to Spook integration
1 participant