Exemplo n.º 1
0
async def test_content_item_update_calls_active_listener(
        psm, protocol, listener):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    await protocol.inject(msg)

    assert listener.call_count == 1

    update_item = set_path(messages.create(pb.UPDATE_CONTENT_ITEM_MESSAGE))
    item = update_item.inner().contentItems.add()
    await protocol.inject(update_item)

    assert listener.call_count == 2

    msg = messages.create(pb.SET_NOW_PLAYING_CLIENT_MESSAGE)
    client = msg.inner().client
    client.bundleIdentifier = CLIENT_ID_1
    await protocol.inject(msg)

    assert listener.call_count == 3

    now_playing = set_path(messages.create(pb.SET_NOW_PLAYING_PLAYER_MESSAGE))
    await protocol.inject(now_playing)

    assert listener.call_count == 4

    await protocol.inject(update_item)

    assert listener.call_count == 5
Exemplo n.º 2
0
    def handle_send_hid_event(self, message, inner):
        # These corresponds to the bytes mapping to pressed key (see
        # send_hid_event in pyatv/mrp/messages.py)
        start = inner.hidEventData[43:49]
        use_page, usage, down_press = struct.unpack(">HHH", start)

        if down_press == 1:
            self.state.outstanding_keypresses.add((use_page, usage))
            self.send(messages.create(0, identifier=message.identifier))
        elif down_press == 0:
            if (use_page, usage) in self.state.outstanding_keypresses:
                if (_convert_key_press(use_page, usage) == "select"
                        and self.state.last_button_pressed == "home"):
                    self.state.powered_on = False
                    self._send_device_info(update=True)
                self.state.last_button_pressed = _convert_key_press(
                    use_page, usage)
                self.state.outstanding_keypresses.remove((use_page, usage))
                _LOGGER.debug("Pressed button: %s",
                              self.state.last_button_pressed)
                self.send(messages.create(0, identifier=message.identifier))
            else:
                _LOGGER.error("Missing key down for %d,%d", use_page, usage)
        else:
            _LOGGER.error("Invalid key press state: %d", down_press)
Exemplo n.º 3
0
    def handle_send_hid_event(self, message, inner):
        # These corresponds to the bytes mapping to pressed key (see
        # send_hid_event in pyatv/mrp/messages.py)
        start = inner.hidEventData[43:49]
        use_page, usage, down_press = struct.unpack('>HHH', start)

        if down_press == 1:
            self.outstanding_keypresses.add((use_page, usage))
            self.send(messages.create(0, identifier=message.identifier))
        elif down_press == 0:
            if (use_page, usage) in self.outstanding_keypresses:
                if (
                    _convert_key_press(use_page, usage) == 'select'
                    and self.last_button_pressed == 'home'
                   ):
                    msg = messages.device_information(
                        'pyatv', message.identifier, 0)
                    self.send(msg)
                self.last_button_pressed = _convert_key_press(use_page, usage)
                self.outstanding_keypresses.remove((use_page, usage))
                _LOGGER.debug('Pressed button: %s', self.last_button_pressed)
                self.send(messages.create(0, identifier=message.identifier))
            else:
                _LOGGER.error('Missing key down for %d,%d', use_page, usage)
        else:
            _LOGGER.error('Invalid key press state: %d', down_press)
Exemplo n.º 4
0
async def test_set_now_playing_player_for_active_client(
        psm, protocol, listener):
    msg = messages.create(pb.SET_NOW_PLAYING_CLIENT_MESSAGE)
    client = msg.inner().client
    client.bundleIdentifier = CLIENT_ID_1
    await protocol.inject(msg)

    msg = set_path(messages.create(pb.SET_NOW_PLAYING_PLAYER_MESSAGE))
    await protocol.inject(msg)

    assert listener.call_count == 2

    assert psm.playing.identifier == PLAYER_ID_1
    assert psm.playing.display_name == PLAYER_NAME_1
Exemplo n.º 5
0
async def test_set_state_calls_active_listener(psm, protocol, listener):
    set_state = set_path(messages.create(pb.SET_STATE_MESSAGE))
    await protocol.inject(set_state)

    assert listener.call_count == 1

    now_playing = set_path(messages.create(pb.SET_NOW_PLAYING_PLAYER_MESSAGE))
    await protocol.inject(now_playing)

    assert listener.call_count == 2

    await protocol.inject(set_state)

    assert listener.call_count == 3
