Пример #1
0
    def __init__(
        self,
        loop,
        session_manager: ClientSessionManager,
        config: conf.AppleTV,
        airplay: Stream,
    ) -> None:
        """Initialize a new Apple TV."""
        super().__init__()
        self._session_manager = session_manager
        self._config = config
        self._dmap_service = config.get_service(Protocol.DMAP)
        assert self._dmap_service is not None
        daap_http = net.HttpSession(
            session_manager.session,
            f"http://{config.address}:{self._dmap_service.port}/",
        )
        self._requester = DaapRequester(daap_http,
                                        self._dmap_service.credentials)

        self._apple_tv = BaseDmapAppleTV(self._requester)
        self._dmap_remote = DmapRemoteControl(self._apple_tv)
        self._dmap_metadata = DmapMetadata(config.identifier, self._apple_tv)
        self._dmap_power = DmapPower()
        self._dmap_push_updater = DmapPushUpdater(loop, self._apple_tv, self)
        self._dmap_features = DmapFeatures(config, self._apple_tv)
        self._airplay = airplay
Пример #2
0
    def __init__(
        self,
        loop: asyncio.AbstractEventLoop,
        session_manager: ClientSessionManager,
        config: conf.AppleTV,
        airplay: Stream,
    ) -> None:
        """Initialize a new Apple TV."""
        super().__init__()

        self._session_manager = session_manager
        self._config = config
        self._mrp_service = config.get_service(Protocol.MRP)
        assert self._mrp_service is not None

        self._connection = MrpConnection(config.address,
                                         self._mrp_service.port,
                                         loop,
                                         atv=self)
        self._srp = SRPAuthHandler()
        self._protocol = MrpProtocol(self._connection, self._srp,
                                     self._mrp_service)
        self._psm = PlayerStateManager(self._protocol, loop)

        self._mrp_remote = MrpRemoteControl(loop, self._psm, self._protocol)
        self._mrp_metadata = MrpMetadata(self._protocol, self._psm,
                                         config.identifier)
        self._mrp_power = MrpPower(loop, self._protocol, self._mrp_remote)
        self._mrp_push_updater = MrpPushUpdater(loop, self._mrp_metadata,
                                                self._psm)
        self._mrp_features = MrpFeatures(self._config, self._psm)
        self._airplay = airplay
Пример #3
0
def setup(
    loop: asyncio.AbstractEventLoop,
    config: conf.AppleTV,
    interfaces: Dict[Any, Relayer],
    device_listener: StateProducer,
    session_manager: ClientSessionManager,
) -> Optional[Tuple[Callable[[], Awaitable[None]], Callable[[], None],
                    Set[FeatureName]]]:
    """Set up a new AirPlay service."""
    service = config.get_service(Protocol.AirPlay)
    assert service is not None

    # TODO: Split up in connect/protocol and Stream implementation
    stream = AirPlayStream(config)

    interfaces[Features].register(
        AirPlayFeatures(cast(conf.AirPlayService, service)), Protocol.AirPlay)
    interfaces[Stream].register(stream, Protocol.AirPlay)

    async def _connect() -> None:
        pass

    def _close() -> None:
        stream.close()

    return _connect, _close, set([FeatureName.PlayUrl])
Пример #4
0
class PairFunctionalTest(AioHTTPTestCase):
    def setUp(self):
        AioHTTPTestCase.setUp(self)
        self.pairing = None

        self.service = AirPlayService('airplay_id', port=self.server.port)
        self.conf = AppleTV('127.0.0.1', 'Apple TV')
        self.conf.add_service(self.service)

    async def tearDownAsync(self):
        await self.pairing.close()
        await super().tearDownAsync()

    async def get_application(self, loop=None):
        self.fake_atv = FakeAirPlayDevice(self)
        self.usecase = AirPlayUseCases(self.fake_atv)
        return self.fake_atv.app

    async def do_pairing(self, pin=DEVICE_PIN):
        self.usecase.airplay_require_authentication()

        self.pairing = await pair(self.conf, Protocol.AirPlay, self.loop)

        self.assertTrue(self.pairing.device_provides_pin)

        await self.pairing.begin()
        if pin:
            self.pairing.pin(pin)

        self.assertFalse(self.pairing.has_paired)

        await self.pairing.finish()
        self.assertTrue(self.pairing.has_paired)
        self.assertEqual(self.service.credentials, DEVICE_CREDENTIALS)

    @unittest_run_loop
    async def test_pairing_exception_invalid_pin(self):
        with self.assertRaises(exceptions.PairingError):
            await self.do_pairing(9999)

    @unittest_run_loop
    async def test_pairing_exception_no_pin(self):
        with self.assertRaises(exceptions.PairingError):
            await self.do_pairing(None)

    @unittest_run_loop
    @patch('os.urandom')
    async def test_pairing_with_device_new_credentials(self, rand_func):
        rand_func.side_effect = predetermined_key
        await self.do_pairing()

    @unittest_run_loop
    async def test_pairing_with_device_existing_credentials(self):
        self.conf.get_service(
            Protocol.AirPlay).credentials = DEVICE_CREDENTIALS
        await self.do_pairing()
