Updated apps
This commit is contained in:
@@ -0,0 +1 @@
|
||||
"""Spook - Your homie."""
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
"""Spook - Your homie."""
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,97 @@
|
||||
"""Spook - Your homie."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.input_number import (
|
||||
CONF_INITIAL,
|
||||
CONF_MAX,
|
||||
CONF_MIN,
|
||||
CONF_STEP,
|
||||
DOMAIN,
|
||||
MODE_BOX,
|
||||
MODE_SLIDER,
|
||||
NumberStorageCollection,
|
||||
_cv_input_number,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_MODE,
|
||||
CONF_NAME,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
)
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv, entity_registry as er
|
||||
|
||||
from ....services import AbstractSpookAdminService
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from homeassistant.core import ServiceCall
|
||||
|
||||
CONF_INPUT_NUMBER_ID = "input_number_id"
|
||||
|
||||
CREATE_FIELDS = {
|
||||
vol.Required(CONF_NAME): vol.All(str, vol.Length(min=1)),
|
||||
vol.Optional(CONF_INPUT_NUMBER_ID): cv.slug,
|
||||
vol.Optional(CONF_MIN, default=0): vol.Coerce(float),
|
||||
vol.Optional(CONF_MAX, default=100): vol.Coerce(float),
|
||||
vol.Optional(CONF_INITIAL): vol.Coerce(float),
|
||||
vol.Optional(CONF_STEP, default=1): vol.All(vol.Coerce(float), vol.Range(min=1e-9)),
|
||||
vol.Optional(CONF_ICON): cv.icon,
|
||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
|
||||
vol.Optional(CONF_MODE, default=MODE_SLIDER): vol.In([MODE_SLIDER, MODE_BOX]),
|
||||
}
|
||||
|
||||
|
||||
class SpookService(AbstractSpookAdminService):
|
||||
"""Input number service to create a new helper on the fly."""
|
||||
|
||||
domain = DOMAIN
|
||||
service = "create"
|
||||
schema = vol.All(vol.Schema(CREATE_FIELDS), _cv_input_number)
|
||||
|
||||
async def async_handle_service(self, call: ServiceCall) -> None:
|
||||
"""Handle the service call."""
|
||||
input_number_id = call.data.get(CONF_INPUT_NUMBER_ID)
|
||||
data = {k: v for k, v in call.data.items() if k != CONF_INPUT_NUMBER_ID}
|
||||
|
||||
collection: NumberStorageCollection
|
||||
if DOMAIN in self.hass.data:
|
||||
collection = self.hass.data[DOMAIN]
|
||||
else:
|
||||
# Major hack to get around edge cases. 👻
|
||||
collection = self.hass.data["websocket_api"]["input_number/list"][
|
||||
0
|
||||
].__self__.storage_collection
|
||||
|
||||
if input_number_id:
|
||||
async with self.hass.data.setdefault(
|
||||
f"{DOMAIN}_create_lock", asyncio.Lock()
|
||||
):
|
||||
desired_entity_id = f"{DOMAIN}.{input_number_id}"
|
||||
ent_reg = er.async_get(self.hass)
|
||||
if ent_reg.async_get(desired_entity_id) or self.hass.states.get(
|
||||
desired_entity_id
|
||||
):
|
||||
message = f"An input number with entity ID '{desired_entity_id}' already exists"
|
||||
raise HomeAssistantError(message)
|
||||
|
||||
item = await collection.async_create_item(data)
|
||||
|
||||
# The entity_id is derived from the name by default. Update the
|
||||
# entity registry to match the requested input_number_id instead.
|
||||
if (
|
||||
current_entity_id := ent_reg.async_get_entity_id(
|
||||
DOMAIN, DOMAIN, item[CONF_ID]
|
||||
)
|
||||
) and current_entity_id != desired_entity_id:
|
||||
ent_reg.async_update_entity(
|
||||
current_entity_id, new_entity_id=desired_entity_id
|
||||
)
|
||||
else:
|
||||
await collection.async_create_item(data)
|
||||
@@ -0,0 +1,50 @@
|
||||
"""Spook - Your homie."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.input_number import DOMAIN, InputNumber
|
||||
|
||||
from ....services import AbstractSpookEntityComponentService, ReplaceExistingService
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from homeassistant.core import ServiceCall
|
||||
|
||||
|
||||
class SpookService(
|
||||
AbstractSpookEntityComponentService[InputNumber], ReplaceExistingService
|
||||
):
|
||||
"""Input number entity service, decrease value by a single step.
|
||||
|
||||
It override the built-in increment service to allow for a custom amount.
|
||||
"""
|
||||
|
||||
domain = DOMAIN
|
||||
service = "decrement"
|
||||
schema = {vol.Optional("amount"): vol.Coerce(float)}
|
||||
|
||||
async def async_handle_service(
|
||||
self,
|
||||
entity: InputNumber,
|
||||
call: ServiceCall,
|
||||
) -> None:
|
||||
"""Handle the service call."""
|
||||
# pylint: disable=protected-access
|
||||
amount = call.data.get("amount", entity._step) # noqa: SLF001
|
||||
if not math.isclose(amount % entity._step, 0, abs_tol=1e-9): # noqa: SLF001
|
||||
msg = (
|
||||
f"Amount {amount} not valid for {entity.entity_id}, "
|
||||
f"it needs to be a multiple of {entity._step}", # noqa: SLF001
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
await entity.async_set_value(
|
||||
max(
|
||||
entity._current_value - amount, # noqa: SLF001
|
||||
entity._minimum, # noqa: SLF001
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,45 @@
|
||||
"""Spook - Your homie."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from homeassistant.components.input_number import (
|
||||
DOMAIN,
|
||||
InputNumber,
|
||||
NumberStorageCollection,
|
||||
)
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from ....services import AbstractSpookEntityComponentService
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from homeassistant.core import ServiceCall
|
||||
|
||||
|
||||
class SpookService(AbstractSpookEntityComponentService[InputNumber]):
|
||||
"""Input number service to delete a helper on the fly."""
|
||||
|
||||
domain = DOMAIN
|
||||
service = "delete"
|
||||
schema = {}
|
||||
|
||||
async def async_handle_service(
|
||||
self,
|
||||
entity: InputNumber,
|
||||
call: ServiceCall, # noqa: ARG002
|
||||
) -> None:
|
||||
"""Handle the service call."""
|
||||
if not entity.editable:
|
||||
message = f"This input number is not editable: {entity.entity_id}"
|
||||
raise HomeAssistantError(message)
|
||||
|
||||
collection: NumberStorageCollection
|
||||
if DOMAIN in self.hass.data:
|
||||
collection = self.hass.data[DOMAIN]
|
||||
else:
|
||||
# Major hack to get around edge cases. 👻
|
||||
collection = self.hass.data["websocket_api"]["input_number/list"][
|
||||
0
|
||||
].__self__.storage_collection
|
||||
await collection.async_delete_item(entity.unique_id)
|
||||
@@ -0,0 +1,50 @@
|
||||
"""Spook - Your homie."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.input_number import DOMAIN, InputNumber
|
||||
|
||||
from ....services import AbstractSpookEntityComponentService, ReplaceExistingService
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from homeassistant.core import ServiceCall
|
||||
|
||||
|
||||
class SpookService(
|
||||
AbstractSpookEntityComponentService[InputNumber], ReplaceExistingService
|
||||
):
|
||||
"""Input number entity service, increase value by a single step.
|
||||
|
||||
It override the built-in increment service to allow for a custom amount.
|
||||
"""
|
||||
|
||||
domain = DOMAIN
|
||||
service = "increment"
|
||||
schema = {vol.Optional("amount"): vol.Coerce(float)}
|
||||
|
||||
async def async_handle_service(
|
||||
self,
|
||||
entity: InputNumber,
|
||||
call: ServiceCall,
|
||||
) -> None:
|
||||
"""Handle the service call."""
|
||||
# pylint: disable=protected-access
|
||||
amount = call.data.get("amount", entity._step) # noqa: SLF001
|
||||
if not math.isclose(amount % entity._step, 0, abs_tol=1e-9): # noqa: SLF001
|
||||
msg = (
|
||||
f"Amount {amount} not valid for {entity.entity_id}, "
|
||||
f"it needs to be a multiple of {entity._step}", # noqa: SLF001
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
await entity.async_set_value(
|
||||
min(
|
||||
entity._current_value + amount, # noqa: SLF001
|
||||
entity._maximum, # noqa: SLF001
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
"""Spook - Your homie."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from homeassistant.components.input_number import DOMAIN, InputNumber
|
||||
|
||||
from ....services import AbstractSpookEntityComponentService
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from homeassistant.core import ServiceCall
|
||||
|
||||
|
||||
class SpookService(AbstractSpookEntityComponentService[InputNumber]):
|
||||
"""Input number entity service, set the max value."""
|
||||
|
||||
domain = DOMAIN
|
||||
service = "max"
|
||||
|
||||
async def async_handle_service(
|
||||
self,
|
||||
entity: InputNumber,
|
||||
call: ServiceCall, # noqa: ARG002
|
||||
) -> None:
|
||||
"""Handle the service call."""
|
||||
# pylint: disable-next=protected-access
|
||||
await entity.async_set_value(entity._maximum) # noqa: SLF001
|
||||
@@ -0,0 +1,28 @@
|
||||
"""Spook - Your homie."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from homeassistant.components.input_number import DOMAIN, InputNumber
|
||||
|
||||
from ....services import AbstractSpookEntityComponentService
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from homeassistant.core import ServiceCall
|
||||
|
||||
|
||||
class SpookService(AbstractSpookEntityComponentService[InputNumber]):
|
||||
"""Input number entity service, set the min value."""
|
||||
|
||||
domain = DOMAIN
|
||||
service = "min"
|
||||
|
||||
async def async_handle_service(
|
||||
self,
|
||||
entity: InputNumber,
|
||||
call: ServiceCall, # noqa: ARG002
|
||||
) -> None:
|
||||
"""Handle the service call."""
|
||||
# pylint: disable-next=protected-access
|
||||
await entity.async_set_value(entity._minimum) # noqa: SLF001
|
||||
Reference in New Issue
Block a user