456 files
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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.
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.
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.
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.
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.
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.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -35,6 +35,10 @@ class SpookService(AbstractSpookAdminService):
|
||||
async_check_labels_exist(self.hass, call.data["label_id"])
|
||||
|
||||
area_registry = ar.async_get(self.hass)
|
||||
|
||||
# Everything is looked up before anything is written. A typo in the
|
||||
# last one should not leave the first ones changed behind an error.
|
||||
updates: dict[str, set[str]] = {}
|
||||
for area_id in call.data["area_id"]:
|
||||
if (area_entry := area_registry.async_get_area(area_id)) is None:
|
||||
msg = f"Area {area_id} not found"
|
||||
@@ -42,4 +46,7 @@ class SpookService(AbstractSpookAdminService):
|
||||
|
||||
labels = area_entry.labels.copy()
|
||||
labels.update(call.data["label_id"])
|
||||
updates[area_id] = labels
|
||||
|
||||
for area_id, labels in updates.items():
|
||||
area_registry.async_update(area_id, labels=labels)
|
||||
|
||||
@@ -36,6 +36,10 @@ class SpookService(AbstractSpookAdminService):
|
||||
async_check_labels_exist(self.hass, call.data["label_id"])
|
||||
|
||||
device_registry = dr.async_get(self.hass)
|
||||
|
||||
# Everything is looked up before anything is written. A typo in the
|
||||
# last one should not leave the first ones changed behind an error.
|
||||
updates: dict[str, set[str]] = {}
|
||||
for device_id in call.data["device_id"]:
|
||||
if (device_entry := device_registry.async_get(device_id)) is None:
|
||||
msg = f"Device {device_id} not found"
|
||||
@@ -43,4 +47,7 @@ class SpookService(AbstractSpookAdminService):
|
||||
|
||||
labels = device_entry.labels.copy()
|
||||
labels.update(call.data["label_id"])
|
||||
updates[device_id] = labels
|
||||
|
||||
for device_id, labels in updates.items():
|
||||
async_update_any_device(device_registry, device_id, labels=labels)
|
||||
|
||||
@@ -35,6 +35,10 @@ class SpookService(AbstractSpookAdminService):
|
||||
async_check_labels_exist(self.hass, call.data["label_id"])
|
||||
|
||||
entity_registry = er.async_get(self.hass)
|
||||
|
||||
# Everything is looked up before anything is written. A typo in the
|
||||
# last one should not leave the first ones changed behind an error.
|
||||
updates: dict[str, set[str]] = {}
|
||||
for entity_id in call.data["entity_id"]:
|
||||
if (entity_entry := entity_registry.async_get(entity_id)) is None:
|
||||
msg = f"Entity {entity_id} not found"
|
||||
@@ -42,4 +46,7 @@ class SpookService(AbstractSpookAdminService):
|
||||
|
||||
labels = entity_entry.labels.copy()
|
||||
labels.update(call.data["label_id"])
|
||||
updates[entity_id] = labels
|
||||
|
||||
for entity_id, labels in updates.items():
|
||||
entity_registry.async_update_entity(entity_id, labels=labels)
|
||||
|
||||
@@ -25,6 +25,9 @@ class SpookService(AbstractSpookAdminService):
|
||||
|
||||
async def async_handle_service(self, call: ServiceCall) -> None:
|
||||
"""Handle the service call."""
|
||||
# Everything is looked up before anything is written. A bad one late
|
||||
# in the list should not leave the first ones changed behind an error.
|
||||
users = []
|
||||
for user_id in call.data["user_id"]:
|
||||
user = await self.hass.auth.async_get_user(user_id)
|
||||
if user is None:
|
||||
@@ -33,4 +36,7 @@ class SpookService(AbstractSpookAdminService):
|
||||
if user.system_generated:
|
||||
message = f"Cannot disable a system-generated user: {user_id}"
|
||||
raise HomeAssistantError(message)
|
||||
users.append(user)
|
||||
|
||||
for user in users:
|
||||
await self.hass.auth.async_update_user(user, is_active=False)
|
||||
|
||||
@@ -25,6 +25,9 @@ class SpookService(AbstractSpookAdminService):
|
||||
|
||||
async def async_handle_service(self, call: ServiceCall) -> None:
|
||||
"""Handle the service call."""
|
||||
# Everything is looked up before anything is written. A bad one late
|
||||
# in the list should not leave the first ones changed behind an error.
|
||||
users = []
|
||||
for user_id in call.data["user_id"]:
|
||||
user = await self.hass.auth.async_get_user(user_id)
|
||||
if user is None:
|
||||
@@ -33,4 +36,7 @@ class SpookService(AbstractSpookAdminService):
|
||||
if user.system_generated:
|
||||
message = f"Cannot enable a system-generated user: {user_id}"
|
||||
raise HomeAssistantError(message)
|
||||
users.append(user)
|
||||
|
||||
for user in users:
|
||||
await self.hass.auth.async_update_user(user, is_active=True)
|
||||
|
||||
@@ -35,6 +35,10 @@ class SpookService(AbstractSpookAdminService):
|
||||
async_check_labels_exist(self.hass, call.data["label_id"])
|
||||
|
||||
area_registry = ar.async_get(self.hass)
|
||||
|
||||
# Everything is looked up before anything is written. A typo in the
|
||||
# last one should not leave the first ones changed behind an error.
|
||||
updates: dict[str, set[str]] = {}
|
||||
for area_id in call.data["area_id"]:
|
||||
if (area_entry := area_registry.async_get_area(area_id)) is None:
|
||||
msg = f"Area {area_id} not found"
|
||||
@@ -42,4 +46,7 @@ class SpookService(AbstractSpookAdminService):
|
||||
|
||||
labels = area_entry.labels.copy()
|
||||
labels.difference_update(call.data["label_id"])
|
||||
updates[area_id] = labels
|
||||
|
||||
for area_id, labels in updates.items():
|
||||
area_registry.async_update(area_id, labels=labels)
|
||||
|
||||
@@ -36,6 +36,10 @@ class SpookService(AbstractSpookAdminService):
|
||||
async_check_labels_exist(self.hass, call.data["label_id"])
|
||||
|
||||
device_registry = dr.async_get(self.hass)
|
||||
|
||||
# Everything is looked up before anything is written. A typo in the
|
||||
# last one should not leave the first ones changed behind an error.
|
||||
updates: dict[str, set[str]] = {}
|
||||
for device_id in call.data["device_id"]:
|
||||
if (device_entry := device_registry.async_get(device_id)) is None:
|
||||
msg = f"Device {device_id} not found"
|
||||
@@ -43,4 +47,7 @@ class SpookService(AbstractSpookAdminService):
|
||||
|
||||
labels = device_entry.labels.copy()
|
||||
labels.difference_update(call.data["label_id"])
|
||||
updates[device_id] = labels
|
||||
|
||||
for device_id, labels in updates.items():
|
||||
async_update_any_device(device_registry, device_id, labels=labels)
|
||||
|
||||
@@ -35,6 +35,10 @@ class SpookService(AbstractSpookAdminService):
|
||||
async_check_labels_exist(self.hass, call.data["label_id"])
|
||||
|
||||
entity_registry = er.async_get(self.hass)
|
||||
|
||||
# Everything is looked up before anything is written. A typo in the
|
||||
# last one should not leave the first ones changed behind an error.
|
||||
updates: dict[str, set[str]] = {}
|
||||
for entity_id in call.data["entity_id"]:
|
||||
if (entity_entry := entity_registry.async_get(entity_id)) is None:
|
||||
msg = f"Entity {entity_id} not found"
|
||||
@@ -42,4 +46,7 @@ class SpookService(AbstractSpookAdminService):
|
||||
|
||||
labels = entity_entry.labels.copy()
|
||||
labels.difference_update(call.data["label_id"])
|
||||
updates[entity_id] = labels
|
||||
|
||||
for entity_id, labels in updates.items():
|
||||
entity_registry.async_update_entity(entity_id, labels=labels)
|
||||
|
||||
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.
BIN
Binary file not shown.
@@ -2,96 +2,32 @@
|
||||
|
||||
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,
|
||||
STORAGE_FIELDS,
|
||||
)
|
||||
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
|
||||
from ....helper_collections import AbstractSpookCreateHelperService
|
||||
|
||||
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,
|
||||
# Core requires a minimum and a maximum. This action never has: it started
|
||||
# out with 0 and 100, and calls written against that keep working.
|
||||
_FIELDS = {
|
||||
**{
|
||||
marker: validator
|
||||
for marker, validator in STORAGE_FIELDS.items()
|
||||
if marker not in (CONF_MIN, CONF_MAX)
|
||||
},
|
||||
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):
|
||||
class SpookService(AbstractSpookCreateHelperService):
|
||||
"""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)
|
||||
fields = _FIELDS
|
||||
|
||||
@@ -2,44 +2,12 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from homeassistant.components.input_number import DOMAIN
|
||||
|
||||
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
|
||||
from ....helper_collections import AbstractSpookDeleteHelperService
|
||||
|
||||
|
||||
class SpookService(AbstractSpookEntityComponentService[InputNumber]):
|
||||
"""Input number service to delete a helper on the fly."""
|
||||
class SpookService(AbstractSpookDeleteHelperService):
|
||||
"""Input number service to delete input numbers 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)
|
||||
|
||||
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user