From e3ac495b0660dc8271d05cb2f64d4c93fba6621d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20St=C3=A5hl?= Date: Wed, 18 Dec 2019 23:22:44 +0100 Subject: [PATCH] Rename play_state to device_state This commit changes play state to device state and consolidates states a bit to make them fit better between MRP and DMAP. In the long run, a standby state shall be added as well once that is supported. --- docs/documentation/atvremote.md | 16 +-- pyatv/const.py | 23 ++-- pyatv/convert.py | 41 +++---- pyatv/dmap/__init__.py | 4 +- pyatv/interface.py | 28 ++--- pyatv/mrp/__init__.py | 23 ++-- pyatv/mrp/protobuf/ProtocolMessage.proto | 1 + pyatv/mrp/protobuf/ProtocolMessage_pb2.py | 24 ++-- pyatv/mrp/protobuf/ProtocolMessage_pb2.pyi | 2 + pyatv/mrp/protobuf/__init__.py | 1 + scripts/autogen_protobuf_extensions.py | 9 ++ tests/dmap/test_dmap_functional.py | 12 +- tests/test_convert.py | 36 +++--- tests/test_interface.py | 16 +-- tests/test_types.py | 125 --------------------- 15 files changed, 119 insertions(+), 242 deletions(-) delete mode 100644 tests/test_types.py diff --git a/docs/documentation/atvremote.md b/docs/documentation/atvremote.md index 79312797c..cf6331884 100644 --- a/docs/documentation/atvremote.md +++ b/docs/documentation/atvremote.md @@ -103,8 +103,8 @@ without having to ask the device for it: $ atvremote -n Kitchen push_updates Press ENTER to stop - Media type: Unknown - Play state: Paused + Media type: Unknown + Device state: Paused -------------------- Updates will be displayed when they happen. Just press ENTER to stop. @@ -148,7 +148,7 @@ called `commands`, as it will present a list of availble commands: - genre - Genre of the currently playing song - hash - Create a unique hash for what is currently playing - media_type - Type of media is currently playing, e.g. video, music - - play_state - Play state, e.g. playing or paused + - device_state - Device state, e.g. playing or paused - position - Position in the playing media (seconds) - repeat - Repeat mode - shuffle - If shuffle is enabled or not @@ -172,11 +172,11 @@ called `commands`, as it will present a list of availble commands: You can for instance get what is currently playing with `playing`: $ atvremote --id 40:CB:C0:A8:DE:9A playing - Media type: Music - Play state: Playing - Position: 0/397s (0.0%) - Repeat: Off - Shuffle: False + Media type: Music + Device state: Playing + Position: 0/397s (0.0%) + Repeat: Off + Shuffle: False Or seek in the currently playing media: diff --git a/pyatv/const.py b/pyatv/const.py index 5fd0c5457..9fbacc93b 100644 --- a/pyatv/const.py +++ b/pyatv/const.py @@ -34,32 +34,25 @@ MEDIA_TYPE_TV = 4 -# Play states +# Device states #: Device is in idle state -PLAY_STATE_IDLE = 0 - -#: No media is currently select/playing -PLAY_STATE_NO_MEDIA = 1 +DEVICE_STATE_IDLE = 0 #: Media is loading/buffering -PLAY_STATE_LOADING = 2 +DEVICE_STATE_LOADING = 1 #: Media is paused -PLAY_STATE_PAUSED = 3 +DEVICE_STATE_PAUSED = 2 #: Media is playing -PLAY_STATE_PLAYING = 4 - -#: Media is being fast forwarded -PLAY_STATE_FAST_FORWARD = 5 - -#: Media is being rewinded -PLAY_STATE_FAST_BACKWARD = 6 +DEVICE_STATE_PLAYING = 3 #: Media is stopped -PLAY_STATE_STOPPED = 7 +DEVICE_STATE_STOPPED = 4 +#: Media is seeking +DEVICE_STATE_SEEKING = 5 # Repeat states diff --git a/pyatv/convert.py b/pyatv/convert.py index 4b3b2967b..069c42fee 100644 --- a/pyatv/convert.py +++ b/pyatv/convert.py @@ -33,22 +33,18 @@ def media_type_str(mediatype): def playstate(state): """Convert iTunes playstate to API representation.""" # pylint: disable=too-many-return-statements - if state is None: - return const.PLAY_STATE_NO_MEDIA - if state == 0: - return const.PLAY_STATE_IDLE + if state == 0 or state is None: + return const.DEVICE_STATE_IDLE if state == 1: - return const.PLAY_STATE_LOADING + return const.DEVICE_STATE_LOADING if state == 2: - return const.PLAY_STATE_STOPPED + return const.DEVICE_STATE_STOPPED if state == 3: - return const.PLAY_STATE_PAUSED + return const.DEVICE_STATE_PAUSED if state == 4: - return const.PLAY_STATE_PLAYING - if state == 5: - return const.PLAY_STATE_FAST_FORWARD - if state == 6: - return const.PLAY_STATE_FAST_BACKWARD + return const.DEVICE_STATE_PLAYING + if state in (5, 6): + return const.DEVICE_STATE_SEEKING raise exceptions.UnknownPlayState('Unknown playstate: ' + str(state)) @@ -56,22 +52,19 @@ def playstate(state): # pylint: disable=too-many-return-statements def playstate_str(state): """Convert internal API playstate to string.""" - if state == const.PLAY_STATE_NO_MEDIA: - return 'No media' - if state == const.PLAY_STATE_IDLE: + if state == const.DEVICE_STATE_IDLE: return 'Idle' - if state == const.PLAY_STATE_LOADING: + if state == const.DEVICE_STATE_LOADING: return 'Loading' - if state == const.PLAY_STATE_PAUSED: + if state == const.DEVICE_STATE_STOPPED: + return 'Stopped' + if state == const.DEVICE_STATE_PAUSED: return 'Paused' - if state == const.PLAY_STATE_PLAYING: + if state == const.DEVICE_STATE_PLAYING: return 'Playing' - if state == const.PLAY_STATE_FAST_FORWARD: - return 'Fast forward' - if state == const.PLAY_STATE_FAST_BACKWARD: - return 'Fast backward' - if state == const.PLAY_STATE_STOPPED: - return 'Stopped' + if state == const.DEVICE_STATE_SEEKING: + return 'Seeking' + return 'Unsupported' diff --git a/pyatv/dmap/__init__.py b/pyatv/dmap/__init__.py index a5c122a6a..65b4496cf 100644 --- a/pyatv/dmap/__init__.py +++ b/pyatv/dmap/__init__.py @@ -236,8 +236,8 @@ def media_type(self): return const.MEDIA_TYPE_VIDEO @property - def play_state(self): - """Play state, e.g. playing or paused.""" + def device_state(self): + """Device state, e.g. playing or paused.""" state = parser.first(self.playstatus, 'cmst', 'caps') return convert.playstate(state) diff --git a/pyatv/interface.py b/pyatv/interface.py index e54d07e7c..ec8d01ff8 100644 --- a/pyatv/interface.py +++ b/pyatv/interface.py @@ -211,39 +211,39 @@ class Playing: def __str__(self): """Convert this playing object to a readable string.""" output = [] - output.append('Media type: {0}'.format( + output.append(' Media type: {0}'.format( convert.media_type_str(self.media_type))) - output.append('Play state: {0}'.format( - convert.playstate_str(self.play_state))) + output.append('Device state: {0}'.format( + convert.playstate_str(self.device_state))) if self.title is not None: - output.append(' Title: {0}'.format(self.title)) + output.append(' Title: {0}'.format(self.title)) if self.artist is not None: - output.append(' Artist: {0}'.format(self.artist)) + output.append(' Artist: {0}'.format(self.artist)) if self.album is not None: - output.append(' Album: {0}'.format(self.album)) + output.append(' Album: {0}'.format(self.album)) if self.genre is not None: - output.append(' Genre: {0}'.format(self.genre)) + output.append(' Genre: {0}'.format(self.genre)) position = self.position total_time = self.total_time if position is not None and total_time is not None and total_time != 0: - output.append(' Position: {0}/{1}s ({2:.1%})'.format( + output.append(' Position: {0}/{1}s ({2:.1%})'.format( position, total_time, float(position)/float(total_time))) elif position is not None and position != 0: - output.append(' Position: {0}s'.format(position)) + output.append(' Position: {0}s'.format(position)) elif total_time is not None and position != 0: - output.append('Total time: {0}s'.format(total_time)) + output.append(' Total time: {0}s'.format(total_time)) if self.repeat is not None: - output.append(' Repeat: {0}'.format( + output.append(' Repeat: {0}'.format( convert.repeat_str(self.repeat))) if self.shuffle is not None: - output.append(' Shuffle: {0}'.format(self.shuffle)) + output.append(' Shuffle: {0}'.format(self.shuffle)) return '\n'.join(output) @@ -264,8 +264,8 @@ def media_type(self): raise exceptions.NotSupportedError @abstractproperty - def play_state(self): - """Play state, e.g. playing or paused.""" + def device_state(self): + """Device state, e.g. playing or paused.""" raise exceptions.NotSupportedError @abstractproperty diff --git a/pyatv/mrp/__init__.py b/pyatv/mrp/__init__.py index 541483e31..6598afbe3 100644 --- a/pyatv/mrp/__init__.py +++ b/pyatv/mrp/__init__.py @@ -1,3 +1,4 @@ + """Implementation of the MediaRemoteTV Protocol used by ATV4 and later.""" import logging @@ -177,24 +178,24 @@ def media_type(self): return const.MEDIA_TYPE_UNKNOWN @property - def play_state(self): - """Play state, e.g. playing or paused.""" + def device_state(self): # pylint: disable=too-many-return-statements + """Device state, e.g. playing or paused.""" state = self._state.playback_state ssm = SetStateMessage_pb2.SetStateMessage if state is None: - return const.PLAY_STATE_IDLE + return const.DEVICE_STATE_IDLE if state == ssm.Playing: - return const.PLAY_STATE_PLAYING + return const.DEVICE_STATE_PLAYING if state == ssm.Paused: - return const.PLAY_STATE_PAUSED + return const.DEVICE_STATE_PAUSED if state == ssm.Stopped: - return const.PLAY_STATE_STOPPED + return const.DEVICE_STATE_STOPPED if state == ssm.Interrupted: - return const.PLAY_STATE_LOADING - # if state == SetStateMessage_pb2.Seeking - # return XXX + return const.DEVICE_STATE_LOADING + if state == ssm.Seeking: + return const.DEVICE_STATE_SEEKING - return const.PLAY_STATE_PAUSED + return const.DEVICE_STATE_PAUSED @property def title(self): @@ -228,7 +229,7 @@ def position(self): elapsed_time = self._state.metadata_field('elapsedTime') if elapsed_time: diff = (datetime.now() - self._state.timestamp).total_seconds() - if self.play_state == const.PLAY_STATE_PLAYING: + if self.device_state == const.DEVICE_STATE_PLAYING: return int(elapsed_time + diff) return int(elapsed_time) return None diff --git a/pyatv/mrp/protobuf/ProtocolMessage.proto b/pyatv/mrp/protobuf/ProtocolMessage.proto index 9373168a6..d1e937ea8 100644 --- a/pyatv/mrp/protobuf/ProtocolMessage.proto +++ b/pyatv/mrp/protobuf/ProtocolMessage.proto @@ -37,6 +37,7 @@ message ProtocolMessage { CRYPTO_PAIRING_MESSAGE = 34; GAME_CONTROLLER_PROPERTIES_MESSAGE = 35; SET_READY_STATE_MESSAGE = 36; + DEVICE_INFO_UPDATE_MESSAGE = 37; SET_CONNECTION_STATE_MESSAGE = 38; SET_HILITE_MODE_MESSAGE = 40; WAKE_DEVICE_MESSAGE = 41; diff --git a/pyatv/mrp/protobuf/ProtocolMessage_pb2.py b/pyatv/mrp/protobuf/ProtocolMessage_pb2.py index 5e2bee579..cfdaae177 100644 --- a/pyatv/mrp/protobuf/ProtocolMessage_pb2.py +++ b/pyatv/mrp/protobuf/ProtocolMessage_pb2.py @@ -20,7 +20,7 @@ package='', syntax='proto2', serialized_options=None, - serialized_pb=_b('\n(pyatv/mrp/protobuf/ProtocolMessage.proto\"\xc9\x0b\n\x0fProtocolMessage\x12#\n\x04type\x18\x01 \x01(\x0e\x32\x15.ProtocolMessage.Type\x12\x12\n\nidentifier\x18\x02 \x01(\t\x12\x10\n\x08priority\x18\x04 \x01(\x05\x12\x11\n\ttimestamp\x18\x05 \x01(\x04\"\xcd\n\n\x04Type\x12\x18\n\x14SEND_COMMAND_MESSAGE\x10\x01\x12\x1f\n\x1bSEND_COMMAND_RESULT_MESSAGE\x10\x02\x12\x15\n\x11GET_STATE_MESSAGE\x10\x03\x12\x15\n\x11SET_STATE_MESSAGE\x10\x04\x12\x17\n\x13SET_ARTWORK_MESSAGE\x10\x05\x12\x1f\n\x1bREGISTER_HID_DEVICE_MESSAGE\x10\x06\x12&\n\"REGISTER_HID_DEVICE_RESULT_MESSAGE\x10\x07\x12\x1a\n\x16SEND_HID_EVENT_MESSAGE\x10\x08\x12$\n SEND_VIRTUAL_TOUCH_EVENT_MESSAGE\x10\n\x12\x18\n\x14NOTIFICATION_MESSAGE\x10\x0b\x12.\n*CONTENT_ITEMS_CHANGED_NOTIFICATION_MESSAGE\x10\x0c\x12\x17\n\x13\x44\x45VICE_INFO_MESSAGE\x10\x0f\x12!\n\x1d\x43LIENT_UPDATES_CONFIG_MESSAGE\x10\x10\x12\'\n#VOLUME_CONTROL_AVAILABILITY_MESSAGE\x10\x11\x12\x1b\n\x17GAME_CONTROLLER_MESSAGE\x10\x12\x12$\n REGISTER_GAME_CONTROLLER_MESSAGE\x10\x13\x12-\n)REGISTER_GAME_CONTROLLER_RESPONSE_MESSAGE\x10\x14\x12&\n\"UNREGISTER_GAME_CONTROLLER_MESSAGE\x10\x15\x12/\n+REGISTER_FOR_GAME_CONTROLLER_EVENTS_MESSAGE\x10\x16\x12\x14\n\x10KEYBOARD_MESSAGE\x10\x17\x12 \n\x1cGET_KEYBOARD_SESSION_MESSAGE\x10\x18\x12\x16\n\x12TEXT_INPUT_MESSAGE\x10\x19\x12#\n\x1fGET_VOICE_INPUT_DEVICES_MESSAGE\x10\x1a\x12,\n(GET_VOICE_INPUT_DEVICES_RESPONSE_MESSAGE\x10\x1b\x12\'\n#REGISTER_VOICE_INPUT_DEVICE_MESSAGE\x10\x1c\x12\x30\n,REGISTER_VOICE_INPUT_DEVICE_RESPONSE_MESSAGE\x10\x1d\x12\x1f\n\x1bSET_RECORDING_STATE_MESSAGE\x10\x1e\x12\x1c\n\x18SEND_VOICE_INPUT_MESSAGE\x10\x1f\x12\"\n\x1ePLAYBACK_QUEUE_REQUEST_MESSAGE\x10 \x12\x17\n\x13TRANSACTION_MESSAGE\x10!\x12\x1a\n\x16\x43RYPTO_PAIRING_MESSAGE\x10\"\x12&\n\"GAME_CONTROLLER_PROPERTIES_MESSAGE\x10#\x12\x1b\n\x17SET_READY_STATE_MESSAGE\x10$\x12 \n\x1cSET_CONNECTION_STATE_MESSAGE\x10&\x12\x1b\n\x17SET_HILITE_MODE_MESSAGE\x10(\x12\x17\n\x13WAKE_DEVICE_MESSAGE\x10)\x12+\n\'SEND_PACKED_VIRTUAL_TOUCH_EVENT_MESSAGE\x10+\x12\"\n\x1eSET_NOW_PLAYING_CLIENT_MESSAGE\x10.\x12\x19\n\x15UPDATE_CLIENT_MESSAGE\x10\x37\x12\x1f\n\x1bUPDATE_CONTENT_ITEM_MESSAGE\x10\x38*\x08\x08\x06\x10\x80\x80\x80\x80\x02') + serialized_pb=_b('\n(pyatv/mrp/protobuf/ProtocolMessage.proto\"\xe9\x0b\n\x0fProtocolMessage\x12#\n\x04type\x18\x01 \x01(\x0e\x32\x15.ProtocolMessage.Type\x12\x12\n\nidentifier\x18\x02 \x01(\t\x12\x10\n\x08priority\x18\x04 \x01(\x05\x12\x11\n\ttimestamp\x18\x05 \x01(\x04\"\xed\n\n\x04Type\x12\x18\n\x14SEND_COMMAND_MESSAGE\x10\x01\x12\x1f\n\x1bSEND_COMMAND_RESULT_MESSAGE\x10\x02\x12\x15\n\x11GET_STATE_MESSAGE\x10\x03\x12\x15\n\x11SET_STATE_MESSAGE\x10\x04\x12\x17\n\x13SET_ARTWORK_MESSAGE\x10\x05\x12\x1f\n\x1bREGISTER_HID_DEVICE_MESSAGE\x10\x06\x12&\n\"REGISTER_HID_DEVICE_RESULT_MESSAGE\x10\x07\x12\x1a\n\x16SEND_HID_EVENT_MESSAGE\x10\x08\x12$\n SEND_VIRTUAL_TOUCH_EVENT_MESSAGE\x10\n\x12\x18\n\x14NOTIFICATION_MESSAGE\x10\x0b\x12.\n*CONTENT_ITEMS_CHANGED_NOTIFICATION_MESSAGE\x10\x0c\x12\x17\n\x13\x44\x45VICE_INFO_MESSAGE\x10\x0f\x12!\n\x1d\x43LIENT_UPDATES_CONFIG_MESSAGE\x10\x10\x12\'\n#VOLUME_CONTROL_AVAILABILITY_MESSAGE\x10\x11\x12\x1b\n\x17GAME_CONTROLLER_MESSAGE\x10\x12\x12$\n REGISTER_GAME_CONTROLLER_MESSAGE\x10\x13\x12-\n)REGISTER_GAME_CONTROLLER_RESPONSE_MESSAGE\x10\x14\x12&\n\"UNREGISTER_GAME_CONTROLLER_MESSAGE\x10\x15\x12/\n+REGISTER_FOR_GAME_CONTROLLER_EVENTS_MESSAGE\x10\x16\x12\x14\n\x10KEYBOARD_MESSAGE\x10\x17\x12 \n\x1cGET_KEYBOARD_SESSION_MESSAGE\x10\x18\x12\x16\n\x12TEXT_INPUT_MESSAGE\x10\x19\x12#\n\x1fGET_VOICE_INPUT_DEVICES_MESSAGE\x10\x1a\x12,\n(GET_VOICE_INPUT_DEVICES_RESPONSE_MESSAGE\x10\x1b\x12\'\n#REGISTER_VOICE_INPUT_DEVICE_MESSAGE\x10\x1c\x12\x30\n,REGISTER_VOICE_INPUT_DEVICE_RESPONSE_MESSAGE\x10\x1d\x12\x1f\n\x1bSET_RECORDING_STATE_MESSAGE\x10\x1e\x12\x1c\n\x18SEND_VOICE_INPUT_MESSAGE\x10\x1f\x12\"\n\x1ePLAYBACK_QUEUE_REQUEST_MESSAGE\x10 \x12\x17\n\x13TRANSACTION_MESSAGE\x10!\x12\x1a\n\x16\x43RYPTO_PAIRING_MESSAGE\x10\"\x12&\n\"GAME_CONTROLLER_PROPERTIES_MESSAGE\x10#\x12\x1b\n\x17SET_READY_STATE_MESSAGE\x10$\x12\x1e\n\x1a\x44\x45VICE_INFO_UPDATE_MESSAGE\x10%\x12 \n\x1cSET_CONNECTION_STATE_MESSAGE\x10&\x12\x1b\n\x17SET_HILITE_MODE_MESSAGE\x10(\x12\x17\n\x13WAKE_DEVICE_MESSAGE\x10)\x12+\n\'SEND_PACKED_VIRTUAL_TOUCH_EVENT_MESSAGE\x10+\x12\"\n\x1eSET_NOW_PLAYING_CLIENT_MESSAGE\x10.\x12\x19\n\x15UPDATE_CLIENT_MESSAGE\x10\x37\x12\x1f\n\x1bUPDATE_CONTENT_ITEM_MESSAGE\x10\x38*\x08\x08\x06\x10\x80\x80\x80\x80\x02') ) @@ -164,38 +164,42 @@ serialized_options=None, type=None), _descriptor.EnumValueDescriptor( - name='SET_CONNECTION_STATE_MESSAGE', index=33, number=38, + name='DEVICE_INFO_UPDATE_MESSAGE', index=33, number=37, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( - name='SET_HILITE_MODE_MESSAGE', index=34, number=40, + name='SET_CONNECTION_STATE_MESSAGE', index=34, number=38, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( - name='WAKE_DEVICE_MESSAGE', index=35, number=41, + name='SET_HILITE_MODE_MESSAGE', index=35, number=40, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( - name='SEND_PACKED_VIRTUAL_TOUCH_EVENT_MESSAGE', index=36, number=43, + name='WAKE_DEVICE_MESSAGE', index=36, number=41, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( - name='SET_NOW_PLAYING_CLIENT_MESSAGE', index=37, number=46, + name='SEND_PACKED_VIRTUAL_TOUCH_EVENT_MESSAGE', index=37, number=43, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( - name='UPDATE_CLIENT_MESSAGE', index=38, number=55, + name='SET_NOW_PLAYING_CLIENT_MESSAGE', index=38, number=46, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( - name='UPDATE_CONTENT_ITEM_MESSAGE', index=39, number=56, + name='UPDATE_CLIENT_MESSAGE', index=39, number=55, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='UPDATE_CONTENT_ITEM_MESSAGE', index=40, number=56, serialized_options=None, type=None), ], containing_type=None, serialized_options=None, serialized_start=159, - serialized_end=1516, + serialized_end=1548, ) _sym_db.RegisterEnumDescriptor(_PROTOCOLMESSAGE_TYPE) @@ -249,7 +253,7 @@ oneofs=[ ], serialized_start=45, - serialized_end=1526, + serialized_end=1558, ) _PROTOCOLMESSAGE.fields_by_name['type'].enum_type = _PROTOCOLMESSAGE_TYPE diff --git a/pyatv/mrp/protobuf/ProtocolMessage_pb2.pyi b/pyatv/mrp/protobuf/ProtocolMessage_pb2.pyi index 20163e245..dbb2f6e3d 100644 --- a/pyatv/mrp/protobuf/ProtocolMessage_pb2.pyi +++ b/pyatv/mrp/protobuf/ProtocolMessage_pb2.pyi @@ -69,6 +69,7 @@ class ProtocolMessage(google___protobuf___message___Message): CRYPTO_PAIRING_MESSAGE = typing___cast(ProtocolMessage.Type, 34) GAME_CONTROLLER_PROPERTIES_MESSAGE = typing___cast(ProtocolMessage.Type, 35) SET_READY_STATE_MESSAGE = typing___cast(ProtocolMessage.Type, 36) + DEVICE_INFO_UPDATE_MESSAGE = typing___cast(ProtocolMessage.Type, 37) SET_CONNECTION_STATE_MESSAGE = typing___cast(ProtocolMessage.Type, 38) SET_HILITE_MODE_MESSAGE = typing___cast(ProtocolMessage.Type, 40) WAKE_DEVICE_MESSAGE = typing___cast(ProtocolMessage.Type, 41) @@ -109,6 +110,7 @@ class ProtocolMessage(google___protobuf___message___Message): CRYPTO_PAIRING_MESSAGE = typing___cast(ProtocolMessage.Type, 34) GAME_CONTROLLER_PROPERTIES_MESSAGE = typing___cast(ProtocolMessage.Type, 35) SET_READY_STATE_MESSAGE = typing___cast(ProtocolMessage.Type, 36) + DEVICE_INFO_UPDATE_MESSAGE = typing___cast(ProtocolMessage.Type, 37) SET_CONNECTION_STATE_MESSAGE = typing___cast(ProtocolMessage.Type, 38) SET_HILITE_MODE_MESSAGE = typing___cast(ProtocolMessage.Type, 40) WAKE_DEVICE_MESSAGE = typing___cast(ProtocolMessage.Type, 41) diff --git a/pyatv/mrp/protobuf/__init__.py b/pyatv/mrp/protobuf/__init__.py index ffa95223d..7451badfa 100644 --- a/pyatv/mrp/protobuf/__init__.py +++ b/pyatv/mrp/protobuf/__init__.py @@ -90,6 +90,7 @@ ProtocolMessage.CLIENT_UPDATES_CONFIG_MESSAGE: ClientUpdatesConfigMessage_pb2.clientUpdatesConfigMessage, ProtocolMessage.CRYPTO_PAIRING_MESSAGE: CryptoPairingMessage_pb2.cryptoPairingMessage, ProtocolMessage.DEVICE_INFO_MESSAGE: DeviceInfoMessage_pb2.deviceInfoMessage, + ProtocolMessage.DEVICE_INFO_UPDATE_MESSAGE: DeviceInfoMessage_pb2.deviceInfoMessage, ProtocolMessage.GET_KEYBOARD_SESSION_MESSAGE: GetKeyboardSessionMessage_pb2.getKeyboardSessionMessage, ProtocolMessage.KEYBOARD_MESSAGE: KeyboardMessage_pb2.keyboardMessage, ProtocolMessage.NOTIFICATION_MESSAGE: NotificationMessage_pb2.notificationMessage, diff --git a/scripts/autogen_protobuf_extensions.py b/scripts/autogen_protobuf_extensions.py index 37a44cf08..baadf39c2 100755 --- a/scripts/autogen_protobuf_extensions.py +++ b/scripts/autogen_protobuf_extensions.py @@ -13,6 +13,9 @@ from collections import namedtuple +# New messages re-using inner message of another type +REUSED_MESSAGES = {'DEVICE_INFO_MESSAGE': 'DEVICE_INFO_UPDATE_MESSAGE'} + BASE_PACKAGE = 'pyatv.mrp.protobuf' OUTPUT_TEMPLATE = """\"\"\"Simplified extension handling for protobuf messages. @@ -122,6 +125,12 @@ def main(): '{0} = ProtocolMessage.{0}'.format( info.const)) + reused = REUSED_MESSAGES.get(info.const) + if reused: + extensions.append( + 'ProtocolMessage.{0}: {1}.{2},'.format( + reused, info.module, info.accessor)) + # Look for remaining messages for module_name, message_name in extract_unreferenced_messages(): if message_name not in message_names: diff --git a/tests/dmap/test_dmap_functional.py b/tests/dmap/test_dmap_functional.py index 1fd025023..a4f0b52ce 100644 --- a/tests/dmap/test_dmap_functional.py +++ b/tests/dmap/test_dmap_functional.py @@ -188,7 +188,7 @@ async def test_metadata_none_type_when_not_playing(self): playing = await self.atv.metadata.playing() self.assertEqual(playing.media_type, const.MEDIA_TYPE_UNKNOWN) - self.assertEqual(playing.play_state, const.PLAY_STATE_NO_MEDIA) + self.assertEqual(playing.device_state, const.DEVICE_STATE_IDLE) @unittest_run_loop async def test_metadata_video_paused(self): @@ -197,7 +197,7 @@ async def test_metadata_video_paused(self): playing = await self.atv.metadata.playing() self.assertEqual(playing.media_type, const.MEDIA_TYPE_VIDEO) - self.assertEqual(playing.play_state, const.PLAY_STATE_PAUSED) + self.assertEqual(playing.device_state, const.DEVICE_STATE_PAUSED) self.assertEqual(playing.title, 'dummy') self.assertEqual(playing.total_time, 123) self.assertEqual(playing.position, 3) @@ -209,7 +209,7 @@ async def test_metadata_video_playing(self): playing = await self.atv.metadata.playing() self.assertEqual(playing.media_type, const.MEDIA_TYPE_VIDEO) - self.assertEqual(playing.play_state, const.PLAY_STATE_PLAYING) + self.assertEqual(playing.device_state, const.DEVICE_STATE_PLAYING) self.assertEqual(playing.title, 'video') self.assertEqual(playing.total_time, 40) self.assertEqual(playing.position, 10) @@ -223,7 +223,7 @@ async def test_metadata_music_paused(self): playing = await self.atv.metadata.playing() self.assertEqual(playing.media_type, const.MEDIA_TYPE_MUSIC) - self.assertEqual(playing.play_state, const.PLAY_STATE_PAUSED) + self.assertEqual(playing.device_state, const.DEVICE_STATE_PAUSED) self.assertEqual(playing.title, 'music') self.assertEqual(playing.artist, 'artist') self.assertEqual(playing.album, 'album') @@ -240,7 +240,7 @@ async def test_metadata_music_playing(self): playing = await self.atv.metadata.playing() self.assertEqual(playing.media_type, const.MEDIA_TYPE_MUSIC) - self.assertEqual(playing.play_state, const.PLAY_STATE_PLAYING) + self.assertEqual(playing.device_state, const.DEVICE_STATE_PLAYING) self.assertEqual(playing.title, 'music') self.assertEqual(playing.artist, 'test1') self.assertEqual(playing.album, 'test2') @@ -338,7 +338,7 @@ async def test_metadata_loading(self): self.usecase.media_is_loading() playing = await self.atv.metadata.playing() - self.assertEqual(playing.play_state, const.PLAY_STATE_LOADING) + self.assertEqual(playing.device_state, const.DEVICE_STATE_LOADING) @unittest_run_loop async def test_button_unsupported_raises(self): diff --git a/tests/test_convert.py b/tests/test_convert.py index 890ab75b2..9789c222b 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -109,28 +109,28 @@ def test_unknown_media_type_to_str(self): # PLAYSTATE TESTS - def test_play_state_no_media(self): + def test_device_state_no_media(self): # This test should not really be here as "None" is in reality not a # valid value. But it is supported nonetheless because that makes # usage nicer. None means that the field is not included in a # server response, which matches the behavior of dmap.first. - self.assertEqual(const.PLAY_STATE_NO_MEDIA, + self.assertEqual(const.DEVICE_STATE_IDLE, convert.playstate(None)) def test_regular_playstates(self): - self.assertEqual(const.PLAY_STATE_IDLE, + self.assertEqual(const.DEVICE_STATE_IDLE, convert.playstate(PLAY_STATE_IDLE)) - self.assertEqual(const.PLAY_STATE_LOADING, + self.assertEqual(const.DEVICE_STATE_LOADING, convert.playstate(PLAY_STATE_LOADING)) - self.assertEqual(const.PLAY_STATE_STOPPED, + self.assertEqual(const.DEVICE_STATE_STOPPED, convert.playstate(PLAY_STATE_STOPPED)) - self.assertEqual(const.PLAY_STATE_PAUSED, + self.assertEqual(const.DEVICE_STATE_PAUSED, convert.playstate(PLAY_STATE_PAUSED)) - self.assertEqual(const.PLAY_STATE_PLAYING, + self.assertEqual(const.DEVICE_STATE_PLAYING, convert.playstate(PLAY_STATE_PLAYING)) - self.assertEqual(const.PLAY_STATE_FAST_FORWARD, + self.assertEqual(const.DEVICE_STATE_SEEKING, convert.playstate(PLAY_STATE_FORWARD)) - self.assertEqual(const.PLAY_STATE_FAST_BACKWARD, + self.assertEqual(const.DEVICE_STATE_SEEKING, convert.playstate(PLAY_STATE_BACKWARD)) def test_unknown_playstate_throws(self): @@ -138,20 +138,18 @@ def test_unknown_playstate_throws(self): convert.playstate(99999) def test_playstate_str(self): - self.assertEqual('No media', - convert.playstate_str(const.PLAY_STATE_NO_MEDIA)) self.assertEqual('Idle', - convert.playstate_str(const.PLAY_STATE_IDLE)) + convert.playstate_str(const.DEVICE_STATE_IDLE)) self.assertEqual('Loading', - convert.playstate_str(const.PLAY_STATE_LOADING)) + convert.playstate_str(const.DEVICE_STATE_LOADING)) + self.assertEqual('Stopped', + convert.playstate_str(const.DEVICE_STATE_STOPPED)) self.assertEqual('Paused', - convert.playstate_str(const.PLAY_STATE_PAUSED)) + convert.playstate_str(const.DEVICE_STATE_PAUSED)) self.assertEqual('Playing', - convert.playstate_str(const.PLAY_STATE_PLAYING)) - self.assertEqual('Fast forward', - convert.playstate_str(const.PLAY_STATE_FAST_FORWARD)) - self.assertEqual('Fast backward', - convert.playstate_str(const.PLAY_STATE_FAST_BACKWARD)) + convert.playstate_str(const.DEVICE_STATE_PLAYING)) + self.assertEqual('Seeking', + convert.playstate_str(const.DEVICE_STATE_SEEKING)) def test_unsupported_playstate_str(self): self.assertEqual('Unsupported', convert.playstate_str(999)) diff --git a/tests/test_interface.py b/tests/test_interface.py index 733344852..3eab240aa 100644 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -38,11 +38,11 @@ def _private_method_ignored(self): class PlayingDummy(interface.Playing): def __init__(self, media_type=const.MEDIA_TYPE_VIDEO, - play_state=const.PLAY_STATE_PLAYING, title=None, artist=None, - album=None, genre=None, total_time=None, position=None, - shuffle=True, repeat=const.REPEAT_STATE_TRACK): + device_state=const.DEVICE_STATE_PLAYING, title=None, + artist=None, album=None, genre=None, total_time=None, + position=None, shuffle=True, repeat=const.REPEAT_STATE_TRACK): self._media_type = media_type - self._play_state = play_state + self._device_state = device_state self._title = title self._artist = artist self._album = album @@ -58,9 +58,9 @@ def media_type(self): return self._media_type @property - def play_state(self): - """Current play state, e.g. playing or paused.""" - return self._play_state + def device_state(self): + """Current device state, e.g. playing or paused.""" + return self._device_state @property def title(self): @@ -145,7 +145,7 @@ def test_try_to_be_smart_with_abbreviations(self): def test_playing_media_type_and_playstate(self): out = str(PlayingDummy()) self.assertIn(convert.media_type_str(const.MEDIA_TYPE_VIDEO), out) - self.assertIn(convert.playstate_str(const.PLAY_STATE_PLAYING), out) + self.assertIn(convert.playstate_str(const.DEVICE_STATE_PLAYING), out) def test_playing_title_artist_album_genre(self): out = str(PlayingDummy( diff --git a/tests/test_types.py b/tests/test_types.py deleted file mode 100644 index 0a55e72c4..000000000 --- a/tests/test_types.py +++ /dev/null @@ -1,125 +0,0 @@ -"""Unit tests for pyatv.types.""" - -import unittest - -from pyatv import (const, convert, exceptions) - -# These are extracted from iTunes, see for instance: -# http://www.blooming.no/wp-content/uploads/2013/03/ITLibMediaItem.h -# Key: cmst.cmmk -MEDIA_KIND_UNKNOWN = 1 -MEDIA_KIND_SONG = 2 -MEDIA_KIND_MOVIE = 3 -MEDIA_KIND_PODCAST = 4 -MEDIA_KIND_AUDIOBOOK = 5 -MEDIA_KIND_PDFBOOKLET = 6 -MEDIA_KIND_MUSICVIDEO = 7 -MEDIA_KIND_TVSHOW = 8 -MEDIA_KIND_INTERACTIVEBOOKLET = 9 -MEDIA_KIND_COACHEDAUDIO = 10 -MEDIA_KIND_VIDEOPASS = 11 -MEDIA_KIND_HOMEVIDEO = 12 -MEDIA_KIND_FUTUREVIDEO = 13 -MEDIA_KIND_RINGTONE = 14 -MEDIA_KIND_DIGITALBOOKLET = 15 -MEDIA_KIND_IOSAPPLICATION = 16 -MEDIA_KIND_VOICEMEMO = 17 -MEDIA_KIND_ITUNESU = 18 -MEDIA_KIND_BOOK = 19 -MEDIA_KIND_PDFBOOK = 20 -MEDIA_KIND_ALERTTONE = 21 - - -# Found on various places on the Internet as well as by testing -# Key: cmst.caps -PLAY_STATE_LOADING = 1 # E.g. buffering -PLAY_STATE_PAUSED = 3 -PLAY_STATE_PLAYING = 4 -PLAY_STATE_FORWARD = 5 -PLAY_STATE_BACKWARD = 6 - - -class TypesTest(unittest.TestCase): - - # MEDIA KIND TESTS - - def test_unknown_media_kind(self): - self.assertEqual(const.MEDIA_TYPE_UNKNOWN, - convert.media_kind(MEDIA_KIND_UNKNOWN)) - - def test_video_media_kinds(self): - self.assertEqual(const.MEDIA_TYPE_VIDEO, - convert.media_kind(MEDIA_KIND_MOVIE)) - self.assertEqual(const.MEDIA_TYPE_VIDEO, - convert.media_kind(MEDIA_KIND_MUSICVIDEO)) - self.assertEqual(const.MEDIA_TYPE_VIDEO, - convert.media_kind(MEDIA_KIND_VIDEOPASS)) - self.assertEqual(const.MEDIA_TYPE_VIDEO, - convert.media_kind(MEDIA_KIND_HOMEVIDEO)) - self.assertEqual(const.MEDIA_TYPE_VIDEO, - convert.media_kind(MEDIA_KIND_FUTUREVIDEO)) - self.assertEqual(const.MEDIA_TYPE_VIDEO, - convert.media_kind(MEDIA_KIND_ITUNESU)) - - def test_music_media_kinds(self): - self.assertEqual(const.MEDIA_TYPE_MUSIC, - convert.media_kind(MEDIA_KIND_SONG)) - self.assertEqual(const.MEDIA_TYPE_MUSIC, - convert.media_kind(MEDIA_KIND_PODCAST)) - self.assertEqual(const.MEDIA_TYPE_MUSIC, - convert.media_kind(MEDIA_KIND_COACHEDAUDIO)) - self.assertEqual(const.MEDIA_TYPE_MUSIC, - convert.media_kind(MEDIA_KIND_RINGTONE)) - self.assertEqual(const.MEDIA_TYPE_MUSIC, - convert.media_kind(MEDIA_KIND_VOICEMEMO)) - self.assertEqual(const.MEDIA_TYPE_MUSIC, - convert.media_kind(MEDIA_KIND_ALERTTONE)) - - def test_tv_kinds(self): - self.assertEqual(const.MEDIA_TYPE_TV, - convert.media_kind(MEDIA_KIND_TVSHOW)) - - def test_unknown_media_kind_throws(self): - with self.assertRaises(exceptions.UnknownMediaKind): - convert.media_kind(99999) - - # PLAYSTATE TESTS - - def test_play_state_no_media(self): - # This test should not really be here as "None" is in reality not a - # valid value. But it is supported nonetheless because that makes - # usage nicer. None means that the field is not included in a - # server response, which matches the behavior of dmap.first. - self.assertEqual(const.PLAY_STATE_NO_MEDIA, - convert.playstate(None)) - - def test_regular_play_states(self): - self.assertEqual(const.PLAY_STATE_LOADING, - convert.playstate(PLAY_STATE_LOADING)) - self.assertEqual(const.PLAY_STATE_PAUSED, - convert.playstate(PLAY_STATE_PAUSED)) - self.assertEqual(const.PLAY_STATE_PLAYING, - convert.playstate(PLAY_STATE_PLAYING)) - self.assertEqual(const.PLAY_STATE_FAST_FORWARD, - convert.playstate(PLAY_STATE_FORWARD)) - self.assertEqual(const.PLAY_STATE_FAST_BACKWARD, - convert.playstate(PLAY_STATE_BACKWARD)) - - def test_unknown_playstate_throws(self): - with self.assertRaises(exceptions.UnknownPlayState): - convert.playstate(99999) - - # TIME TESTS - - def test_no_time_returns_zero(self): - self.assertEqual(0, convert.ms_to_s(None)) - - def test_time_in_seconds(self): - self.assertEqual(0, convert.ms_to_s(400)) - self.assertEqual(1, convert.ms_to_s(501)) - self.assertEqual(36, convert.ms_to_s(36000)) - - def test_invalid_time(self): - # Sometimes really large times are reported during buffering, this test - # handles those special cases. - self.assertEqual(0, convert.ms_to_s(2**32 - 1))