Exemplo n.º 6
0
async def test_remove_active_player(psm, protocol, listener):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    await protocol.inject(msg)

    msg = set_path(messages.create(pb.SET_NOW_PLAYING_PLAYER_MESSAGE))
    await protocol.inject(msg)

    assert psm.playing.identifier == PLAYER_ID_1

    remove = set_path(messages.create(pb.REMOVE_PLAYER_MESSAGE))
    await protocol.inject(remove)

    assert listener.call_count == 3
    assert not psm.playing.is_valid
Exemplo n.º 7
0
async def test_content_item_update(psm, protocol):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    msg = add_metadata_item(msg, identifier="id", title="item", playCount=123)
    await protocol.inject(msg)

    msg = set_path(messages.create(pb.UPDATE_CONTENT_ITEM_MESSAGE))
    item = msg.inner().contentItems.add()
    item.identifier = "id"
    item.metadata.title = "new title"
    item.metadata.playCount = 1111
    await protocol.inject(msg)

    player = psm.get_player(msg.inner().playerPath)
    assert player.metadata_field("title") == "new title"
    assert player.metadata_field("playCount") == 1111
Exemplo n.º 8
0
async def test_metadata_single_item(psm, protocol):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    msg = add_metadata_item(msg, title="item")
    await protocol.inject(msg)

    player = psm.get_player(msg.inner().playerPath)
    assert player.metadata.title == "item"
Exemplo n.º 9
0
def _set_state_message(metadata, identifier):
    # Most things are hardcoded here for simplicity. Will change that
    # as time goes by and more dynamic content is needed.
    set_state = messages.create(protobuf.SET_STATE_MESSAGE)
    inner = set_state.inner()
    inner.playbackState = metadata.playback_state
    inner.displayName = "Fake Player"

    for command in metadata.supported_commands:
        item = inner.supportedCommands.supportedCommands.add()
        item.command = command
        item.enabled = True

    if metadata.repeat and metadata.repeat != const.RepeatState.Off:
        cmd = inner.supportedCommands.supportedCommands.add()
        cmd.command = protobuf.CommandInfo_pb2.ChangeRepeatMode
        cmd.repeatMode = _REPEAT_LOOKUP[metadata.repeat]

    if metadata.shuffle:
        cmd = inner.supportedCommands.supportedCommands.add()
        cmd.command = protobuf.CommandInfo_pb2.ChangeShuffleMode
        cmd.shuffleMode = metadata.shuffle

    queue = inner.playbackQueue
    queue.location = 0
    _fill_item(queue.contentItems.add(), metadata)

    client = inner.playerPath.client
    client.processIdentifier = 123
    client.bundleIdentifier = identifier
    return set_state
Exemplo n.º 10
0
async def heartbeat_loop(protocol):
    """Periodically send heartbeat messages to device."""
    _LOGGER.debug("Starting heartbeat loop")
    count = 0
    attempts = 0
    message = messages.create(protobuf.GENERIC_MESSAGE)
    while True:
        try:
            # Re-attempts are made with no initial delay to more quickly
            # recover a failed heartbeat (if possible)
            if attempts == 0:
                await asyncio.sleep(HEARTBEAT_INTERVAL)

            _LOGGER.debug("Sending periodic heartbeat %d", count)
            await protocol.send_and_receive(message)
            _LOGGER.debug("Got heartbeat %d", count)
        except asyncio.CancelledError:
            break
        except Exception:
            attempts += 1
            if attempts > HEARTBEAT_RETRIES:
                _LOGGER.error("heartbeat %d failed after %d tries", count,
                              attempts)
                protocol.connection.close()
                break
            _LOGGER.debug("heartbeat %d failed", count)
        else:
            attempts = 0
        finally:
            count += 1

    _LOGGER.debug("Stopping heartbeat loop at %d", count)
Exemplo n.º 11
0
 def update_client(self, display_name, identifier):
     msg = messages.create(protobuf.UPDATE_CLIENT_MESSAGE)
     client = msg.inner().client
     client.bundleIdentifier = identifier
     if display_name is not None:
         client.displayName = display_name
     self._send(msg)
Exemplo n.º 12
0
    def set_active_player(self, identifier):
        if identifier not in self.states:
            raise Exception('invalid player: %s', identifier)

        now_playing = messages.create(protobuf.SET_NOW_PLAYING_CLIENT_MESSAGE)
        client = now_playing.inner().client
        client.bundleIdentifier = identifier
        self._send(now_playing)
Exemplo n.º 13
0
async def test_get_metadata_field(psm, protocol):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    msg = add_metadata_item(msg, title="item", playCount=123)
    await protocol.inject(msg)

    player = psm.get_player(msg.inner().playerPath)
    assert player.metadata_field("title") == "item"
    assert player.metadata_field("playCount") == 123
