Skip to content

Add create and delete services for input number helpers#1222

Open
potelux wants to merge 2 commits intofrenck:mainfrom
potelux:add-input-number-create-delete-services
Open

Add create and delete services for input number helpers#1222
potelux wants to merge 2 commits intofrenck:mainfrom
potelux:add-input-number-create-delete-services

Conversation

@potelux
Copy link
Copy Markdown

@potelux potelux commented Mar 25, 2026

Description

Adds create and delete services to the input_number integration, enabling helpers to be created and deleted programmatically at runtime without requiring a UI interaction or a configuration reload.

Motivation and Context

The primary use case (in my situation) is allowing the creation of robust blueprints that can also create their own necessary helpers. This makes it even easier for users to share their cool setups.

Specifically, I use this in my TagReader automation to create the NFC tag helper and in my Inovelli Global Configuration automation to have a single dashboard that can modify settings for all Inovelli devices.

How has this been tested?

I had originally created this for HomeAssistant core and had written working tests there. Now, I tested it manually checking for errors such as creating two helpers with the same entitiy id, out of range min/max, etc.

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.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds two new admin services for Home Assistant input_number: create (creates UI-managed input numbers, optional explicit object_id) and delete (removes editable UI-managed input numbers). Implements service handlers, validation schemas, and registers both services in services.yaml.

Changes

Cohort / File(s) Summary
Input Number Create Service
custom_components/spook/ectoplasms/input_number/services/create.py
New SpookService implements input_number.create. Declares CREATE_FIELDS and CONF_OBJECT_ID, composes schema with _cv_input_number, resolves the NumberStorageCollection via hass.data["websocket_api"]["input_number/list"], checks/handles optional object_id (collision check, create-lock, temporary _get_suggested_id override), and calls async_create_item.
Input Number Delete Service
custom_components/spook/ectoplasms/input_number/services/delete.py
New SpookService implements input_number.delete. Validates target entity is editable, resolves the NumberStorageCollection via websocket API, and calls async_delete_item to remove the item by entity.unique_id.
Service Definitions
custom_components/spook/services.yaml
Adds input_number_create and input_number_delete service entries. create exposes fields (name, optional object_id, min, max, initial, step, mode, icon, unit_of_measurement); delete is targeted at entity.domain: input_number and limited to UI-managed input numbers.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant CreateService
    participant StorageCollection
    participant InputNumberEntity
    rect rgba(200,200,255,0.5)
    Client->>CreateService: Call create service (payload)
    CreateService->>CreateService: Validate schema (CREATE_FIELDS + _cv_input_number)
    CreateService->>StorageCollection: Resolve via hass.data.websocket_api input_number/list
    alt object_id provided
        CreateService->>StorageCollection: Check id_manager.has_id(object_id)
        alt exists
            CreateService-->>Client: Raise HomeAssistantError (ID collision)
        else
            CreateService->>CreateService: Acquire create lock
            CreateService->>StorageCollection: Override _get_suggested_id -> object_id
            CreateService->>StorageCollection: async_create_item(payload)
            CreateService->>StorageCollection: Restore _get_suggested_id
            CreateService->>CreateService: Release lock
            StorageCollection-->>InputNumberEntity: Item created
            InputNumberEntity-->>Client: Success
        end
    else object_id not provided
        CreateService->>StorageCollection: async_create_item(payload)
        StorageCollection-->>InputNumberEntity: Item created
        InputNumberEntity-->>Client: Success
    end
    end
Loading
sequenceDiagram
    participant Client
    participant DeleteService
    participant InputNumberEntity
    participant StorageCollection
    rect rgba(200,255,200,0.5)
    Client->>DeleteService: Call delete service (target entity)
    DeleteService->>InputNumberEntity: Check entity.editable
    alt Not editable
        DeleteService-->>Client: Raise HomeAssistantError
    else Editable
        DeleteService->>StorageCollection: Resolve via hass.data.websocket_api input_number/list
        DeleteService->>StorageCollection: async_delete_item(entity.unique_id)
        StorageCollection-->>Client: Item deleted
    end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I nudged a lock, I named a light,
