186 lines
7.1 KiB
Python
186 lines
7.1 KiB
Python
"""Switch entity representing a Squeezebox alarm."""
|
|
|
|
import datetime
|
|
import logging
|
|
from typing import Any, cast
|
|
|
|
from pysqueezebox.player import Alarm
|
|
|
|
from homeassistant.components.switch import SwitchEntity
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.const import EntityCategory, Platform
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import entity_registry as er
|
|
from homeassistant.helpers.device_registry import format_mac
|
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
|
from homeassistant.helpers.event import async_track_time_change
|
|
|
|
from .const import ATTR_ALARM_ID, DOMAIN, SIGNAL_PLAYER_DISCOVERED
|
|
from .coordinator import SqueezeBoxPlayerUpdateCoordinator
|
|
from .entity import SqueezeboxEntity
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: ConfigEntry,
|
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
|
) -> None:
|
|
"""Set up the Squeezebox alarm switch."""
|
|
|
|
async def _player_discovered(
|
|
coordinator: SqueezeBoxPlayerUpdateCoordinator,
|
|
) -> None:
|
|
def _async_listener() -> None:
|
|
"""Handle alarm creation and deletion after coordinator data update."""
|
|
new_alarms: set[str] = set()
|
|
received_alarms: set[str] = set()
|
|
|
|
if coordinator.data["alarms"] and coordinator.available:
|
|
received_alarms = set(coordinator.data["alarms"])
|
|
new_alarms = received_alarms - coordinator.known_alarms
|
|
removed_alarms = coordinator.known_alarms - received_alarms
|
|
|
|
if new_alarms:
|
|
for new_alarm in new_alarms:
|
|
coordinator.known_alarms.add(new_alarm)
|
|
_LOGGER.debug(
|
|
"Setting up alarm entity for alarm %s on player %s",
|
|
new_alarm,
|
|
coordinator.player,
|
|
)
|
|
async_add_entities([SqueezeBoxAlarmEntity(coordinator, new_alarm)])
|
|
|
|
if removed_alarms and coordinator.available:
|
|
for removed_alarm in removed_alarms:
|
|
_uid = f"{coordinator.player_uuid}_alarm_{removed_alarm}"
|
|
_LOGGER.debug(
|
|
"Alarm %s with unique_id %s needs to be deleted",
|
|
removed_alarm,
|
|
_uid,
|
|
)
|
|
|
|
entity_registry = er.async_get(hass)
|
|
_entity_id = entity_registry.async_get_entity_id(
|
|
Platform.SWITCH,
|
|
DOMAIN,
|
|
_uid,
|
|
)
|
|
if _entity_id:
|
|
entity_registry.async_remove(_entity_id)
|
|
coordinator.known_alarms.remove(removed_alarm)
|
|
|
|
_LOGGER.debug(
|
|
"Setting up alarm enabled entity for player %s", coordinator.player
|
|
)
|
|
# Add listener first for future coordinator refresh
|
|
coordinator.async_add_listener(_async_listener)
|
|
|
|
# If coordinator already has alarm data from the initial refresh,
|
|
# call the listener immediately to process existing alarms and create alarm entities.
|
|
if coordinator.data["alarms"]:
|
|
_LOGGER.debug(
|
|
"Coordinator has alarm data, calling _async_listener immediately for player %s",
|
|
coordinator.player,
|
|
)
|
|
_async_listener()
|
|
async_add_entities([SqueezeBoxAlarmsEnabledEntity(coordinator)])
|
|
|
|
entry.async_on_unload(
|
|
async_dispatcher_connect(hass, SIGNAL_PLAYER_DISCOVERED, _player_discovered)
|
|
)
|
|
|
|
|
|
class SqueezeBoxAlarmEntity(SqueezeboxEntity, SwitchEntity):
|
|
"""Representation of a Squeezebox alarm switch."""
|
|
|
|
_attr_translation_key = "alarm"
|
|
_attr_entity_category = EntityCategory.CONFIG
|
|
|
|
def __init__(
|
|
self, coordinator: SqueezeBoxPlayerUpdateCoordinator, alarm_id: str
|
|
) -> None:
|
|
"""Initialize the Squeezebox alarm switch."""
|
|
super().__init__(coordinator)
|
|
self._alarm_id = alarm_id
|
|
self._attr_translation_placeholders = {"alarm_id": self._alarm_id}
|
|
self._attr_unique_id: str = (
|
|
f"{format_mac(self._player.player_id)}_alarm_{self._alarm_id}"
|
|
)
|
|
|
|
async def async_added_to_hass(self) -> None:
|
|
"""Set up alarm switch when added to hass."""
|
|
await super().async_added_to_hass()
|
|
|
|
async def async_write_state_daily(now: datetime.datetime) -> None:
|
|
"""Update alarm state attributes each calendar day."""
|
|
_LOGGER.debug("Updating state attributes for %s", self.name)
|
|
self.async_write_ha_state()
|
|
|
|
self.async_on_remove(
|
|
async_track_time_change(
|
|
self.hass, async_write_state_daily, hour=0, minute=0, second=0
|
|
)
|
|
)
|
|
|
|
@property
|
|
def alarm(self) -> Alarm:
|
|
"""Return the alarm object."""
|
|
return self.coordinator.data["alarms"][self._alarm_id]
|
|
|
|
@property
|
|
def available(self) -> bool:
|
|
"""Return whether the alarm is available."""
|
|
return super().available and self._alarm_id in self.coordinator.data["alarms"]
|
|
|
|
@property
|
|
def extra_state_attributes(self) -> dict[str, Any]:
|
|
"""Return attributes of Squeezebox alarm switch."""
|
|
return {ATTR_ALARM_ID: str(self._alarm_id)}
|
|
|
|
@property
|
|
def is_on(self) -> bool:
|
|
"""Return the state of the switch."""
|
|
return cast(bool, self.alarm["enabled"])
|
|
|
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
|
"""Turn off the switch."""
|
|
await self.coordinator.player.async_update_alarm(self._alarm_id, enabled=False)
|
|
await self.coordinator.async_request_refresh()
|
|
|
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
|
"""Turn on the switch."""
|
|
await self.coordinator.player.async_update_alarm(self._alarm_id, enabled=True)
|
|
await self.coordinator.async_request_refresh()
|
|
|
|
|
|
class SqueezeBoxAlarmsEnabledEntity(SqueezeboxEntity, SwitchEntity):
|
|
"""Representation of a Squeezebox players alarms enabled master switch."""
|
|
|
|
_attr_translation_key = "alarms_enabled"
|
|
_attr_entity_category = EntityCategory.CONFIG
|
|
|
|
def __init__(self, coordinator: SqueezeBoxPlayerUpdateCoordinator) -> None:
|
|
"""Initialize the Squeezebox alarm switch."""
|
|
super().__init__(coordinator)
|
|
self._attr_unique_id: str = (
|
|
f"{format_mac(self._player.player_id)}_alarms_enabled"
|
|
)
|
|
|
|
@property
|
|
def is_on(self) -> bool:
|
|
"""Return the state of the switch."""
|
|
return cast(bool, self.coordinator.player.alarms_enabled)
|
|
|
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
|
"""Turn off the switch."""
|
|
await self.coordinator.player.async_set_alarms_enabled(False)
|
|
await self.coordinator.async_request_refresh()
|
|
|
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
|
"""Turn on the switch."""
|
|
await self.coordinator.player.async_set_alarms_enabled(True)
|
|
await self.coordinator.async_request_refresh()
|