def test_parse_strings(self): in_data = tags.string_tag('stra', '') + \ tags.string_tag('strb', 'test string') parsed = parser.parse(in_data, lookup_tag) self.assertEqual(2, len(parsed)) self.assertEqual('', parser.first(parsed, 'stra')) self.assertEqual('test string', parser.first(parsed, 'strb'))
def test_parse_bool(self): in_data = tags.bool_tag('bola', True) + \ tags.bool_tag('bolb', False) parsed = parser.parse(in_data, lookup_tag) self.assertEqual(2, len(parsed)) self.assertTrue(parser.first(parsed, 'bola')) self.assertFalse(parser.first(parsed, 'bolb'))
def test_parse_strings(): in_data = tags.string_tag("stra", "") + tags.string_tag( "strb", "test string") parsed = parser.parse(in_data, lookup_tag) assert 2 == len(parsed) assert "" == parser.first(parsed, "stra") assert "test string" == parser.first(parsed, "strb")
def test_parse_strings(self): in_data = tags.string_tag("stra", "") + tags.string_tag( "strb", "test string") parsed = parser.parse(in_data, lookup_tag) self.assertEqual(2, len(parsed)) self.assertEqual("", parser.first(parsed, "stra")) self.assertEqual("test string", parser.first(parsed, "strb"))
def test_parse_value_in_container(self): in_data = tags.container_tag( "cona", tags.uint8_tag("uuu8", 36) + tags.uint16_tag("uu16", 13000)) parsed = parser.parse(in_data, lookup_tag) self.assertEqual(1, len(parsed)) inner = parser.first(parsed, "cona") self.assertEqual(2, len(inner)) self.assertEqual(36, parser.first(inner, "uuu8")) self.assertEqual(13000, parser.first(inner, "uu16"))
def test_parse_value_in_container(): in_data = tags.container_tag( "cona", tags.uint8_tag("uuu8", 36) + tags.uint16_tag("uu16", 13000)) parsed = parser.parse(in_data, lookup_tag) assert 1 == len(parsed) inner = parser.first(parsed, "cona") assert 2 == len(inner) assert 36 == parser.first(inner, "uuu8") assert 13000 == parser.first(inner, "uu16")
def test_parse_uint_of_various_lengths(): in_data = (tags.uint8_tag("uuu8", 12) + tags.uint16_tag("uu16", 37888) + tags.uint32_tag("uu32", 305419896) + tags.uint64_tag("uu64", 8982983289232)) parsed = parser.parse(in_data, lookup_tag) assert 4 == len(parsed) assert 12 == parser.first(parsed, "uuu8") assert 37888 == parser.first(parsed, "uu16") assert 305419896 == parser.first(parsed, "uu32") assert 8982983289232 == parser.first(parsed, "uu64")
def test_parse_value_in_container(self): in_data = tags.container_tag('cona', tags.uint8_tag('uuu8', 36) + tags.uint16_tag('uu16', 13000)) parsed = parser.parse(in_data, lookup_tag) self.assertEqual(1, len(parsed)) inner = parser.first(parsed, 'cona') self.assertEqual(2, len(inner)) self.assertEqual(36, parser.first(inner, 'uuu8')) self.assertEqual(13000, parser.first(inner, 'uu16'))
def test_parse_uint_of_various_lengths(self): in_data = (tags.uint8_tag("uuu8", 12) + tags.uint16_tag("uu16", 37888) + tags.uint32_tag("uu32", 305419896) + tags.uint64_tag("uu64", 8982983289232)) parsed = parser.parse(in_data, lookup_tag) self.assertEqual(4, len(parsed)) self.assertEqual(12, parser.first(parsed, "uuu8")) self.assertEqual(37888, parser.first(parsed, "uu16")) self.assertEqual(305419896, parser.first(parsed, "uu32")) self.assertEqual(8982983289232, parser.first(parsed, "uu64"))
async def test_succesful_pairing(self): await self._start() url = self._pairing_url(PAIRING_CODE) data, _ = await utils.simple_get(url, self.loop) # Verify content returned in pairingresponse parsed = parser.parse(data, tag_definitions.lookup_tag) self.assertEqual(parser.first(parsed, 'cmpa', 'cmpg'), 1) self.assertEqual(parser.first(parsed, 'cmpa', 'cmnm'), REMOTE_NAME) self.assertEqual(parser.first(parsed, 'cmpa', 'cmty'), 'iPhone')
def handle_set_parameter(self, request: HttpRequest) -> Optional[HttpResponse]: """Handle incoming SET_PARAMETER request.""" _LOGGER.debug("Received SET_PARAMETER: %s", request) if request.headers["Content-Type"] == "application/x-dmap-tagged": tags = parser.parse(request.body, lookup_tag) self.state.metadata.title = parser.first(tags, "mlit", "minm") self.state.metadata.artist = parser.first(tags, "mlit", "asar") self.state.metadata.album = parser.first(tags, "mlit", "asal") return HttpResponse("RTSP", "1.0", 200, "OK", {"CSeq": request.headers["CSeq"]}, b"")
def test_parse_uint_of_various_lengths(self): in_data = tags.uint8_tag('uuu8', 12) + \ tags.uint16_tag('uu16', 37888) + \ tags.uint32_tag('uu32', 305419896) + \ tags.uint64_tag('uu64', 8982983289232) parsed = parser.parse(in_data, lookup_tag) self.assertEqual(4, len(parsed)) self.assertEqual(12, parser.first(parsed, 'uuu8')) self.assertEqual(37888, parser.first(parsed, 'uu16')) self.assertEqual(305419896, parser.first(parsed, 'uu32')) self.assertEqual(8982983289232, parser.first(parsed, 'uu64'))
async def test_succesful_pairing(mock_pairing): pairing, zeroconf, service = await mock_pairing() url = pairing_url(zeroconf, PAIRING_CODE) data, _ = await utils.simple_get(url) await pairing.finish() # Verify content returned in pairingresponse parsed = parser.parse(data, tag_definitions.lookup_tag) assert parser.first(parsed, "cmpa", "cmpg") == 1 assert parser.first(parsed, "cmpa", "cmnm") == REMOTE_NAME assert parser.first(parsed, "cmpa", "cmty") == "iPhone" assert service.credentials == PAIRING_GUID
def test_parse_binary_plist(): data = {"key": "value"} in_data = tags.raw_tag("plst", plistlib.dumps(data, fmt=plistlib.FMT_BINARY)) parsed = parser.parse(in_data, lookup_tag) assert 1 == len(parsed) assert data, parser.first(parsed, "plst")
def test_parse_binary_plist(self): data = {"key": "value"} in_data = tags.raw_tag("plst", plistlib.dumps(data, fmt=plistlib.FMT_BINARY)) parsed = parser.parse(in_data, lookup_tag) self.assertEqual(1, len(parsed)) self.assertEqual(data, parser.first(parsed, "plst"))
def media_type(self): """Type of media is currently playing, e.g. video, music.""" state = parser.first(self.playstatus, "cmst", "caps") if not state: return MediaType.Unknown mediakind = parser.first(self.playstatus, "cmst", "cmmk") if mediakind is not None: return daap.media_kind(mediakind) # Fallback: if artist or album exists we assume music (not present # for video) if self.artist or self.album: return MediaType.Music return MediaType.Video
async def perform_pairing(self, remote_name, expected_code, port): """Pair with a remote client. This will perform a GET-request to the specified port and hand over information to the client (pyatv) so that the pairing process can be completed. """ server = f"http://127.0.0.1:{port}" url = f"{server}/pairing?pairingcode={expected_code}&servicename=test" data, _ = await utils.simple_get(url) # Verify content returned in pairingresponse parsed = parser.parse(data, tag_definitions.lookup_tag) assert parser.first(parsed, "cmpa", "cmpg") == 1 assert parser.first(parsed, "cmpa", "cmnm") == remote_name assert parser.first(parsed, "cmpa", "cmty") == "iPhone"
def _is_available(self, field: tuple, expected_value=None) -> FeatureState: if self.apple_tv.latest_playstatus: value = parser.first(self.apple_tv.latest_playstatus, *field) if value is not None: if not expected_value or expected_value == value: return FeatureState.Available return FeatureState.Unavailable
def media_type(self): """Type of media is currently playing, e.g. video, music.""" state = parser.first(self.playstatus, 'cmst', 'caps') if not state: return const.MEDIA_TYPE_UNKNOWN mediakind = parser.first(self.playstatus, 'cmst', 'cmmk') if mediakind is not None: return convert.media_kind(mediakind) # Fallback: if artist or album exists we assume music (not present # for video) if self.artist or self.album: return const.MEDIA_TYPE_MUSIC return const.MEDIA_TYPE_VIDEO
async def perform_pairing(self, pairing_response, port): """Pair with a remote client. This will perform a GET-request to the specified port and hand over information to the client (pyatv) so that the pairing process can be completed. """ server = 'http://127.0.0.1:{}'.format(port) url = '{}/pairing?pairingcode={}&servicename=test'.format( server, pairing_response.pairing_code) data, _ = await utils.simple_get(url, self.loop) # Verify content returned in pairingresponse parsed = parser.parse(data, tag_definitions.lookup_tag) self.tc.assertEqual(parser.first(parsed, 'cmpa', 'cmpg'), 1) self.tc.assertEqual(parser.first(parsed, 'cmpa', 'cmnm'), pairing_response.remote_name) self.tc.assertEqual(parser.first(parsed, 'cmpa', 'cmty'), 'iPhone')
def shuffle(self): """If shuffle is enabled or not.""" state = parser.first(self.playstatus, "cmst", "cash") if state is None or state == 0: return ShuffleState.Off # DMAP does not support the "albums" state and will always report # "songs" if shuffle is active return ShuffleState.Songs
async def test_pair_custom_pairing_guid(self): await self._start(pin_code=PIN_CODE2, pairing_guid=PAIRING_GUID2) url = self._pairing_url(PAIRING_CODE2) data, _ = await utils.simple_get(url, self.loop) # Verify content returned in pairingresponse parsed = parser.parse(data, tag_definitions.lookup_tag) self.assertEqual(parser.first(parsed, 'cmpa', 'cmpg'), int(PAIRING_GUID2, 16))
def _convert_button(data): value = parser.first(data, 'cmbe') if value == 'touchUp&time=6&point=20,250': return 'up' elif value == 'touchUp&time=6&point=20,275': return 'down' elif value == 'touchUp&time=7&point=50,100': return 'left' elif value == 'touchUp&time=7&point=75,100': return 'right' else: return value
async def playstatus(self, use_revision=False, timeout=None): """Request raw data about what is currently playing. If use_revision=True, this command will "block" until playstatus changes on the device. Must be logged in. """ cmd_url = _PSU_CMD.format( self.playstatus_revision if use_revision else 0) resp = await self.daap.get(cmd_url, timeout=timeout) self.playstatus_revision = parser.first(resp, 'cmst', 'cmsr') return resp
async def login(self): """Login to Apple TV using specified login id.""" # Do not use session.get_data(...) in login as that would end up in # an infinite loop. def _login_request(): url = self._mkurl("login?[AUTH]&hasFP=1", session=False, login_id=True) _login_request.log_text = "Login request: " + url return self.http.get_data(url, headers=_DMAP_HEADERS,) resp = await self._do(_login_request, is_login=True) self._session_id = parser.first(resp, "mlog", "mlid") _LOGGER.info("Logged in and got session id %s", self._session_id) return self._session_id
async def test_pair_custom_pairing_guid(mock_pairing): pairing, zeroconf, service = await mock_pairing(pin_code=PIN_CODE2, pairing_guid=PAIRING_GUID2) url = pairing_url(zeroconf, PAIRING_CODE2) data, _ = await utils.simple_get(url) await pairing.finish() # Verify content returned in pairingresponse parsed = parser.parse(data, tag_definitions.lookup_tag) assert parser.first(parsed, "cmpa", "cmpg") == int(PAIRING_GUID2, 16) assert service.credentials == PAIRING_GUID2
def _convert_button(self, data): value = parser.first(data, 'cmbe') # Consider navigation buttons if six commands have been received if self.buttons_press_count == 6: if value == 'touchUp&time=6&point=20,250': return 'up' elif value == 'touchUp&time=6&point=20,275': return 'down' elif value == 'touchUp&time=7&point=50,100': return 'left' elif value == 'touchUp&time=7&point=75,100': return 'right' return value
async def login(self): """Login to Apple TV using specified login id.""" # Do not use session.get_data(...) in login as that would end up in # an infinte loop. def _login_request(): return self.http.get_data( self._mkurl('login?[AUTH]&hasFP=1', session=False, login_id=True), headers=_DMAP_HEADERS) resp = await self._do(_login_request, is_login=True) self._session_id = parser.first(resp, 'mlog', 'mlid') _LOGGER.info('Logged in and got session id %s', self._session_id) return self._session_id
def _convert_button(self, data): value = parser.first(data, "cmbe") # Consider navigation buttons if six commands have been received if self.buttons_press_count == 6: if value == "touchUp&time=6&point=20,250": return "up" elif value == "touchUp&time=6&point=20,275": return "down" elif value == "touchUp&time=7&point=50,100": return "left" elif value == "touchUp&time=7&point=75,100": return "right" return value
async def playstatus(self, use_revision=False, timeout=None): """Request raw data about what is currently playing. If use_revision=True, this command will "block" until playstatus changes on the device. Must be logged in. """ cmd_url = _PSU_CMD.format(self.playstatus_revision if use_revision else 0) resp = await self.daap.get(cmd_url, timeout=timeout) self.playstatus_revision = parser.first(resp, "cmst", "cmsr") self.latest_playstatus = resp self.latest_playing = build_playing_instance(resp) self.latest_hash = self.latest_playing.hash return self.latest_playing