Skip to content

Commit 5783ab9

Browse files
committed
Add create and delete services for input number helpers
1 parent 2dd6dc2 commit 5783ab9

3 files changed

Lines changed: 216 additions & 0 deletions

File tree

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""Spook - Your homie."""
2+
3+
from __future__ import annotations
4+
5+
import asyncio
6+
from typing import TYPE_CHECKING
7+
8+
import voluptuous as vol
9+
10+
from homeassistant.components.input_number import (
11+
CONF_INITIAL,
12+
CONF_MAX,
13+
CONF_MIN,
14+
CONF_STEP,
15+
DOMAIN,
16+
MODE_BOX,
17+
MODE_SLIDER,
18+
NumberStorageCollection,
19+
_cv_input_number,
20+
)
21+
from homeassistant.const import CONF_ICON, CONF_ID, CONF_MODE, CONF_NAME, CONF_UNIT_OF_MEASUREMENT
22+
from homeassistant.exceptions import HomeAssistantError
23+
from homeassistant.helpers import config_validation as cv, entity_registry as er
24+
25+
from ....services import AbstractSpookAdminService
26+
27+
if TYPE_CHECKING:
28+
from homeassistant.core import ServiceCall
29+
30+
CONF_INPUT_NUMBER_ID = "input_number_id"
31+
32+
CREATE_FIELDS = {
33+
vol.Required(CONF_NAME): vol.All(str, vol.Length(min=1)),
34+
vol.Optional(CONF_INPUT_NUMBER_ID): cv.slug,
35+
vol.Optional(CONF_MIN, default=0): vol.Coerce(float),
36+
vol.Optional(CONF_MAX, default=100): vol.Coerce(float),
37+
vol.Optional(CONF_INITIAL): vol.Coerce(float),
38+
vol.Optional(CONF_STEP, default=1): vol.All(vol.Coerce(float), vol.Range(min=1e-9)),
39+
vol.Optional(CONF_ICON): cv.icon,
40+
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
41+
vol.Optional(CONF_MODE, default=MODE_SLIDER): vol.In([MODE_SLIDER, MODE_BOX]),
42+
}
43+
44+
45+
class SpookService(AbstractSpookAdminService):
46+
"""Input number service to create a new helper on the fly."""
47+
48+
domain = DOMAIN
49+
service = "create"
50+
schema = vol.All(vol.Schema(CREATE_FIELDS), _cv_input_number)
51+
52+
async def async_handle_service(self, call: ServiceCall) -> None:
53+
"""Handle the service call."""
54+
input_number_id = call.data.get(CONF_INPUT_NUMBER_ID)
55+
data = {k: v for k, v in call.data.items() if k != CONF_INPUT_NUMBER_ID}
56+
57+
collection: NumberStorageCollection = self.hass.data["websocket_api"][
58+
"input_number/list"
59+
][0].__self__.storage_collection
60+
61+
if input_number_id:
62+
async with self.hass.data.setdefault(
63+
f"{DOMAIN}_create_lock", asyncio.Lock()
64+
):
65+
desired_entity_id = f"{DOMAIN}.{input_number_id}"
66+
ent_reg = er.async_get(self.hass)
67+
if ent_reg.async_get(desired_entity_id):
68+
message = f"An input number with entity ID '{desired_entity_id}' already exists"
69+
raise HomeAssistantError(message)
70+
71+
item = await collection.async_create_item(data)
72+
73+
# The entity_id is derived from the name by default. Update the
74+
# entity registry to match the requested input_number_id instead.
75+
if (
76+
current_entity_id := ent_reg.async_get_entity_id(
77+
DOMAIN, DOMAIN, item[CONF_ID]
78+
)
79+
) and current_entity_id != desired_entity_id:
80+
ent_reg.async_update_entity(
81+
current_entity_id, new_entity_id=desired_entity_id
82+
)
83+
else:
84+
await collection.async_create_item(data)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Spook - Your homie."""
2+
3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING
6+
7+
from homeassistant.components.input_number import (
8+
DOMAIN,
9+
InputNumber,
10+
NumberStorageCollection,
11+
)
12+
from homeassistant.exceptions import HomeAssistantError
13+
14+
from ....services import AbstractSpookEntityComponentService
15+
16+
if TYPE_CHECKING:
17+
from homeassistant.core import ServiceCall
18+
19+
20+
class SpookService(AbstractSpookEntityComponentService[InputNumber]):
21+
"""Input number service to delete a helper on the fly."""
22+
23+
domain = DOMAIN
24+
service = "delete"
25+
schema = {}
26+
27+
async def async_handle_service(
28+
self,
29+
entity: InputNumber,
30+
call: ServiceCall,
31+
) -> None:
32+
"""Handle the service call."""
33+
if not entity.editable:
34+
message = f"This input number is not editable: {entity.entity_id}"
35+
raise HomeAssistantError(message)
36+
37+
collection: NumberStorageCollection = self.hass.data["websocket_api"][
38+
"input_number/list"
39+
][0].__self__.storage_collection
40+
await collection.async_delete_item(entity.unique_id)

custom_components/spook/services.yaml

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,98 @@ input_number_min:
956956
entity:
957957
domain: input_number
958958

959+
input_number_create:
960+
name: Create an input number 👻
961+
description: >-
962+
Create a new input number helper on the fly.
963+
fields:
964+
name:
965+
name: Name
966+
description: The name of the new input number helper.
967+
required: true
968+
selector:
969+
text:
970+
input_number_id:
971+
name: Input number entity ID
972+
description: >-
973+
The entity ID for the new input number helper. The input_number. prefix
974+
is added automatically. If not provided, the entity ID will be generated
975+
based on the name.
976+
required: false
977+
selector:
978+
text:
979+
min:
980+
name: Minimum
981+
description: The minimum value of the input number.
982+
required: false
983+
default: 0
984+
selector:
985+
number:
986+
mode: box
987+
step: any
988+
max:
989+
name: Maximum
990+
description: The maximum value of the input number.
991+
required: false
992+
default: 100
993+
selector:
994+
number:
995+
mode: box
996+
step: any
997+
initial:
998+
name: Initial value
999+
description: The initial value of the input number when Home Assistant starts.
1000+
required: false
1001+
selector:
1002+
number:
1003+
mode: box
1004+
step: any
1005+
step:
1006+
name: Step size
1007+
description: The step size of the input number.
1008+
required: false
1009+
default: 1
1010+
selector:
1011+
number:
1012+
mode: box
1013+
step: any
1014+
mode:
1015+
name: Display mode
1016+
description: >-
1017+
The display mode of the input number, which can be either a slider or
1018+
an input box.
1019+
required: false
1020+
default: slider
1021+
selector:
1022+
select:
1023+
options:
1024+
- label: Slider
1025+
value: slider
1026+
- label: Input field
1027+
value: box
1028+
icon:
1029+
name: Icon
1030+
description: The icon to use for the input number helper.
1031+
required: false
1032+
selector:
1033+
icon:
1034+
unit_of_measurement:
1035+
name: Unit of measurement
1036+
description: The unit of measurement of the input number.
1037+
required: false
1038+
selector:
1039+
text:
1040+
1041+
input_number_delete:
1042+
name: Delete an input number 👻
1043+
description: >-
1044+
Delete an input number helper. This works only with input numbers created
1045+
and managed via the UI. Input numbers created and managed in YAML cannot
1046+
be managed by Spook.
1047+
target:
1048+
entity:
1049+
domain: input_number
1050+
9591051
person_add_device_tracker:
9601052
name: Add a device tracker 👻
9611053
description: >-

0 commit comments

Comments
 (0)