Example #1
0
async def test_content_item_update_calls_active_listener(psm, protocol_mock, listener):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    await protocol_mock.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_mock.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_mock.inject(msg)

    assert listener.call_count == 3

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

    assert listener.call_count == 4

    await protocol_mock.inject(update_item)

    assert listener.call_count == 5
Example #2
0
    def volume_control(self, available):
        msg = messages.create(protobuf.VOLUME_CONTROL_AVAILABILITY_MESSAGE)
        msg.inner().volumeControlAvailable = available
        self._send(msg)

        msg = messages.create(
            protobuf.VOLUME_CONTROL_CAPABILITIES_DID_CHANGE_MESSAGE)
        msg.inner().capabilities.volumeControlAvailable = available
        msg.inner().outputDeviceUID = DEVICE_UID
        self._send(msg)
Example #3
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/protocols/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))

                # Special cases for some buttons
                if pressed_key == "volumeup" and not math.isclose(
                        self.state.volume, 100.0):
                    self.state.set_volume(
                        min(self.state.volume + VOLUME_STEP, 100.0),
                        DEVICE_UID)
                elif pressed_key == "volumedown" and not math.isclose(
                        self.state.volume, 0.0):
                    self.state.set_volume(
                        max(self.state.volume - VOLUME_STEP, 0.0), DEVICE_UID)
            else:
                _LOGGER.error("Missing key down for %d,%d", use_page, usage)
        else:
            _LOGGER.error("Invalid key press state: %d", down_press)
Example #4
0
async def test_set_now_playing_player_for_active_client(psm, protocol_mock, listener):
    msg = messages.create(pb.SET_NOW_PLAYING_CLIENT_MESSAGE)
    client = msg.inner().client
    client.bundleIdentifier = CLIENT_ID_1
    await protocol_mock.inject(msg)

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

    assert listener.call_count == 2

    assert psm.playing.identifier == PLAYER_ID_1
    assert psm.playing.display_name == PLAYER_NAME_1
Example #5
0
async def test_content_item_update(psm, protocol_mock):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    msg = add_metadata_item(msg, identifier="id", title="item", playCount=123)
    await protocol_mock.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_mock.inject(msg)

    player = psm.get_player(msg.inner().playerPath)
    assert player.metadata_field("title") == "new title"
    assert player.metadata_field("playCount") == 1111
Example #6
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)
Example #7
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)
Example #8
0
async def test_metadata_single_item(psm, protocol_mock):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    msg = add_metadata_item(msg, title="item")
    await protocol_mock.inject(msg)

    player = psm.get_player(msg.inner().playerPath)
    assert player.metadata.title == "item"
Example #9
0
async def test_get_metadata_field(psm, protocol_mock):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    msg = add_metadata_item(msg, title="item", playCount=123)
    await protocol_mock.inject(msg)

    player = psm.get_player(msg.inner().playerPath)
    assert player.metadata_field("title") == "item"
    assert player.metadata_field("playCount") == 123
Example #10
0
async def test_playback_state_playing_with_zero_playbac_rate(psm, protocol_mock):
    set_state = set_path(messages.create(pb.SET_STATE_MESSAGE))
    set_state.inner().playbackState = pb.PlaybackState.Playing
    msg = add_metadata_item(set_state, playbackRate=0.0)
    await protocol_mock.inject(msg)

    player = psm.get_player(msg.inner().playerPath)
    assert player.playback_state == pb.PlaybackState.Playing
Example #11
0
async def test_set_now_playing_player_when_no_client(psm, protocol_mock, listener):
    msg = set_path(messages.create(pb.SET_NOW_PLAYING_PLAYER_MESSAGE))
    await protocol_mock.inject(msg)

    assert listener.call_count == 0

    assert not psm.playing.identifier
    assert not psm.playing.display_name
Example #12
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))
Example #13
0
async def test_default_player_when_only_client_set(psm, protocol_mock, listener):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    await protocol_mock.inject(msg)
    msg = set_path(
        messages.create(pb.SET_STATE_MESSAGE),
        player_id=DEFAULT_PLAYER,
        player_name="Default Name",
    )
    await protocol_mock.inject(msg)

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

    assert psm.playing.identifier == DEFAULT_PLAYER
    assert psm.playing.display_name == "Default Name"
Example #14
0
async def test_update_client(psm, protocol_mock, listener):
    msg = messages.create(pb.SET_NOW_PLAYING_CLIENT_MESSAGE)
    client = msg.inner().client
    client.bundleIdentifier = CLIENT_ID_1
    await protocol_mock.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_mock.inject(update)

    assert listener.call_count == 2
    assert psm.client.display_name == CLIENT_NAME_1
Example #15
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)
Example #16
0
async def test_get_command_info(psm, protocol_mock):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    info = msg.inner().supportedCommands.supportedCommands.add()
    info.command = pb.CommandInfo_pb2.Pause
    await protocol_mock.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
Example #17
0
async def test_set_now_playing_client(psm, protocol_mock, listener):
    msg = messages.create(pb.SET_NOW_PLAYING_CLIENT_MESSAGE)
    client = msg.inner().client
    client.bundleIdentifier = CLIENT_ID_1
    await protocol_mock.inject(msg)

    assert listener.call_count == 1

    assert psm.client.bundle_identifier == CLIENT_ID_1
Example #18
0
async def test_remove_active_player(psm, protocol_mock, listener):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    await protocol_mock.inject(msg)

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

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

    assert psm.playing.identifier == PLAYER_ID_1

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

    assert listener.call_count == 4
    assert not psm.playing.is_valid
