Squeezebox dispatch helper (#37030)

This commit is contained in:
rajlaud 2020-06-24 12:04:17 -05:00 committed by GitHub
parent a798b508bc
commit f7325a7d35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 62 additions and 70 deletions

View File

@ -1,55 +1,18 @@
"""The Logitech Squeezebox integration.""" """The Logitech Squeezebox integration."""
import asyncio
import logging import logging
from pysqueezebox import async_discover
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
from homeassistant.config_entries import SOURCE_DISCOVERY, ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_START
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .const import DOMAIN, ENTRY_PLAYERS, KNOWN_PLAYERS, PLAYER_DISCOVERY_UNSUB from .const import DISCOVERY_TASK, DOMAIN, PLAYER_DISCOVERY_UNSUB
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DISCOVERY_TASK = "discovery_task"
async def start_server_discovery(hass):
"""Start a server discovery task."""
def _discovered_server(server):
asyncio.create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_DISCOVERY},
data={
CONF_HOST: server.host,
CONF_PORT: int(server.port),
"uuid": server.uuid,
},
)
)
hass.data.setdefault(DOMAIN, {})
if DISCOVERY_TASK not in hass.data[DOMAIN]:
_LOGGER.debug("Adding server discovery task for squeezebox")
hass.data[DOMAIN][DISCOVERY_TASK] = hass.async_create_task(
async_discover(_discovered_server)
)
async def async_setup(hass: HomeAssistant, config: dict): async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the Logitech Squeezebox component.""" """Set up the Logitech Squeezebox component."""
if hass.is_running:
asyncio.create_task(start_server_discovery(hass))
else:
hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_START, start_server_discovery(hass)
)
return True return True
@ -66,13 +29,6 @@ async def async_unload_entry(hass, entry):
# Stop player discovery task for this config entry. # Stop player discovery task for this config entry.
hass.data[DOMAIN][entry.entry_id][PLAYER_DISCOVERY_UNSUB]() hass.data[DOMAIN][entry.entry_id][PLAYER_DISCOVERY_UNSUB]()
# Remove config entry's players from list of known players
entry_players = hass.data[DOMAIN][entry.entry_id][ENTRY_PLAYERS]
if entry_players:
for player in entry_players:
_LOGGER.debug("Remove entry player %s from list of known players.", player)
hass.data[DOMAIN][KNOWN_PLAYERS].remove(player)
# Remove stored data for this config entry # Remove stored data for this config entry
hass.data[DOMAIN].pop(entry.entry_id) hass.data[DOMAIN].pop(entry.entry_id)

View File

@ -150,12 +150,11 @@ class SqueezeboxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors = {} errors = {}
if user_input: if user_input:
error = await self._validate_input(user_input) error = await self._validate_input(user_input)
if error: if not error:
errors["base"] = error
else:
return self.async_create_entry( return self.async_create_entry(
title=user_input[CONF_HOST], data=user_input title=user_input[CONF_HOST], data=user_input
) )
errors["base"] = error
return self.async_show_form( return self.async_show_form(
step_id="edit", data_schema=self.data_schema, errors=errors step_id="edit", data_schema=self.data_schema, errors=errors

View File

@ -3,4 +3,5 @@ DOMAIN = "squeezebox"
ENTRY_PLAYERS = "entry_players" ENTRY_PLAYERS = "entry_players"
KNOWN_PLAYERS = "known_players" KNOWN_PLAYERS = "known_players"
PLAYER_DISCOVERY_UNSUB = "player_discovery_unsub" PLAYER_DISCOVERY_UNSUB = "player_discovery_unsub"
DISCOVERY_TASK = "discovery_task"
DEFAULT_PORT = 9000 DEFAULT_PORT = 9000

View File

@ -2,7 +2,7 @@
import asyncio import asyncio
import logging import logging
from pysqueezebox import Server from pysqueezebox import Server, async_discover
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
@ -23,6 +23,7 @@ from homeassistant.components.media_player.const import (
SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_MUTE,
SUPPORT_VOLUME_SET, SUPPORT_VOLUME_SET,
) )
from homeassistant.config_entries import SOURCE_DISCOVERY
from homeassistant.const import ( from homeassistant.const import (
ATTR_COMMAND, ATTR_COMMAND,
CONF_HOST, CONF_HOST,
@ -34,17 +35,20 @@ from homeassistant.const import (
STATE_OFF, STATE_OFF,
STATE_PAUSED, STATE_PAUSED,
STATE_PLAYING, STATE_PLAYING,
STATE_UNAVAILABLE,
) )
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from homeassistant.util.dt import utcnow from homeassistant.util.dt import utcnow
from .__init__ import start_server_discovery
from .const import ( from .const import (
DEFAULT_PORT, DEFAULT_PORT,
DISCOVERY_TASK,
DOMAIN, DOMAIN,
ENTRY_PLAYERS,
KNOWN_PLAYERS, KNOWN_PLAYERS,
PLAYER_DISCOVERY_UNSUB, PLAYER_DISCOVERY_UNSUB,
) )
@ -57,6 +61,8 @@ SERVICE_UNSYNC = "unsync"
ATTR_QUERY_RESULT = "query_result" ATTR_QUERY_RESULT = "query_result"
ATTR_SYNC_GROUP = "sync_group" ATTR_SYNC_GROUP = "sync_group"
SIGNAL_PLAYER_REDISCOVERED = "squeezebox_player_rediscovered"
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DISCOVERY_INTERVAL = 60 DISCOVERY_INTERVAL = 60
@ -107,6 +113,30 @@ SQUEEZEBOX_MODE = {
} }
async def start_server_discovery(hass):
"""Start a server discovery task."""
def _discovered_server(server):
asyncio.create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_DISCOVERY},
data={
CONF_HOST: server.host,
CONF_PORT: int(server.port),
"uuid": server.uuid,
},
)
)
hass.data.setdefault(DOMAIN, {})
if DISCOVERY_TASK not in hass.data[DOMAIN]:
_LOGGER.debug("Adding server discovery task for squeezebox")
hass.data[DOMAIN][DISCOVERY_TASK] = hass.async_create_task(
async_discover(_discovered_server)
)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up squeezebox platform from platform entry in configuration.yaml (deprecated).""" """Set up squeezebox platform from platform entry in configuration.yaml (deprecated)."""
@ -129,13 +159,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN].setdefault(config_entry.entry_id, {}) hass.data[DOMAIN].setdefault(config_entry.entry_id, {})
known_players = hass.data[DOMAIN].get(KNOWN_PLAYERS) known_players = hass.data[DOMAIN].setdefault(KNOWN_PLAYERS, [])
if known_players is None:
hass.data[DOMAIN][KNOWN_PLAYERS] = known_players = []
entry_players = hass.data[DOMAIN][config_entry.entry_id].setdefault(
ENTRY_PLAYERS, []
)
_LOGGER.debug("Creating LMS object for %s", host) _LOGGER.debug("Creating LMS object for %s", host)
lms = Server(async_get_clientsession(hass), host, port, username, password) lms = Server(async_get_clientsession(hass), host, port, username, password)
@ -153,15 +177,16 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
), ),
None, None,
) )
if entity and not entity.available: if entity:
# check if previously unavailable player has connected
await player.async_update() await player.async_update()
entity.available = player.connected async_dispatcher_send(
hass, SIGNAL_PLAYER_REDISCOVERED, player.player_id, player.connected
)
if not entity: if not entity:
_LOGGER.debug("Adding new entity: %s", player) _LOGGER.debug("Adding new entity: %s", player)
entity = SqueezeBoxEntity(player) entity = SqueezeBoxEntity(player)
known_players.append(entity) known_players.append(entity)
entry_players.append(entity)
async_add_entities([entity]) async_add_entities([entity])
players = await lms.async_get_players() players = await lms.async_get_players()
@ -227,6 +252,7 @@ class SqueezeBoxEntity(MediaPlayerEntity):
self._last_update = None self._last_update = None
self._query_result = {} self._query_result = {}
self._available = True self._available = True
self._remove_dispatcher = None
@property @property
def device_state_attributes(self): def device_state_attributes(self):
@ -254,16 +280,17 @@ class SqueezeBoxEntity(MediaPlayerEntity):
"""Return True if device connected to LMS server.""" """Return True if device connected to LMS server."""
return self._available return self._available
@available.setter @callback
def available(self, val): def rediscovered(self, unique_id, connected):
"""Set available to True or False.""" """Make a player available again."""
self._available = bool(val) if unique_id == self.unique_id and connected:
self._available = True
_LOGGER.info("Player %s is available again", self.name)
self._remove_dispatcher()
@property @property
def state(self): def state(self):
"""Return the state of the device.""" """Return the state of the device."""
if not self.available:
return STATE_UNAVAILABLE
if not self._player.power: if not self._player.power:
return STATE_OFF return STATE_OFF
if self._player.mode: if self._player.mode:
@ -282,6 +309,15 @@ class SqueezeBoxEntity(MediaPlayerEntity):
_LOGGER.info("Player %s is not available", self.name) _LOGGER.info("Player %s is not available", self.name)
self._available = False self._available = False
# start listening for restored players
self._remove_dispatcher = async_dispatcher_connect(
self.hass, SIGNAL_PLAYER_REDISCOVERED, self.rediscovered
)
async def async_will_remove_from_hass(self):
"""Remove from list of known players when removed from hass."""
self.hass.data[DOMAIN][KNOWN_PLAYERS].remove(self)
@property @property
def volume_level(self): def volume_level(self):
"""Volume level of the media player (0..1).""" """Volume level of the media player (0..1)."""