Numbers sprout softly in the night.
IDs snugged in with careful cheer,
Delete hops in when edits appear,
A rabbit’s code — precise and bright.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and clearly describes the main changes: adding create and delete services for input number helpers, which matches the primary objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description check ✅ Passed The pull request description clearly relates to the changeset, explaining the feature to add create and delete services for input_number helpers with relevant use cases and testing approach.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@custom_components/spook/ectoplasms/input_number/services/create.py`:
- Around line 64-69: The code mutates the shared private attribute
collection._get_suggested_id around an await (collection.async_create_item)
which can race between concurrent service calls; wrap the override and the await
in an async lock stored on hass to serialize access: acquire an asyncio.Lock
from self.hass.data.setdefault("spook_input_number_lock", asyncio.Lock()) and
perform original_get_suggested_id = collection._get_suggested_id,
collection._get_suggested_id = lambda info: object_id, await
collection.async_create_item(data) inside an async with block, and restore
collection._get_suggested_id in a finally to ensure proper cleanup.

In `@custom_components/spook/services.yaml`:
- Around line 1010-1013: The numeric service schema sets number.min to 0.001
which is stricter than backend validation and can cause UI rejections; update
the number schema (keys: number, min, step) to align with backend by raising the
allowed range to the backend minimum (e.g., set min to 0 or remove the min
constraint) so the UI accepts values the backend already permits while keeping
step: any.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8c7e0b62-f8e6-4218-a05d-57416bf66a45

📥 Commits

Reviewing files that changed from the base of the PR and between 2dd6dc2 and 7c8edc0.

📒 Files selected for processing (3)
  • custom_components/spook/ectoplasms/input_number/services/create.py
  • custom_components/spook/ectoplasms/input_number/services/delete.py
  • custom_components/spook/services.yaml

Comment thread custom_components/spook/ectoplasms/input_number/services/create.py Outdated
Comment thread custom_components/spook/services.yaml
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@custom_components/spook/ectoplasms/input_number/services/create.py`:
- Around line 61-72: The has_id check on collection.id_manager.has_id is outside
the create lock, causing a TOCTOU race; move that check inside the async with
self.hass.data.setdefault(f"{DOMAIN}_create_lock", asyncio.Lock()) block so you
acquire the lock before calling collection.id_manager.has_id, and only then
raise HomeAssistantError if the id exists; keep the existing pattern of setting
collection._get_suggested_id to return object_id, awaiting
collection.async_create_item(data), and deleting collection._get_suggested_id in
the finally block.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9427b9f2-c1cf-4ebb-b98e-756788c3a1ac

📥 Commits

Reviewing files that changed from the base of the PR and between 7c8edc0 and 2c50dc7.

📒 Files selected for processing (2)
  • custom_components/spook/ectoplasms/input_number/services/create.py
  • custom_components/spook/services.yaml

Comment thread custom_components/spook/ectoplasms/input_number/services/create.py Outdated
@potelux potelux force-pushed the add-input-number-create-delete-services branch 3 times, most recently from 0e4b352 to a3e785e Compare March 25, 2026 04:57
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
custom_components/spook/ectoplasms/input_number/services/create.py (1)

61-67: ⚠️ Potential issue | 🟠 Major

Move duplicate-ID validation inside the lock.

Line 62 still checks has_id before acquiring the lock on Lines 65–67, so concurrent calls can pass the check and race at create time.

🔧 Suggested patch
         if object_id:
-            if collection.id_manager.has_id(object_id):
-                message = f"An input number with object ID '{object_id}' already exists"
-                raise HomeAssistantError(message)
             async with self.hass.data.setdefault(
                 f"{DOMAIN}_create_lock", asyncio.Lock()
             ):