Example #19
0
async def test_remove_not_active_client(psm, protocol_mock, listener):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    await protocol_mock.inject(msg)

    msg = messages.create(pb.SET_NOW_PLAYING_CLIENT_MESSAGE)
    client = msg.inner().client
    client.bundleIdentifier = CLIENT_ID_1
    await protocol_mock.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_mock.inject(remove)

    assert listener.call_count == 2
    assert psm.client.bundle_identifier == CLIENT_ID_1
Example #20
0
    def set_active_player(self, identifier):
        if identifier is not None and identifier not in self.states:
            raise Exception(f"invalid player: {identifier}")

        self.active_player = identifier
        now_playing = messages.create(protobuf.SET_NOW_PLAYING_CLIENT_MESSAGE)
        client = now_playing.inner().client
        if identifier:
            client.bundleIdentifier = identifier
        self._send(now_playing)
Example #21
0
    def set_volume(self, volume, device_uid):
        if 0 <= volume <= 1:
            self.volume = volume

            msg = messages.create(protobuf.VOLUME_DID_CHANGE_MESSAGE)
            msg.inner().outputDeviceUID = device_uid
            msg.inner().volume = volume
            self._send(msg)
        else:
            _LOGGER.debug("Value %f out of range", volume)
Example #22
0
async def test_get_client_and_player(psm, protocol_mock):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    await protocol_mock.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
Example #23
0
    def handle_client_updates_config(self, message, inner):
        for identifier, metadata in self.state.states.items():
            self.send_to_client(_set_state_message(metadata, identifier))

        # Trigger sending of SetNowPlayingClientMessage
        if self.state.active_player:
            self.state.set_active_player(self.state.active_player)

        if message.identifier is not None:
            self.send_to_client(
                messages.create(0, identifier=message.identifier))
Example #24
0
async def test_set_state_calls_active_listener(psm, protocol_mock, listener):
    set_state = set_path(messages.create(pb.SET_STATE_MESSAGE))
    await protocol_mock.inject(set_state)

    assert listener.call_count == 1

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

    assert listener.call_count == 2

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

    assert listener.call_count == 3

    await protocol_mock.inject(set_state)

    assert listener.call_count == 4
Example #25
0
async def test_set_default_supported_commands(psm, protocol_mock, listener):
    msg = messages.create(pb.SET_DEFAULT_SUPPORTED_COMMANDS_MESSAGE)
    supported_commands = msg.inner().supportedCommands.supportedCommands
    command = supported_commands.add()
    command.command = pb.CommandInfo_pb2.Play
    msg.inner().playerPath.client.bundleIdentifier = CLIENT_ID_1
    await protocol_mock.inject(msg)

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

    # Default commands are set on client, so any player belonging to that client
    # should have the supported command
    player_path = pb.PlayerPath()
    player_path.client.bundleIdentifier = CLIENT_ID_1
    player_path.player.identifier = PLAYER_ID_1
    player = psm.get_player(player_path)

    assert player.command_info(pb.CommandInfo_pb2.Play)
    assert listener.call_count == 2
Example #26
0
async def test_audio_volume_did_change(protocol_mock, audio, device_uid,
                                       volume, expected_volume):
    await volume_controls_changed(protocol_mock, DEVICE_UID, True)
    assert audio.is_available

    assert math.isclose(audio.volume, 0.0)

    message = messages.create(protobuf.VOLUME_DID_CHANGE_MESSAGE)
    message.inner().outputDeviceUID = device_uid
    message.inner().volume = volume
    await protocol_mock.inject(message)

    assert math.isclose(audio.volume, expected_volume)
Example #27
0
async def test_metadata_item_identifier(psm, protocol_mock):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    msg = add_metadata_item(msg, identifier="id1", title="item1")
    await protocol_mock.inject(msg)

    player = psm.get_player(msg.inner().playerPath)
    assert player.item_identifier == "id1"

    msg = add_metadata_item(msg, location=1, identifier="id2", title="item2")
    await protocol_mock.inject(msg)

    player = psm.get_player(msg.inner().playerPath)
    assert player.item_identifier == "id2"
Example #28
0
async def test_playback_state_without_rate(psm, protocol_mock):
    msg = set_path(messages.create(pb.SET_STATE_MESSAGE))
    msg.inner().playbackState = pb.PlaybackState.Paused
    msg = add_metadata_item(msg)
    await protocol_mock.inject(msg)

    player = psm.get_player(msg.inner().playerPath)
    assert player.playback_state is pb.PlaybackState.Paused

    msg.inner().playbackState = pb.PlaybackState.Playing
    await protocol_mock.inject(msg)

    player = psm.get_player(msg.inner().playerPath)
    assert player.playback_state is pb.PlaybackState.Playing
Example #29
0
    async def _do_press(keycode: Tuple[int, int], hold: bool):
        await protocol.send(
            messages.send_hid_event(keycode[0], keycode[1], True))

        if hold:
            # Hardcoded hold time for one second
            await asyncio.sleep(1)

        await protocol.send(
            messages.send_hid_event(keycode[0], keycode[1], False))

        # Send and receive a generic message as some kind of "flush" mechanism
        if flush:
            await protocol.send_and_receive(
                messages.create(protobuf.GENERIC_MESSAGE))
Example #30
0
    def handle_playback_queue_request(self, message, inner):
        state = self.state.get_player_state(self.state.active_player)

        setstate = messages.create(protobuf.SET_STATE_MESSAGE,
                                   identifier=message.identifier)

        artwork_data = self.state.states[self.state.active_player].artwork
        if artwork_data:
            queue = setstate.inner().playbackQueue
            queue.location = 0
            item = queue.contentItems.add()
            item.artworkData = artwork_data
            item.artworkDataWidth = state.artwork_width or 456
            item.artworkDataHeight = state.artwork_height or 789
        self.send_to_client(setstate)