Пример #5
0
 def __init__(
     self, config: conf.AppleTV, session_manager: ClientSessionManager, _
 ) -> None:
     """Initialize a new MrpPairingHandler."""
     super().__init__(session_manager, config.get_service(Protocol.AirPlay))
     self.http: Optional[HttpConnection] = None
     self.address: str = str(config.address)
     self.pairing_procedure: Optional[AirPlayPairingProcedure] = None
     self.credentials: LegacyCredentials = self._setup_credentials()
     self.pin_code: Optional[str] = None
     self._has_paired: bool = False
Пример #6
0
def setup(  # pylint: disable=too-many-locals
    loop: asyncio.AbstractEventLoop,
    config: conf.AppleTV,
    interfaces: Dict[Any, Relayer],
    device_listener: StateProducer,
    session_manager: ClientSessionManager,
) -> Optional[
    Tuple[Callable[[], Awaitable[None]], Callable[[], None], Set[FeatureName]]
]:
    """Set up a new MRP service."""
    service = config.get_service(Protocol.MRP)
    assert service is not None

    connection = MrpConnection(config.address, service.port, loop, atv=device_listener)
    protocol = MrpProtocol(connection, SRPAuthHandler(), service)
    psm = PlayerStateManager(protocol)

    remote_control = MrpRemoteControl(loop, psm, protocol)
    metadata = MrpMetadata(protocol, psm, config.identifier)
    push_updater = MrpPushUpdater(loop, metadata, psm)
    power = MrpPower(loop, protocol, remote_control)

    interfaces[RemoteControl].register(remote_control, Protocol.MRP)
    interfaces[Metadata].register(metadata, Protocol.MRP)
    interfaces[Power].register(power, Protocol.MRP)
    interfaces[PushUpdater].register(push_updater, Protocol.MRP)
    interfaces[Features].register(MrpFeatures(config, psm), Protocol.MRP)

    # Forward power events to the facade instance
    power.listener = interfaces[Power]

    async def _connect() -> None:
        await protocol.start()

    def _close() -> None:
        push_updater.stop()
        protocol.stop()

    # Features managed by this protocol
    features = set(
        [
            FeatureName.Artwork,
            FeatureName.VolumeDown,
            FeatureName.VolumeUp,
            FeatureName.App,
        ]
    )
    features.update(_FEATURES_SUPPORTED)
    features.update(_FEATURE_COMMAND_MAP.keys())
    features.update(_FIELD_FEATURES.keys())

    return _connect, _close, features
Пример #7
0
def setup(
    loop: asyncio.AbstractEventLoop,
    config: conf.AppleTV,
    interfaces: Dict[Any, Relayer],
    device_listener: StateProducer,
    session_manager: ClientSessionManager,
) -> Optional[
    Tuple[Callable[[], Awaitable[None]], Callable[[], None], Set[FeatureName]]
]:
    """Set up a new DMAP service."""
    service = config.get_service(Protocol.DMAP)
    assert service is not None

    daap_http = HttpSession(
        session_manager.session,
        f"http://{config.address}:{service.port}/",
    )
    requester = DaapRequester(daap_http, service.credentials)
    apple_tv = BaseDmapAppleTV(requester)
    push_updater = DmapPushUpdater(loop, apple_tv, device_listener)
    metadata = DmapMetadata(config.identifier, apple_tv)

    interfaces[RemoteControl].register(DmapRemoteControl(apple_tv), Protocol.DMAP)
    interfaces[Metadata].register(metadata, Protocol.DMAP)
    interfaces[Power].register(DmapPower(), Protocol.DMAP)
    interfaces[PushUpdater].register(push_updater, Protocol.DMAP)
    interfaces[Features].register(DmapFeatures(config, apple_tv), Protocol.DMAP)

    async def _connect() -> None:
        await requester.login()

    def _close() -> None:
        push_updater.stop()
        device_listener.listener.connection_closed()

    # Features managed by this protocol
    features = set([FeatureName.VolumeDown, FeatureName.VolumeUp])
    features.update(_AVAILABLE_FEATURES)
    features.update(_UNKNOWN_FEATURES)
    features.update(_FIELD_FEATURES.keys())

    return _connect, _close, features