+                if collection.id_manager.has_id(object_id):
+                    message = f"An input number with object ID '{object_id}' already exists"
+                    raise HomeAssistantError(message)
                 collection._get_suggested_id = lambda info: object_id  # noqa: SLF001
                 try:
                     await collection.async_create_item(data)
                 finally:
                     del collection._get_suggested_id  # noqa: SLF001
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@custom_components/spook/ectoplasms/input_number/services/create.py` around
lines 61 - 67, The duplicate-ID check using collection.id_manager.has_id
currently runs before acquiring the creation lock (the async with
self.hass.data.setdefault(f"{DOMAIN}_create_lock", asyncio.Lock())), causing a
race; move the has_id validation inside the async with block that uses that lock
(i.e., acquire the lock first, then call collection.id_manager.has_id(object_id)
and raise HomeAssistantError if true) so concurrent create calls re-check under
the same lock and cannot both proceed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@custom_components/spook/ectoplasms/input_number/services/create.py`:
- Around line 61-67: The duplicate-ID check using collection.id_manager.has_id
currently runs before acquiring the creation lock (the async with
self.hass.data.setdefault(f"{DOMAIN}_create_lock", asyncio.Lock())), causing a
race; move the has_id validation inside the async with block that uses that lock
(i.e., acquire the lock first, then call collection.id_manager.has_id(object_id)
and raise HomeAssistantError if true) so concurrent create calls re-check under
the same lock and cannot both proceed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 43f3dfcf-d7b5-4ea8-abd6-3a26e22d9ebc

📥 Commits

Reviewing files that changed from the base of the PR and between a5aea19 and 38df41e.

📒 Files selected for processing (2)
  • custom_components/spook/ectoplasms/input_number/services/create.py
  • custom_components/spook/services.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • custom_components/spook/services.yaml

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@custom_components/spook/ectoplasms/input_number/services/delete.py`:
- Around line 37-40: The current access to the storage collection via
self.hass.data["websocket_api"]["input_number/list"][0].__self__.storage_collection
is fragile; update the logic in delete.py to first try the standard stored
collection under self.hass.data[DOMAIN] (the same pattern used in
zone/delete.py) and only if that key is missing fall back to the websocket_api
lookup, then call collection.async_delete_item(entity.unique_id); locate and
adjust the code around the variable collection and the async_delete_item call to
implement this conditional lookup (use the DOMAIN key constant as in other
services).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6a629740-4190-41cd-a1dc-7169a588689a

📥 Commits

Reviewing files that changed from the base of the PR and between 38df41e and a3e785e.

📒 Files selected for processing (3)
  • custom_components/spook/ectoplasms/input_number/services/create.py
  • custom_components/spook/ectoplasms/input_number/services/delete.py
  • custom_components/spook/services.yaml
✅ Files skipped from review due to trivial changes (1)
  • custom_components/spook/services.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • custom_components/spook/ectoplasms/input_number/services/create.py

Comment thread custom_components/spook/ectoplasms/input_number/services/delete.py Outdated
@potelux potelux force-pushed the add-input-number-create-delete-services branch 2 times, most recently from f523f56 to 5783ab9 Compare March 25, 2026 14:13
@potelux
Copy link
Copy Markdown
Author

potelux commented Mar 25, 2026

The code rabbit suggestion about async locks is not completely clear to me. I had Claude assist me in implementing the suggestion and was able to test and confirm its functionality. Reading over the code, it seems logical, but I am not knowledgeable enough on async to know if I am missing a key concept.

Additionally, I corrected an issue where I was setting the underlying object ID, and not the entity ID.

@potelux potelux force-pushed the add-input-number-create-delete-services branch from 5783ab9 to cca4f7f Compare March 25, 2026 14:52
@potelux potelux force-pushed the add-input-number-create-delete-services branch 2 times, most recently from 6247754 to f45728a Compare April 7, 2026 20:47
@potelux
Copy link
Copy Markdown
Author

potelux commented Apr 7, 2026

@frenck -- I would like to implement the same thing for Text Sensor. You don't need to review this now, but is it a feature you'd actually consider? I don't want to implement the Text Sensor one, too, if both will end up rejected.

@potelux potelux force-pushed the add-input-number-create-delete-services branch from f45728a to ead5c7c Compare April 10, 2026 02:13
@potelux potelux force-pushed the add-input-number-create-delete-services branch from ead5c7c to 73ac0e0 Compare April 13, 2026 15:05
@sonarqubecloud
Copy link
Copy Markdown

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.

1 participant