Updated apps

This commit is contained in:
2026-07-20 22:52:35 -04:00
parent 28a8cb98f6
commit a0c3271743
1164 changed files with 94781 additions and 6892 deletions
@@ -0,0 +1 @@
"""Spook - Your homie."""
@@ -0,0 +1,73 @@
"""Spook - Your homie."""
from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
from homeassistant.const import EntityCategory
from homeassistant.helpers import issue_registry as ir
from ...entity import SpookEntityDescription
from .entity import RepairsSpookEntity
if TYPE_CHECKING:
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@dataclass(frozen=True, kw_only=True)
class RepairsSpookButtonEntityDescription(
SpookEntityDescription,
ButtonEntityDescription,
):
"""Class describing Spook Repairs button entities."""
ignore: bool
BUTTONS: tuple[RepairsSpookButtonEntityDescription, ...] = (
RepairsSpookButtonEntityDescription(
key="ignore_all",
translation_key="repairs_ignore_all",
entity_id="button.ignore_all_issues",
icon="mdi:checkbox-outline",
entity_category=EntityCategory.CONFIG,
ignore=True,
),
RepairsSpookButtonEntityDescription(
key="unignore_all",
translation_key="repairs_unignore_all",
entity_id="button.unignore_all_issues",
icon="mdi:checkbox-blank-outline",
entity_category=EntityCategory.CONFIG,
ignore=False,
),
)
async def async_setup_entry(
_hass: HomeAssistant,
_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Spook sensor."""
async_add_entities(RepairsSpookButtonEntity(description) for description in BUTTONS)
class RepairsSpookButtonEntity(RepairsSpookEntity, ButtonEntity):
"""Spook button providing Repairs actions."""
entity_description: RepairsSpookButtonEntityDescription
async def async_press(self) -> None:
"""Press the button."""
issue_registry = ir.async_get(self.hass)
for domain, issue_id in issue_registry.issues:
issue_registry.async_ignore(
domain,
issue_id,
ignore=self.entity_description.ignore,
)
@@ -0,0 +1,25 @@
"""Spook - Your homie."""
from __future__ import annotations
from homeassistant.components import repairs
from homeassistant.const import __version__
from homeassistant.helpers.device_registry import DeviceInfo
from ...const import DOMAIN
from ...entity import SpookEntity, SpookEntityDescription
class RepairsSpookEntity(SpookEntity):
"""Defines an base Spook entity for Repairs related entities."""
def __init__(self, description: SpookEntityDescription) -> None:
"""Initialize the entity."""
super().__init__(description=description)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, repairs.DOMAIN)},
manufacturer="Home Assistant",
name="Repairs",
sw_version=__version__,
)
self._attr_unique_id = f"{repairs.DOMAIN}_{description.key}"
@@ -0,0 +1,71 @@
"""Spook - Your homie."""
from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING
from homeassistant.components.event import EventEntity, EventEntityDescription
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.helpers.issue_registry import EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED
from ...entity import SpookEntityDescription
from .entity import RepairsSpookEntity
if TYPE_CHECKING:
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@dataclass(frozen=True, kw_only=True)
class RepairsSpookEventEntityDescription(
SpookEntityDescription,
EventEntityDescription,
):
"""Class describing Spook Repairs event entities."""
async def async_setup_entry(
_hass: HomeAssistant,
_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Spook event."""
async_add_entities(
[
RepairsSpookEventEntity(
RepairsSpookEventEntityDescription(
key="event",
translation_key="repairs_event",
entity_id="event.repair",
event_types=["create", "remove", "update"],
),
),
],
)
class RepairsSpookEventEntity(RepairsSpookEntity, EventEntity):
"""Spook sensor providing repairs information."""
entity_description: RepairsSpookEventEntityDescription
_attr_name = None
@callback
def _handle_repairs_issue_registry_updated_event(self, event: Event) -> None:
"""Handle repairs issue registry updates."""
data = {**event.data}
if (event_type := data.pop("action", None)) is None:
return
self._trigger_event(event_type, data)
self.async_schedule_update_ha_state()
async def async_added_to_hass(self) -> None:
"""Register for event updates."""
self.async_on_remove(
self.hass.bus.async_listen(
EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED,
self._handle_repairs_issue_registry_updated_event,
),
)
@@ -0,0 +1,118 @@
"""Spook - Your homie."""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import TYPE_CHECKING
from homeassistant.components.sensor import (
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EntityCategory
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.helpers import issue_registry as ir
from ...entity import SpookEntityDescription
from ...listeners import async_listen_once_tracked
from .entity import RepairsSpookEntity
if TYPE_CHECKING:
from collections.abc import Callable
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@dataclass(frozen=True, kw_only=True)
class RepairsSpookSensorEntityDescription(
SpookEntityDescription,
SensorEntityDescription,
):
"""Class describing Spook Repairs sensor entities."""
value_fn: Callable[[list[ir.IssueEntry]], int]
update_events: set[str] = field(default_factory=set)
SENSORS: tuple[RepairsSpookSensorEntityDescription, ...] = (
RepairsSpookSensorEntityDescription(
key="total_issues",
translation_key="repairs_total_issues",
entity_id="sensor.issues",
icon="mdi:wrench",
native_unit_of_measurement="issues",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={ir.EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED},
value_fn=lambda items: len([item for item in items if item.active]),
),
RepairsSpookSensorEntityDescription(
key="active_issues",
translation_key="repairs_active_issues",
entity_id="sensor.active_issues",
icon="mdi:wrench-check",
native_unit_of_measurement="issues",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={ir.EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED},
value_fn=lambda items: len(
[item for item in items if item.active and not item.dismissed_version],
),
),
RepairsSpookSensorEntityDescription(
key="ignored_issues",
translation_key="repairs_ignored_issues",
entity_id="sensor.ignored_issues",
icon="mdi:wrench-clock",
native_unit_of_measurement="issues",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={ir.EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED},
value_fn=lambda items: len(
[item for item in items if item.active and item.dismissed_version],
),
),
)
async def async_setup_entry(
_hass: HomeAssistant,
_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Spook sensor."""
async_add_entities(
HomeAssistantSpookSensorEntity(description) for description in SENSORS
)
class HomeAssistantSpookSensorEntity(RepairsSpookEntity, SensorEntity):
"""Spook sensor providing repairs information."""
entity_description: RepairsSpookSensorEntityDescription
async def async_added_to_hass(self) -> None:
"""Register for sensor updates."""
@callback
def _update_state(_: Event) -> None:
"""Update state."""
self.async_schedule_update_ha_state()
for event in self.entity_description.update_events:
self.async_on_remove(self.hass.bus.async_listen(event, _update_state))
self.async_on_remove(
async_listen_once_tracked(
self.hass, EVENT_HOMEASSISTANT_STARTED, _update_state
),
)
@property
def native_value(self) -> int:
"""Return the sensor value."""
return self.entity_description.value_fn(
list(ir.async_get(self.hass).issues.values()),
)
@@ -0,0 +1 @@
"""Spook - Your homie."""
@@ -0,0 +1,51 @@
"""Spook - Your homie."""
from __future__ import annotations
from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.repairs import DOMAIN as REPAIRS_DOMAIN
from homeassistant.helpers import config_validation as cv, issue_registry as ir
from homeassistant.util.ulid import ulid
from ....const import DOMAIN
from ....services import AbstractSpookService
if TYPE_CHECKING:
from homeassistant.core import ServiceCall
class SpookService(AbstractSpookService):
"""Home Assistant Repairs service to create your own issues."""
domain = REPAIRS_DOMAIN
service = "create"
schema = {
vol.Required("title"): cv.string,
vol.Required("description"): cv.string,
vol.Optional("issue_id", default=ulid): cv.string,
vol.Optional("domain", default=DOMAIN): cv.string,
vol.Optional("severity", default=ir.IssueSeverity.WARNING): vol.Coerce(
ir.IssueSeverity,
),
vol.Optional("persistent", default=False): cv.boolean,
}
async def async_handle_service(self, call: ServiceCall) -> None:
"""Handle the service call."""
ir.async_create_issue(
self.hass,
domain=DOMAIN,
is_fixable=True,
is_persistent=call.data["persistent"],
issue_domain=call.data["domain"],
issue_id=f"user_{call.data['issue_id']}",
severity=call.data["severity"],
translation_key="user_issue",
translation_placeholders={
"title": call.data["title"],
"description": call.data["description"],
},
)
@@ -0,0 +1,26 @@
"""Spook - Your homie."""
from __future__ import annotations
from typing import TYPE_CHECKING
from homeassistant.components.repairs import DOMAIN
from homeassistant.helpers import issue_registry as ir
from ....services import AbstractSpookService
if TYPE_CHECKING:
from homeassistant.core import ServiceCall
class SpookService(AbstractSpookService):
"""Home Assistant Repairs service for ignoring all issues."""
domain = DOMAIN
service = "ignore_all"
async def async_handle_service(self, _: ServiceCall) -> None:
"""Handle the service call."""
issue_registry = ir.async_get(self.hass)
for domain, issue_id in issue_registry.issues:
issue_registry.async_ignore(domain, issue_id, ignore=True)
@@ -0,0 +1,28 @@
"""Spook - Your homie."""
from __future__ import annotations
from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.repairs import DOMAIN as REPAIRS_DOMAIN
from homeassistant.helpers import config_validation as cv, issue_registry as ir
from ....const import DOMAIN
from ....services import AbstractSpookService
if TYPE_CHECKING:
from homeassistant.core import ServiceCall
class SpookService(AbstractSpookService):
"""Home Assistant Repairs service to create your own issues."""
domain = REPAIRS_DOMAIN
service = "remove"
schema = {vol.Required("issue_id"): cv.string}
async def async_handle_service(self, call: ServiceCall) -> None:
"""Handle the service call."""
ir.async_delete_issue(self.hass, DOMAIN, f"user_{call.data['issue_id']}")
@@ -0,0 +1,26 @@
"""Spook - Your homie."""
from __future__ import annotations
from typing import TYPE_CHECKING
from homeassistant.components.repairs import DOMAIN
from homeassistant.helpers import issue_registry as ir
from ....services import AbstractSpookService
if TYPE_CHECKING:
from homeassistant.core import ServiceCall
class SpookService(AbstractSpookService):
"""Home Assistant Repairs service for unignoring all issues."""
domain = DOMAIN
service = "unignore_all"
async def async_handle_service(self, _: ServiceCall) -> None:
"""Handle the service call."""
issue_registry = ir.async_get(self.hass)
for domain, issue_id in issue_registry.issues:
issue_registry.async_ignore(domain, issue_id, ignore=False)