if player_id not in self._players:
return
player = self._players[player_id]
+ prov_id = player.player_provider
+ prov = self.providers[prov_id]
+ LOGGER.info('received command %s for player %s' %(cmd, player.name))
# handle some common workarounds
if cmd in ['pause', 'play'] and cmd_args == 'toggle':
cmd = 'pause' if player.state == PlayerState.Playing else 'play'
- if cmd == 'power' and cmd_args == 'toggle':
+ if cmd == 'power' and (cmd_args == 'toggle' or not cmd_args):
cmd_args = 'off' if player.powered else 'on'
- if cmd == 'volume' and (cmd_args == 'up' or '+' in cmd_args):
+ if cmd == 'volume' and (cmd_args == 'up' or '+' in str(cmd_args)):
cmd_args = player.volume_level + 2
- elif cmd == 'volume' and (cmd_args == 'down' or '-' in cmd_args):
+ elif cmd == 'volume' and (cmd_args == 'down' or '-' in str(cmd_args)):
cmd_args = player.volume_level - 2
# redirect playlist related commands to parent player
if player.group_parent and cmd not in ['power', 'volume', 'mute']:
await self.__player_command_hass_integration(player, cmd, cmd_args)
# handle mute as power
if cmd == 'power' and player.settings['mute_as_power']:
- cmd = 'mute'
- cmd_args = 'on' if cmd_args == 'off' else 'off' # invert logic (power ON is mute OFF)
- # normal execution of command on player
- prov_id = self._players[player_id].player_provider
- prov = self.providers[prov_id]
- await prov.player_command(player_id, cmd, cmd_args)
+ args = 'on' if cmd_args == 'off' else 'off' # invert logic (power ON is mute OFF)
+ await prov.player_command(player_id, 'mute', args)
# handle play on power on
if cmd == 'power' and cmd_args == 'on' and player.settings['play_power_on']:
- await prov.player_command(player_id, 'play')
+ cmd = 'play'
+ cmd_args = None
+ # normal execution of command on player
+ await prov.player_command(player_id, cmd, cmd_args)
# handle group volume/power for group players
await self.__player_group_commands(player, cmd, cmd_args)
player_childs = [item for item in self._players.values() if item.group_parent == group_player.player_id]
if not group_player.powered and cmd == 'power' and cmd_args == 'on':
# power on group player
- await self.player_command(group_player.player_id, 'power', 'on')
+ self.mass.event_loop.create_task(
+ self.player_command(group_player.player_id, 'power', 'on'))
elif group_player.powered and cmd == 'power' and cmd_args == 'off':
# check if the group player should still be turned on
new_powered = False
new_powered = True
break
if not new_powered:
- await self.player_command(group_player.player_id, 'power', 'off')
+ self.mass.event_loop.create_task(
+ self.player_command(group_player.player_id, 'power', 'off'))
async def remove_player(self, player_id):
''' handle a player remove '''
player_childs = [item for item in self._players.values() if item.group_parent == player_id]
if player.settings['apply_group_volume']:
player_details.volume_level = await self.__get_group_volume(player_childs)
- if player.settings['apply_group_power']:
- player_details.powered = await self.__get_group_power(player_childs)
# compare values to detect changes
if player.cur_item and player_details.cur_item and player.cur_item.name != player_details.cur_item.name:
player_changed = True
if player_changed:
# player is added or updated!
asyncio.ensure_future(self.mass.event('player updated', player))
- # is groupplayer, trigger update of its childs
- # for child in player_childs:
- # asyncio.create_task(self.trigger_update(child.player_id))
- # # if child player in a group, trigger update of parent
- # if player.group_parent:
- # asyncio.create_task(self.trigger_update(player.group_parent))
+ if player_details.is_group:
+ # is groupplayer, trigger update of its childs
+ player_childs = [item for item in self._players.values() if item.group_parent == player_id]
+ for child in player_childs:
+ asyncio.create_task(self.trigger_update(child.player_id))
+ elif player.group_parent:
+ # if child player in a group, trigger update of parent
+ asyncio.create_task(self.trigger_update(player.group_parent))
async def __update_player_hass_settings(self, player_details, player_settings):
''' handle home assistant integration on a player '''
''' handle group volume '''
group_power = False
for child_player in player_childs:
+ print(child_player.name)
+ print(child_player.powered)
if child_player.enabled and child_player.powered:
group_power = True
break
from pychromecast.controllers.media import MediaController
import types
import urllib
+import select
def setup(mass):
''' setup the provider'''
self._chromecasts = {}
self._player_queue = {}
self.supported_musicproviders = ['http']
- self.http_session = aiohttp.ClientSession(loop=mass.event_loop)
asyncio.ensure_future(self.__discover_chromecasts())
async def player_command(self, player_id, cmd:str, cmd_args=None):
''' issue command on player (play, pause, next, previous, stop, power, volume, mute) '''
if cmd == 'play':
+ self._players[player_id].powered = True
if self._chromecasts[player_id].media_controller.status.player_is_playing:
pass
elif self._chromecasts[player_id].media_controller.status.player_is_paused:
self._chromecasts[player_id].media_controller.play()
else:
await self.__resume_queue(player_id)
+ await self.mass.player.update_player(self._players[player_id])
elif cmd == 'pause':
self._chromecasts[player_id].media_controller.pause()
elif cmd == 'stop':
self._chromecasts[player_id].set_volume_muted(False)
elif cmd == 'mute':
self._chromecasts[player_id].set_volume_muted(True)
+ #self._chromecasts[player_id].wait()
async def player_queue(self, player_id, offset=0, limit=50):
''' return the current items in the player's queue '''
''' load queue on player with given queue items '''
castplayer = self._chromecasts[player_id]
player = self._players[player_id]
- media_controller = castplayer.media_controller
- receiver_ctrl = media_controller._socket_client.receiver_controller
queue_items = await self.__create_queue_items(new_tracks[:50])
queuedata = {
"type": 'QUEUE_LOAD',
"startIndex": startindex, # Item index to play after this request or keep same item if undefined
"items": queue_items # only load 50 tracks at once or the socket will crash
}
- await self.__send_player_queue(receiver_ctrl, media_controller, queuedata)
+ await self.__send_player_queue(castplayer, queuedata)
if len(new_tracks) > 50:
await self.__queue_insert(player_id, new_tracks[51:])
''' insert item into the player queue '''
castplayer = self._chromecasts[player_id]
queue_items = await self.__create_queue_items(new_tracks)
- media_controller = castplayer.media_controller
- receiver_ctrl = media_controller._socket_client.receiver_controller
for chunk in chunks(queue_items, 50):
queuedata = {
"type": 'QUEUE_INSERT',
"insertBefore": insert_before,
"items": chunk
}
- await self.__send_player_queue(receiver_ctrl, media_controller, queuedata)
+ await self.__send_player_queue(castplayer, queuedata)
async def __queue_update(self, player_id, queue_items_to_update):
''' update the cast player queue '''
castplayer = self._chromecasts[player_id]
- media_controller = castplayer.media_controller
- receiver_ctrl = media_controller._socket_client.receiver_controller
queuedata = {
"type": 'QUEUE_UPDATE',
"items": queue_items_to_update
}
- await self.__send_player_queue(receiver_ctrl, media_controller, queuedata)
+ await self.__send_player_queue(castplayer, queuedata)
async def __queue_remove(self, player_id, queue_item_ids):
''' remove items from the cast player queue '''
- media_controller = self._chromecasts[player_id].media_controller
- receiver_ctrl = media_controller._socket_client.receiver_controller
+ castplayer = self._chromecasts[player_id]
queuedata = {
"type": 'QUEUE_REMOVE',
"items": queue_item_ids
}
- await self.__send_player_queue(receiver_ctrl, media_controller, queuedata)
+ await self.__send_player_queue(castplayer, queuedata)
async def __resume_queue(self, player_id):
''' resume queue play after power off '''
}
}
- async def __send_player_queue(self, receiver_ctrl, media_controller, queuedata):
+ async def __send_player_queue(self, castplayer, queuedata):
'''send new data to the CC queue'''
+ media_controller = castplayer.media_controller
+ receiver_ctrl = media_controller._socket_client.receiver_controller
def app_launched_callback():
"""Plays media after chromecast has switched to requested app."""
queuedata['mediaSessionId'] = media_controller.status.media_session_id
media_controller.send_message(queuedata, inc_session_id=False)
+ #castplayer.wait()
receiver_ctrl.launch_app(media_controller.app_id,
callback_function=app_launched_callback)
player.muted = caststatus.volume_muted
player.volume_level = caststatus.volume_level * 100
if mediastatus:
- # chromecast does not support power on/of so we use idle state instead
+ # chromecast does not support power on/of so we only set state
if mediastatus.player_state in ['PLAYING', 'BUFFERING']:
player.state = PlayerState.Playing
- player.powered = True
elif mediastatus.player_state == 'PAUSED':
player.state = PlayerState.Paused
- player.powered = not chromecast.is_idle
else:
player.state = PlayerState.Stopped
- player.powered = player.powered
player.cur_item = await self.__parse_track(mediastatus)
player.cur_item_time = chromecast.media_controller.status.adjusted_current_time
await self.mass.player.update_player(player)
if added_player:
if added_player in self._players:
self._players[added_player].group_parent = str(mz._uuid)
+ self.mass.event_loop.create_task(self.mass.player.update_player(self._players[added_player]))
elif removed_player:
if removed_player in self._players:
self._players[removed_player].group_parent = None
+ self.mass.event_loop.create_task(self.mass.player.update_player(self._players[removed_player]))
else:
for member in mz.members:
if member in self._players:
self._players[member].group_parent = str(mz._uuid)
+ self.mass.event_loop.create_task(self.mass.player.update_player(self._players[member]))
+
+ async def __chromecast_discovered(self, chromecast):
+ LOGGER.info("discovered chromecast: %s" % chromecast)
+ player_id = str(chromecast.uuid)
+ ip_change = False
+ if player_id in self._chromecasts and chromecast.uri != self._chromecasts[player_id].uri:
+ LOGGER.warning('Chromecast uri changed ?! - old: %s - new: %s' %(self._chromecasts[player_id].uri, chromecast.uri))
+ ip_change = True
+ if not player_id in self._players or ip_change:
+ player = MusicPlayer()
+ player.player_id = player_id
+ player.name = chromecast.name
+ player.player_provider = self.prov_id
+ # patch the receive message method for handling queue status updates
+ chromecast.queue = []
+ chromecast.media_controller.queue_items = []
+ chromecast.media_controller.queue_cur_id = None
+ chromecast.media_controller.receive_message = types.MethodType(receive_message, chromecast.media_controller)
+ listenerCast = StatusListener(chromecast, self.__handle_player_state, self.mass.event_loop)
+ chromecast.register_status_listener(listenerCast)
+ listenerMedia = StatusMediaListener(chromecast, self.__handle_player_state, self.mass.event_loop)
+ chromecast.media_controller.register_status_listener(listenerMedia)
+ if chromecast.cast_type == 'group':
+ player.is_group = True
+ mz = MultizoneController(chromecast.uuid)
+ mz.register_listener(MZListener(mz, self.__handle_group_members_update, self.mass.event_loop))
+ chromecast.register_handler(mz)
+ chromecast.register_connection_listener(MZConnListener(mz))
+ self._chromecasts[player_id] = chromecast
+ self._players[player_id] = player
+ if not player_id in self._player_queue:
+ # TODO: persistant storage of player queue ?
+ self._player_queue[player_id] = []
+ chromecast.wait()
@run_periodic(600)
async def __discover_chromecasts(self):
''' discover chromecasts on the network '''
- LOGGER.info('Starting Chromecast discovery...')
- bg_task = run_background_task(self.mass.bg_executor, pychromecast.get_chromecasts)
- chromecasts = await asyncio.gather(bg_task)
- for chromecast in chromecasts[0]:
- player_id = str(chromecast.uuid)
- ip_change = False
- if player_id in self._chromecasts and chromecast.uri != self._chromecasts[player_id].uri:
- LOGGER.warning('Chromecast uri changed ?! - old: %s - new: %s' %(self._chromecasts[player_id].uri, chromecast.uri))
- ip_change = True
- if not player_id in self._players or ip_change:
- player = MusicPlayer()
- player.player_id = player_id
- player.name = chromecast.name
- player.player_provider = self.prov_id
- # patch the receive message method for handling queue status updates
- chromecast.queue = []
- chromecast.media_controller.queue_items = []
- chromecast.media_controller.queue_cur_id = None
- chromecast.media_controller.receive_message = types.MethodType(receive_message, chromecast.media_controller)
- listenerCast = StatusListener(chromecast, self.__handle_player_state, self.mass.event_loop)
- chromecast.register_status_listener(listenerCast)
- listenerMedia = StatusMediaListener(chromecast, self.__handle_player_state, self.mass.event_loop)
- chromecast.media_controller.register_status_listener(listenerMedia)
- if chromecast.cast_type == 'group':
- player.is_group = True
- mz = MultizoneController(chromecast.uuid)
- mz.register_listener(MZListener(mz, self.__handle_group_members_update, self.mass.event_loop))
- chromecast.register_handler(mz)
- chromecast.register_connection_listener(MZConnListener(mz))
- self._chromecasts[player_id] = chromecast
- self._players[player_id] = player
- if not player_id in self._player_queue:
- # TODO: persistant storage of player queue ?
- self._player_queue[player_id] = []
- chromecast.wait()
- LOGGER.info('Chromecast discovery done...')
+ LOGGER.info('Running Chromecast discovery...')
+ def callback(chromecast):
+ self.mass.event_loop.create_task(self.__chromecast_discovered(chromecast))
+
+ stop_discovery = pychromecast.get_chromecasts(blocking=False, callback=callback)
+ await asyncio.sleep(10)
+ stop_discovery()
+ LOGGER.info('Finished Chromecast discovery...')
def chunks(l, n):
"""Yield successive n-sized chunks from l."""