|
| 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) |
0 commit comments