221 files

This commit is contained in:
Home Assistant Version Control
2026-10-10 15:24:58 +00:00
parent ae12b88c7d
commit bfafb4c28a
221 changed files with 73725 additions and 3277 deletions
@@ -3,8 +3,8 @@
from __future__ import annotations
from homeassistant.components import homeassistant
from homeassistant.const import __version__
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.loader import async_get_loaded_integration
from ...const import DOMAIN
from ...entity import SpookEntity, SpookEntityDescription
@@ -16,10 +16,21 @@ class HomeAssistantSpookEntity(SpookEntity):
def __init__(self, description: SpookEntityDescription) -> None:
"""Initialize the entity."""
super().__init__(description=description)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, homeassistant.DOMAIN)},
manufacturer="Home Assistant",
name="Home Assistant",
sw_version=__version__,
)
self._attr_unique_id = f"{homeassistant.DOMAIN}_{description.key}"
@property
def device_info(self) -> DeviceInfo:
"""Return the device, which is Spook's, so it runs Spook's version.
Read once the entity is added to Home Assistant, as the version comes
from the loaded integration.
"""
version = async_get_loaded_integration(self.hass, DOMAIN).version
return DeviceInfo(
identifiers={(DOMAIN, homeassistant.DOMAIN)},
manufacturer="Spook 👻",
name="Home Assistant",
sw_version=str(version) if version else None,
configuration_url="https://spook.boo/devices-entities",
entry_type=DeviceEntryType.SERVICE,
)
@@ -13,9 +13,12 @@ from homeassistant.components.homeassistant.exposed_entities import (
async_expose_entity,
)
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv, entity_registry as er
from ....const import DOMAIN
from ....errors import entity_not_found
if TYPE_CHECKING:
from homeassistant.core import HomeAssistant, ServiceCall
@@ -41,13 +44,20 @@ def async_config_entry_ids(hass: HomeAssistant, call: ServiceCall) -> list[str]:
domains: list[str] = call.data.get(CONF_DOMAIN, [])
if not entry_ids and not domains:
msg = "Name the integration entries to change, or their integration"
raise ServiceValidationError(msg)
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="integration_entries_required",
)
for domain in domains:
if not (entries := hass.config_entries.async_entries(domain)):
msg = f"The {domain} integration has no entries"
raise ServiceValidationError(msg)
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="integration_without_entries",
translation_placeholders={
"domain": domain,
},
)
entry_ids.extend(
entry.entry_id for entry in entries if entry.entry_id not in entry_ids
)
@@ -82,8 +92,7 @@ def async_set_voice_assistant_exposure(
hass.states.get(entity_id) is None
and entity_registry.async_get(entity_id) is None
):
msg = f"Unknown entity: {entity_id}"
raise HomeAssistantError(msg)
raise entity_not_found(entity_id)
for entity_id in entity_ids:
for assistant in call.data[CONF_ASSISTANTS]:
@@ -7,9 +7,9 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import area_registry as ar, config_validation as cv
from ....errors import area_not_found
from ....services import AbstractSpookAdminService
if TYPE_CHECKING:
@@ -30,8 +30,7 @@ class SpookService(AbstractSpookAdminService):
"""Handle the service call."""
area_registry = ar.async_get(self.hass)
if not (area := area_registry.async_get_area(call.data["area_id"])):
msg = f"Area {call.data['area_id']} not found"
raise HomeAssistantError(msg)
raise area_not_found(call.data["area_id"])
aliases = area.aliases.copy()
area_registry.async_update(
@@ -7,12 +7,12 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
config_validation as cv,
floor_registry as fr,
)
from ....errors import floor_not_found
from ....services import AbstractSpookAdminService
if TYPE_CHECKING:
@@ -33,8 +33,7 @@ class SpookService(AbstractSpookAdminService):
"""Handle the service call."""
floor_registry = fr.async_get(self.hass)
if not (floor := floor_registry.async_get_floor(call.data["floor_id"])):
msg = f"Floor {call.data['floor_id']} not found"
raise HomeAssistantError(msg)
raise floor_not_found(call.data["floor_id"])
aliases = floor.aliases.copy()
floor_registry.async_update(
@@ -7,13 +7,13 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
area_registry as ar,
config_validation as cv,
floor_registry as fr,
)
from ....errors import floor_not_found
from ....services import AbstractSpookAdminService
if TYPE_CHECKING:
@@ -34,8 +34,7 @@ class SpookService(AbstractSpookAdminService):
"""Handle the service call."""
floor_registry = fr.async_get(self.hass)
if not floor_registry.async_get_floor(call.data["floor_id"]):
msg = f"Floor {call.data['floor_id']} not found"
raise HomeAssistantError(msg)
raise floor_not_found(call.data["floor_id"])
area_registry = ar.async_get(self.hass)
for area_id in call.data["area_id"]:
@@ -7,7 +7,6 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
area_registry as ar,
config_validation as cv,
@@ -15,6 +14,7 @@ from homeassistant.helpers import (
)
from ....core_compat import async_update_any_device
from ....errors import area_not_found
from ....services import AbstractSpookAdminService
if TYPE_CHECKING:
@@ -35,8 +35,7 @@ class SpookService(AbstractSpookAdminService):
"""Handle the service call."""
area_registry = ar.async_get(self.hass)
if not area_registry.async_get_area(call.data["area_id"]):
msg = f"Area {call.data['area_id']} not found"
raise HomeAssistantError(msg)
raise area_not_found(call.data["area_id"])
device_registry = dr.async_get(self.hass)
for device_id in call.data["device_id"]:
@@ -7,13 +7,13 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
area_registry as ar,
config_validation as cv,
entity_registry as er,
)
from ....errors import area_not_found
from ....services import AbstractSpookAdminService
if TYPE_CHECKING:
@@ -34,8 +34,7 @@ class SpookService(AbstractSpookAdminService):
"""Handle the service call."""
area_registry = ar.async_get(self.hass)
if not area_registry.async_get_area(call.data["area_id"]):
msg = f"Area {call.data['area_id']} not found"
raise HomeAssistantError(msg)
raise area_not_found(call.data["area_id"])
entity_registry = er.async_get(self.hass)
for entity_id in call.data["entity_id"]:
@@ -7,12 +7,12 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
area_registry as ar,
config_validation as cv,
)
from ....errors import area_not_found
from ....services import AbstractSpookAdminService
from ..labels import async_check_labels_exist
@@ -41,8 +41,7 @@ class SpookService(AbstractSpookAdminService):
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"
raise HomeAssistantError(msg)
raise area_not_found(area_id)
labels = area_entry.labels.copy()
labels.update(call.data["label_id"])
@@ -7,13 +7,13 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
)
from ....core_compat import async_update_any_device
from ....errors import device_not_found
from ....services import AbstractSpookAdminService
from ..labels import async_check_labels_exist
@@ -42,8 +42,7 @@ class SpookService(AbstractSpookAdminService):
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"
raise HomeAssistantError(msg)
raise device_not_found(device_id)
labels = device_entry.labels.copy()
labels.update(call.data["label_id"])
@@ -7,12 +7,12 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
config_validation as cv,
entity_registry as er,
)
from ....errors import entity_not_found
from ....services import AbstractSpookAdminService
from ..labels import async_check_labels_exist
@@ -41,8 +41,7 @@ class SpookService(AbstractSpookAdminService):
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"
raise HomeAssistantError(msg)
raise entity_not_found(entity_id)
labels = entity_entry.labels.copy()
labels.update(call.data["label_id"])
@@ -7,9 +7,9 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from ....errors import config_entry_not_found
from ....services import AbstractSpookAdminService
if TYPE_CHECKING:
@@ -30,8 +30,7 @@ class SpookService(AbstractSpookAdminService):
call.data["config_entry_id"],
)
):
msg = f"Config entry not found: {call.data['config_entry_id']}"
raise HomeAssistantError(msg)
raise config_entry_not_found(call.data["config_entry_id"])
self.hass.config_entries.async_update_entry(
entry,
@@ -10,6 +10,8 @@ from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from ....const import DOMAIN as SPOOK_DOMAIN
from ....errors import user_not_found
from ....services import AbstractSpookAdminService
if TYPE_CHECKING:
@@ -31,11 +33,15 @@ class SpookService(AbstractSpookAdminService):
for user_id in call.data["user_id"]:
user = await self.hass.auth.async_get_user(user_id)
if user is None:
message = f"Could not find user: {user_id}"
raise HomeAssistantError(message)
raise user_not_found(user_id)
if user.system_generated:
message = f"Cannot disable a system-generated user: {user_id}"
raise HomeAssistantError(message)
raise HomeAssistantError(
translation_domain=SPOOK_DOMAIN,
translation_key="cannot_disable_system_user",
translation_placeholders={
"user_id": user_id,
},
)
users.append(user)
for user in users:
@@ -7,9 +7,9 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from ....errors import config_entry_not_found
from ....services import AbstractSpookAdminService
if TYPE_CHECKING:
@@ -30,8 +30,7 @@ class SpookService(AbstractSpookAdminService):
call.data["config_entry_id"],
)
):
msg = f"Config entry not found: {call.data['config_entry_id']}"
raise HomeAssistantError(msg)
raise config_entry_not_found(call.data["config_entry_id"])
self.hass.config_entries.async_update_entry(
entry,
@@ -10,6 +10,8 @@ from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from ....const import DOMAIN as SPOOK_DOMAIN
from ....errors import user_not_found
from ....services import AbstractSpookAdminService
if TYPE_CHECKING:
@@ -31,11 +33,15 @@ class SpookService(AbstractSpookAdminService):
for user_id in call.data["user_id"]:
user = await self.hass.auth.async_get_user(user_id)
if user is None:
message = f"Could not find user: {user_id}"
raise HomeAssistantError(message)
raise user_not_found(user_id)
if user.system_generated:
message = f"Cannot enable a system-generated user: {user_id}"
raise HomeAssistantError(message)
raise HomeAssistantError(
translation_domain=SPOOK_DOMAIN,
translation_key="cannot_enable_system_user",
translation_placeholders={
"user_id": user_id,
},
)
users.append(user)
for user in users:
@@ -7,9 +7,9 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import area_registry as ar, config_validation as cv
from ....errors import area_not_found
from ....services import AbstractSpookAdminService
if TYPE_CHECKING:
@@ -30,8 +30,7 @@ class SpookService(AbstractSpookAdminService):
"""Handle the service call."""
area_registry = ar.async_get(self.hass)
if not (area := area_registry.async_get_area(call.data["area_id"])):
msg = f"Area {call.data['area_id']} not found"
raise HomeAssistantError(msg)
raise area_not_found(call.data["area_id"])
aliases = area.aliases.copy()
area_registry.async_update(
@@ -7,9 +7,9 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv, floor_registry as fr
from ....errors import floor_not_found
from ....services import AbstractSpookAdminService
if TYPE_CHECKING:
@@ -30,8 +30,7 @@ class SpookService(AbstractSpookAdminService):
"""Handle the service call."""
floor_registry = fr.async_get(self.hass)
if not (floor := floor_registry.async_get_floor(call.data["floor_id"])):
msg = f"Floor {call.data['floor_id']} not found"
raise HomeAssistantError(msg)
raise floor_not_found(call.data["floor_id"])
aliases = floor.aliases.copy()
floor_registry.async_update(
@@ -7,12 +7,12 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
area_registry as ar,
config_validation as cv,
)
from ....errors import area_not_found
from ....services import AbstractSpookAdminService
from ..labels import async_check_labels_exist
@@ -41,8 +41,7 @@ class SpookService(AbstractSpookAdminService):
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"
raise HomeAssistantError(msg)
raise area_not_found(area_id)
labels = area_entry.labels.copy()
labels.difference_update(call.data["label_id"])
@@ -7,13 +7,13 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
)
from ....core_compat import async_update_any_device
from ....errors import device_not_found
from ....services import AbstractSpookAdminService
from ..labels import async_check_labels_exist
@@ -42,8 +42,7 @@ class SpookService(AbstractSpookAdminService):
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"
raise HomeAssistantError(msg)
raise device_not_found(device_id)
labels = device_entry.labels.copy()
labels.difference_update(call.data["label_id"])
@@ -7,12 +7,12 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
config_validation as cv,
entity_registry as er,
)
from ....errors import entity_not_found
from ....services import AbstractSpookAdminService
from ..labels import async_check_labels_exist
@@ -41,8 +41,7 @@ class SpookService(AbstractSpookAdminService):
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"
raise HomeAssistantError(msg)
raise entity_not_found(entity_id)
labels = entity_entry.labels.copy()
labels.difference_update(call.data["label_id"])
@@ -12,7 +12,7 @@ from homeassistant.const import RESTART_EXIT_CODE
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from ....const import LOGGER
from ....const import DOMAIN as SPOOK_DOMAIN, LOGGER
from ....services import AbstractSpookAdminService, ReplaceExistingService
if TYPE_CHECKING:
@@ -42,9 +42,9 @@ class SpookService(AbstractSpookAdminService, ReplaceExistingService):
return
if not self.overriden_service:
msg = "Spook encountered an error while restarting Home Assistant."
raise HomeAssistantError(
msg,
translation_domain=SPOOK_DOMAIN,
translation_key="restart_failed",
)
# Waited on, the way calling Home Assistant's own restart is. It
@@ -7,9 +7,9 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import area_registry as ar, config_validation as cv
from ....errors import area_not_found
from ....services import AbstractSpookAdminService
if TYPE_CHECKING:
@@ -30,8 +30,7 @@ class SpookService(AbstractSpookAdminService):
"""Handle the service call."""
area_registry = ar.async_get(self.hass)
if not area_registry.async_get_area(call.data["area_id"]):
msg = f"Area {call.data['area_id']} not found"
raise HomeAssistantError(msg)
raise area_not_found(call.data["area_id"])
area_registry.async_update(
call.data["area_id"],
@@ -7,9 +7,9 @@ from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.homeassistant import DOMAIN
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv, floor_registry as fr
from ....errors import floor_not_found
from ....services import AbstractSpookAdminService
if TYPE_CHECKING:
@@ -30,8 +30,7 @@ class SpookService(AbstractSpookAdminService):
"""Handle the service call."""
floor_registry = fr.async_get(self.hass)
if not floor_registry.async_get_floor(call.data["floor_id"]):
msg = f"Floor {call.data['floor_id']} not found"
raise HomeAssistantError(msg)
raise floor_not_found(call.data["floor_id"])
floor_registry.async_update(
call.data["floor_id"],