1 """Code to handle the api connection to a Roon server."""
6 from roonapi
import RoonApi, RoonDiscovery
12 from .const
import CONF_ROON_ID, ROON_APPINFO
14 _LOGGER = logging.getLogger(__name__)
15 INITIAL_SYNC_INTERVAL = 5
16 FULL_SYNC_INTERVAL = 30
20 """Manages a single Roon Server."""
23 """Initialize the system."""
36 """Set up a roon server based on config parameters."""
42 _LOGGER.debug(
"static roon core host=%s port=%s", host, port)
45 discover = RoonDiscovery(core_id)
46 server = discover.first()
48 _LOGGER.debug(
"dynamic roon core core_id=%s server=%s", core_id, server)
49 return (server[0], server[1])
53 (host, port) = get_roon_host()
54 return RoonApi(ROON_APPINFO, token, host, port, blocking_init=
True)
56 core_id = self.
config_entryconfig_entry.data.get(CONF_ROON_ID)
58 self.
roonapiroonapi = await self.
hasshass.async_add_executor_job(get_roon_api)
60 self.
roonapiroonapi.register_state_callback(
66 core_id
if core_id
is not None else self.
config_entryconfig_entry.data[CONF_HOST]
70 self.
config_entryconfig_entry.async_create_background_task(
77 """Reset this connection to default state.
79 Will cancel any scheduled setup retry and will unload
87 """Return list of zones."""
88 return self.
roonapiroonapi.zones
91 """Register a roon player."""
96 """Get the name of the roon player from entity_id."""
100 """Get the id of the roon player from the roon name."""
104 """Stop background worker."""
106 self.
_exit_exit =
True
109 """Callbacks from the roon api websocket with state change."""
113 """Background work loop."""
114 self.
_exit_exit =
False
115 await asyncio.sleep(INITIAL_SYNC_INTERVAL)
116 while not self.
_exit_exit:
118 await asyncio.sleep(FULL_SYNC_INTERVAL)
121 """Update the players which were reported as changed by the Roon API."""
122 _LOGGER.debug(
"async_update_changed_players %s", changed_zones_ids)
123 for zone_id
in changed_zones_ids:
124 if zone_id
not in self.
roonapiroonapi.zones:
127 zone = self.
roonapiroonapi.zones[zone_id]
128 for device
in zone[
"outputs"]:
129 dev_name = device[
"display_name"]
130 if dev_name ==
"Unnamed" or not dev_name:
134 dev_id = player_data[
"dev_id"]
135 player_data[
"is_available"] =
True
143 """Periodic full scan of all devices."""
144 zone_ids = self.
roonapiroonapi.zones.keys()
145 _LOGGER.debug(
"async_update_players %s", zone_ids)
149 for zone
in self.
roonapiroonapi.zones.values():
150 for device
in zone[
"outputs"]:
152 dev_id = player_data[
"dev_id"]
153 all_devs[dev_id] = player_data
155 if dev_id
in all_devs:
158 player_data = {
"dev_id": dev_id}
159 player_data[
"is_available"] =
False
164 """Create player object dict by combining zone with output."""
165 new_dict = zone.copy()
166 new_dict.update(output)
167 new_dict.pop(
"outputs")
168 new_dict[
"roon_id"] = self.
roon_idroon_id
169 new_dict[
"is_synced"] = len(zone[
"outputs"]) > 1
170 new_dict[
"zone_name"] = zone[
"display_name"]
171 new_dict[
"display_name"] = output[
"display_name"]
172 new_dict[
"last_changed"] =
utcnow()
174 new_dict[
"dev_id"] = f
"roon_{self.roon_id}_{output['display_name']}"
def async_create_player_data(self, zone, output)
def roon_name(self, entity_id)
def async_update_players(self)
def roonapi_state_callback(self, event, changed_zones)
def async_update_changed_players(self, changed_zones_ids)
def async_setup(self, tries=0)
def __init__(self, hass, config_entry)
def entity_id(self, roon_name)
def add_player_id(self, entity_id, roon_name)
bool add(self, _T matcher)
bool remove(self, _T matcher)
web.Response get(self, web.Request request, str config_key)
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)