Пример #8
0
async def pair(config: conf.AppleTV,
               protocol: Protocol,
               loop: asyncio.AbstractEventLoop,
               session: aiohttp.ClientSession = None,
               **kwargs):
    """Pair a protocol for an Apple TV."""
    service = config.get_service(protocol)
    if not service:
        raise exceptions.NoServiceError(f"no service available for {protocol}")

    handler = {
        Protocol.DMAP: DmapPairingHandler,
        Protocol.MRP: MrpPairingHandler,
        Protocol.AirPlay: AirPlayPairingHandler,
        Protocol.Companion: CompanionPairingHandler,
    }.get(protocol)

    if handler is None:
        raise RuntimeError(f"missing implementation for {protocol}")

    return handler(config, await http.create_session(session), loop, **kwargs)
Пример #9
0
async def pair(config: conf.AppleTV,
               protocol: Protocol,
               loop: asyncio.AbstractEventLoop,
               session: aiohttp.ClientSession = None,
               **kwargs):
    """Pair a protocol for an Apple TV."""
    service = config.get_service(protocol)
    if not service:
        raise exceptions.NoServiceError("no service available for protocol " +
                                        str(protocol))

    handler = {
        Protocol.DMAP: DmapPairingHandler,
        Protocol.MRP: MrpPairingHandler,
        Protocol.AirPlay: AirPlayPairingHandler,
    }.get(protocol)

    if handler is None:
        raise exceptions.UnsupportedProtocolError(str(protocol))

    return handler(config, await net.create_session(session), loop, **kwargs)
Пример #10
0
def setup(
    loop: asyncio.AbstractEventLoop,
    config: conf.AppleTV,
    interfaces: Dict[Any, Relayer],
    device_listener: StateProducer,
    session_manager: ClientSessionManager,
) -> Optional[Tuple[Callable[[], Awaitable[None]], Callable[[], None],
                    Set[FeatureName]]]:
    """Set up a new Companion service."""
    service = config.get_service(Protocol.Companion)
    assert service is not None

    # Companion doesn't work without credentials, so don't setup if none exists
    if not service.credentials:
        return None

    api = CompanionAPI(config, loop)

    interfaces[Apps].register(CompanionApps(api), Protocol.Companion)
    interfaces[Features].register(
        CompanionFeatures(cast(conf.CompanionService, service)),
        Protocol.Companion)
    interfaces[Power].register(CompanionPower(api), Protocol.Companion)

    async def _connect() -> None:
        pass

    def _close() -> None:
        pass

    return (
        _connect,
        _close,
        set([
            FeatureName.AppList,
            FeatureName.LaunchApp,
            FeatureName.TurnOn,
            FeatureName.TurnOff,
        ]),
    )
Пример #11
0
def setup(
    loop: asyncio.AbstractEventLoop,
    config: conf.AppleTV,
    interfaces: Dict[Any, Relayer],
    device_listener: StateProducer,
    session_manager: ClientSessionManager,
) -> Optional[Tuple[Callable[[], Awaitable[None]], Callable[[], None],
                    Set[FeatureName]]]:
    """Set up a new RAOP service."""
    service = config.get_service(Protocol.RAOP)
    assert service is not None

    state_manager = RaopStateManager()
    metadata = RaopMetadata(state_manager)
    push_updater = RaopPushUpdater(metadata, loop)

    class RaopStateListener(RaopListener):
        """Listener for RAOP state changes."""
        def playing(self, metadata: AudioMetadata) -> None:
            """Media started playing with metadata."""
            state_manager.metadata = metadata
            self._trigger()

        def stopped(self) -> None:
            """Media stopped playing."""
            state_manager.metadata = None
            self._trigger()

        @staticmethod
        def _trigger():
            """Trigger push update."""
            if push_updater.active:
                asyncio.ensure_future(push_updater.state_updated(), loop=loop)

    service = cast(conf.RaopService, service)

    raop_listener = RaopStateListener()

    interfaces[Stream].register(
        RaopStream(str(config.address), service, raop_listener, loop),
        Protocol.RAOP)
    interfaces[Features].register(RaopFeatures(state_manager), Protocol.RAOP)
    interfaces[PushUpdater].register(push_updater, Protocol.RAOP)
    interfaces[Metadata].register(metadata, Protocol.RAOP)

    async def _connect() -> None:
        pass

    def _close() -> None:
        pass

    return (
        _connect,
        _close,
        set([
            FeatureName.StreamFile,
            FeatureName.PushUpdates,
            FeatureName.Artist,
            FeatureName.Album,
            FeatureName.Title,
        ]),
    )