"""Snapcast Integration.""" from __future__ import annotations import logging import snapcast.control from snapcast.control.client import Snapclient from homeassistant.components.media_player import MediaPlayerEntity from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddEntitiesCallback from .media_player import SnapcastClientDevice, SnapcastGroupDevice _LOGGER = logging.getLogger(__name__) class HomeAssistantSnapcast: """Snapcast server and data stored in the Home Assistant data object.""" hass: HomeAssistant def __init__( self, hass: HomeAssistant, server: snapcast.control.Snapserver, hpid: str, entry_id: str, ) -> None: """Initialize the HomeAssistantSnapcast object. Parameters ---------- hass: HomeAssistant hass object server : snapcast.control.Snapserver Snapcast server hpid : str host and port entry_id: str ConfigEntry entry_id Returns ------- None """ self.hass: HomeAssistant = hass self.server: snapcast.control.Snapserver = server self.hpid: str = hpid self._entry_id = entry_id self.clients: list[SnapcastClientDevice] = [] self.groups: list[SnapcastGroupDevice] = [] self.hass_async_add_entities: AddEntitiesCallback # connect callbacks self.server.set_on_update_callback(self.on_update) self.server.set_on_connect_callback(self.on_connect) self.server.set_on_disconnect_callback(self.on_disconnect) self.server.set_new_client_callback(self.on_add_client) async def disconnect(self) -> None: """Disconnect from server.""" self.server.set_on_update_callback(None) self.server.set_on_connect_callback(None) self.server.set_on_disconnect_callback(None) self.server.set_new_client_callback(None) await self.server.stop() def on_update(self) -> None: """Update all entities. Retrieve all groups/clients from server and add/update/delete entities. """ if not self.hass_async_add_entities: return new_groups: list[MediaPlayerEntity] = [] groups: list[MediaPlayerEntity] = [] hass_groups = {g.identifier: g for g in self.groups} for group in self.server.groups: if group.identifier in hass_groups: groups.append(hass_groups[group.identifier]) hass_groups[group.identifier].async_schedule_update_ha_state() else: new_groups.append(SnapcastGroupDevice(group, self.hpid, self._entry_id)) new_clients: list[MediaPlayerEntity] = [] clients: list[MediaPlayerEntity] = [] hass_clients = {c.identifier: c for c in self.clients} for client in self.server.clients: if client.identifier in hass_clients: clients.append(hass_clients[client.identifier]) hass_clients[client.identifier].async_schedule_update_ha_state() else: new_clients.append( SnapcastClientDevice(client, self.hpid, self._entry_id) ) del_entities: list[MediaPlayerEntity] = [ x for x in self.groups if x not in groups ] del_entities.extend([x for x in self.clients if x not in clients]) _LOGGER.debug("New clients: %s", str(new_clients)) _LOGGER.debug("New groups: %s", str(new_groups)) _LOGGER.debug("Delete: %s", str(del_entities)) ent_reg = er.async_get(self.hass) for entity in del_entities: ent_reg.async_remove(entity.entity_id) self.hass_async_add_entities(new_clients + new_groups) def on_connect(self) -> None: """Activate all entities and update.""" for client in self.clients: client.set_availability(True) for group in self.groups: group.set_availability(True) _LOGGER.info("Server connected: %s", self.hpid) self.on_update() def on_disconnect(self, ex: Exception | None) -> None: """Deactivate all entities.""" for client in self.clients: client.set_availability(False) for group in self.groups: group.set_availability(False) _LOGGER.warning( "Server disconnected: %s. Trying to reconnect. %s", self.hpid, str(ex or "") ) def on_add_client(self, client: Snapclient) -> None: """Add a Snapcast client. Parameters ---------- client : Snapclient Snapcast client to be added to HA. """ if not self.hass_async_add_entities: return clients = [SnapcastClientDevice(client, self.hpid, self._entry_id)] self.hass_async_add_entities(clients)