Exemplo n.º 14
0
async def test_set_now_playing_player(psm, protocol, listener):
    msg = set_path(messages.create(pb.SET_NOW_PLAYING_PLAYER_MESSAGE))
    await protocol.inject(msg)

    assert listener.call_count == 1

    assert psm.playing.identifier == PLAYER_ID_1
    assert psm.playing.display_name == PLAYER_NAME_1
Exemplo n.º 15
0
 def handle_generic(self, message, inner):
     # Generic message is used by pyatv for heartbeats
     self.state.heartbeat_count += 1
     _LOGGER.debug("Received heartbeat (total count: %d)",
                   self.state.heartbeat_count)
     self.send_to_client(
         messages.create(protobuf.ProtocolMessage.UNKNOWN_MESSAGE,
                         identifier=message.identifier))
Exemplo n.º 16
0
    def handle_get_keyboard_session_message(self, message):
        _LOGGER.debug('Get keyboard session')

        # This message has a lot more fields, but pyatv currently
        # not use them so ignore for now
        resp = messages.create(protobuf.KEYBOARD_MESSAGE)
        resp.identifier = message.identifier
        self._send(resp)
Exemplo n.º 17
0
async def test_update_client(psm, protocol, listener):
    msg = messages.create(pb.SET_NOW_PLAYING_CLIENT_MESSAGE)
    client = msg.inner().client
    client.bundleIdentifier = CLIENT_ID_1
    await protocol.inject(msg)

    assert listener.call_count == 1
    assert psm.client.display_name is None

    update = messages.create(pb.UPDATE_CLIENT_MESSAGE)
    client = update.inner().client
    client.bundleIdentifier = CLIENT_ID_1
    client.displayName = CLIENT_NAME_1
    await protocol.inject(update)

    assert listener.call_count == 2
    assert psm.client.display_name == CLIENT_NAME_1
Exemplo n.º 18
0
async def test_default_player_when_only_client_set(psm, protocol, listener):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    await protocol.inject(msg)
    msg = set_path(
        messages.create(pb.SET_STATE_MESSAGE),
        player_id="MediaRemote-DefaultPlayer",
        player_name="Default Name",
    )
    await protocol.inject(msg)

    msg = messages.create(pb.SET_NOW_PLAYING_CLIENT_MESSAGE)
    client = msg.inner().client
    client.bundleIdentifier = CLIENT_ID_1
    await protocol.inject(msg)

    assert psm.playing.identifier == "MediaRemote-DefaultPlayer"
    assert psm.playing.display_name == "Default Name"
Exemplo n.º 19
0
async def test_set_now_playing_player_when_no_client(psm, protocol, listener):
    msg = set_path(messages.create(pb.SET_NOW_PLAYING_PLAYER_MESSAGE))
    await protocol.inject(msg)

    assert listener.call_count == 0

    assert not psm.playing.identifier
    assert not psm.playing.display_name
Exemplo n.º 20
0
async def test_playback_state_seeking(psm, protocol):
    set_state = set_path(messages.create(pb.SET_STATE_MESSAGE))
    set_state.inner().playbackState = pb.PlaybackState.Playing
    msg = add_metadata_item(set_state, playbackRate=2.0)
    await protocol.inject(msg)

    player = psm.get_player(msg.inner().playerPath)
    assert player.playback_state == pb.PlaybackState.Seeking
Exemplo n.º 21
0
 def default_supported_commands(self, commands):
     msg = messages.create(protobuf.SET_DEFAULT_SUPPORTED_COMMANDS_MESSAGE)
     supported_commands = msg.inner().supportedCommands.supportedCommands
     for command in commands:
         item = supported_commands.add()
         item.command = command
         item.enabled = True
     msg.inner().playerPath.client.bundleIdentifier = PLAYER_IDENTIFIER
     self._send(msg)
Exemplo n.º 22
0
async def test_remove_client_if_belongs_to_active_player(
        psm, protocol, listener):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    await protocol.inject(msg)

    msg = set_path(messages.create(pb.SET_NOW_PLAYING_PLAYER_MESSAGE))
    await protocol.inject(msg)

    msg = messages.create(pb.SET_NOW_PLAYING_CLIENT_MESSAGE)
    client = msg.inner().client
    client.bundleIdentifier = CLIENT_ID_1
    await protocol.inject(msg)

    remove = set_path(messages.create(pb.REMOVE_PLAYER_MESSAGE))
    await protocol.inject(remove)

    assert psm.client is None
    assert listener.call_count == 4
Exemplo n.º 23
0
async def test_set_now_playing_client(psm, protocol, listener):
    msg = messages.create(pb.SET_NOW_PLAYING_CLIENT_MESSAGE)
    client = msg.inner().client
    client.bundleIdentifier = CLIENT_ID_1
    await protocol.inject(msg)

    assert listener.call_count == 1

    assert psm.client.bundle_identifier == CLIENT_ID_1
Exemplo n.º 24
0
async def test_get_command_info(psm, protocol):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    info = msg.inner().supportedCommands.supportedCommands.add()
    info.command = pb.CommandInfo_pb2.Pause
    await protocol.inject(msg)

    player = psm.get_player(msg.inner().playerPath)
    assert player.command_info(pb.CommandInfo_pb2.Play) is None
    assert player.command_info(pb.CommandInfo_pb2.Pause) is not None
Exemplo n.º 25
0
 def handle_playback_queue_request(self, message, inner):
     setstate = messages.create(protobuf.SET_STATE_MESSAGE,
                                identifier=message.identifier)
     queue = setstate.inner().playbackQueue
     queue.location = 0
     item = queue.contentItems.add()
     item.artworkData = self.state.states[self.state.active_player].artwork
     item.artworkDataWidth = 456
     item.artworkDataHeight = 789
     self.send(setstate)
Exemplo n.º 26
0
async def test_remove_not_active_client(psm, protocol, listener):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    await protocol.inject(msg)

    msg = messages.create(pb.SET_NOW_PLAYING_CLIENT_MESSAGE)
    client = msg.inner().client
    client.bundleIdentifier = CLIENT_ID_1
    await protocol.inject(msg)

    assert listener.call_count == 2
    assert psm.client.bundle_identifier == CLIENT_ID_1

    remove = messages.create(pb.REMOVE_CLIENT_MESSAGE)
    client = remove.inner().client
    client.bundleIdentifier = CLIENT_ID_2
    await protocol.inject(remove)

    assert listener.call_count == 2
    assert psm.client.bundle_identifier == CLIENT_ID_1
Exemplo n.º 27
0
async def test_get_client_and_player(psm, protocol):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    await protocol.inject(msg)

    player = psm.get_player(msg.inner().playerPath)
    assert player.identifier == PLAYER_ID_1
    assert player.display_name == PLAYER_NAME_1

    client = psm.get_client(msg.inner().playerPath.client)
    assert client.bundle_identifier == CLIENT_ID_1
    assert client.display_name == CLIENT_NAME_1
Exemplo n.º 28
0
    def item_update(self, metadata, identifier):
        msg = messages.create(protobuf.UPDATE_CONTENT_ITEM_MESSAGE)
        inner = msg.inner()

        _fill_item(inner.contentItems.add(), metadata)

        client = inner.playerPath.client
        client.processIdentifier = 123
        client.bundleIdentifier = identifier

        self.send(msg)
Exemplo n.º 29
0
    def handle_send_hid_event(self, message, inner):
        outstanding = self.state.outstanding_keypresses

        # These corresponds to the bytes mapping to pressed key (see
        # send_hid_event in pyatv/mrp/messages.py)
        start = inner.hidEventData[43:49]
        use_page, usage, down_press = struct.unpack(">HHH", start)

        if down_press == 1:
            outstanding[(use_page, usage)] = stub_sleep()
            self.send_to_client(
                messages.create(0, identifier=message.identifier))
        elif down_press == 0:
            if (use_page, usage) in outstanding:
                pressed_key = _KEY_LOOKUP.get((use_page, usage))
                if not pressed_key:
                    raise Exception(
                        f"unsupported key: use_page={use_page}, usage={usage}")
                if pressed_key == "select" and self.state.last_button_pressed == "home":
                    self.state.powered_on = False
                    self._send_device_info(update=True)

                time_diff = stub_sleep() - outstanding[(use_page, usage)]
                if time_diff > 0.5:
                    self.state.last_button_action = const.InputAction.Hold
                elif self.state.last_button_pressed == pressed_key:
                    # NB: Will report double tap for >= 3 clicks (fix when needed)
                    self.state.last_button_action = const.InputAction.DoubleTap
                else:
                    self.state.last_button_action = const.InputAction.SingleTap

                self.state.last_button_pressed = pressed_key
                del outstanding[(use_page, usage)]
                _LOGGER.debug("Pressed button: %s",
                              self.state.last_button_pressed)
                self.send_to_client(
                    messages.create(0, identifier=message.identifier))
            else:
                _LOGGER.error("Missing key down for %d,%d", use_page, usage)
        else:
            _LOGGER.error("Invalid key press state: %d", down_press)