├── .codecov.yml ├── .coveragerc ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ ├── feature_request.yml │ ├── implementation-proposal.yml │ ├── investigation.yml │ ├── minor-change.yml │ └── question-or-idea.yml ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ ├── release.yml │ └── tests.yml ├── .gitignore ├── .gitpod.yml ├── CHANGES.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CONTRIBUTORS.md ├── Dockerfile ├── LICENSE.md ├── MANIFEST.in ├── README.md ├── base_versions.txt ├── chickn.yaml ├── docs ├── .gitignore ├── 404.html ├── CNAME ├── Gemfile ├── _config.yml ├── _includes │ ├── api │ ├── atvremote_scan │ ├── code │ ├── issue │ └── pypi ├── _layouts │ └── template.html ├── api │ ├── pyatv.conf.html │ ├── pyatv.const.html │ ├── pyatv.convert.html │ ├── pyatv.exceptions.html │ ├── pyatv.helpers.html │ ├── pyatv.html │ ├── pyatv.interface.html │ ├── pyatv.settings.html │ ├── pyatv.storage.file_storage.html │ ├── pyatv.storage.html │ └── pyatv.storage.memory_storage.html ├── assets │ ├── css │ │ ├── custom.css │ │ ├── hljs.css │ │ ├── normalize.css │ │ ├── pdoc.css │ │ ├── sanitize.css │ │ └── style.scss │ ├── img │ │ └── logo.svg │ └── js │ │ ├── highlight.9.12.0.min.js │ │ ├── mermaid.8.9.2.min.js │ │ └── mermaid.min.js.map ├── development │ ├── apps.md │ ├── audio.md │ ├── control.md │ ├── development.md │ ├── device_info.md │ ├── examples.md │ ├── features.md │ ├── keyboard.md │ ├── listeners.md │ ├── logging.md │ ├── metadata.md │ ├── power_management.md │ ├── scan_pair_and_connect.md │ ├── services.md │ ├── storage.md │ ├── stream.md │ └── testing.md ├── documentation │ ├── atvlog.md │ ├── atvproxy.md │ ├── atvremote.md │ ├── atvscript.md │ ├── concepts.md │ ├── documentation.md │ ├── getting_started.md │ ├── protocols.md │ ├── supported_features.md │ ├── tutorial.md │ └── workspace.code-workspace ├── favicon.ico ├── index.md ├── internals │ ├── design.md │ ├── documentation.md │ ├── interfaces.md │ ├── internals.md │ ├── submit_pr.md │ ├── testing.md │ └── tools.md ├── pdoc_templates │ ├── config.mako │ └── html.mako └── support │ ├── acknowledgements.md │ ├── faq.md │ ├── migration.md │ ├── scanning_issues.md │ ├── support.md │ └── troubleshooting.md ├── examples ├── __init__.py ├── auto_connect.py ├── connect_with_credentials.py ├── manual_connect.py ├── pairing.py ├── play_url.py ├── scan_and_connect.py ├── storage.py ├── stream.py └── tutorial.py ├── pyatv ├── __init__.py ├── auth │ ├── hap_channel.py │ ├── hap_pairing.py │ ├── hap_session.py │ ├── hap_srp.py │ ├── hap_tlv8.py │ └── server_auth.py ├── conf.py ├── const.py ├── convert.py ├── core │ ├── __init__.py │ ├── facade.py │ ├── mdns.py │ ├── protocol.py │ ├── relayer.py │ └── scan.py ├── exceptions.py ├── helpers.py ├── interface.py ├── protocols │ ├── __init__.py │ ├── airplay │ │ ├── __init__.py │ │ ├── ap2_session.py │ │ ├── auth │ │ │ ├── __init__.py │ │ │ ├── hap.py │ │ │ ├── hap_transient.py │ │ │ └── legacy.py │ │ ├── channels.py │ │ ├── mrp_connection.py │ │ ├── pairing.py │ │ ├── player.py │ │ ├── server_auth.py │ │ ├── srp.py │ │ └── utils.py │ ├── companion │ │ ├── __init__.py │ │ ├── api.py │ │ ├── auth.py │ │ ├── connection.py │ │ ├── keyed_archiver.py │ │ ├── pairing.py │ │ ├── plist_payloads │ │ │ ├── __init__.py │ │ │ └── rti_text_operations.py │ │ ├── protocol.py │ │ └── server_auth.py │ ├── dmap │ │ ├── __init__.py │ │ ├── daap.py │ │ ├── pairing.py │ │ ├── parser.py │ │ ├── tag_definitions.py │ │ └── tags.py │ ├── mrp │ │ ├── __init__.py │ │ ├── auth.py │ │ ├── connection.py │ │ ├── messages.py │ │ ├── pairing.py │ │ ├── player_state.py │ │ ├── protobuf │ │ │ ├── AudioFadeMessage.proto │ │ │ ├── AudioFadeMessage_pb2.py │ │ │ ├── AudioFadeMessage_pb2.pyi │ │ │ ├── AudioFadeResponseMessage.proto │ │ │ ├── AudioFadeResponseMessage_pb2.py │ │ │ ├── AudioFadeResponseMessage_pb2.pyi │ │ │ ├── AudioFormatSettingsMessage.proto │ │ │ ├── AudioFormatSettingsMessage_pb2.py │ │ │ ├── AudioFormatSettingsMessage_pb2.pyi │ │ │ ├── ClientUpdatesConfigMessage.proto │ │ │ ├── ClientUpdatesConfigMessage_pb2.py │ │ │ ├── ClientUpdatesConfigMessage_pb2.pyi │ │ │ ├── CommandInfo.proto │ │ │ ├── CommandInfo_pb2.py │ │ │ ├── CommandInfo_pb2.pyi │ │ │ ├── CommandOptions.proto │ │ │ ├── CommandOptions_pb2.py │ │ │ ├── CommandOptions_pb2.pyi │ │ │ ├── Common.proto │ │ │ ├── Common_pb2.py │ │ │ ├── Common_pb2.pyi │ │ │ ├── ConfigureConnectionMessage.proto │ │ │ ├── ConfigureConnectionMessage_pb2.py │ │ │ ├── ConfigureConnectionMessage_pb2.pyi │ │ │ ├── ContentItem.proto │ │ │ ├── ContentItemMetadata.proto │ │ │ ├── ContentItemMetadata_pb2.py │ │ │ ├── ContentItemMetadata_pb2.pyi │ │ │ ├── ContentItem_pb2.py │ │ │ ├── ContentItem_pb2.pyi │ │ │ ├── CryptoPairingMessage.proto │ │ │ ├── CryptoPairingMessage_pb2.py │ │ │ ├── CryptoPairingMessage_pb2.pyi │ │ │ ├── DeviceInfoMessage.proto │ │ │ ├── DeviceInfoMessage_pb2.py │ │ │ ├── DeviceInfoMessage_pb2.pyi │ │ │ ├── GenericMessage.proto │ │ │ ├── GenericMessage_pb2.py │ │ │ ├── GenericMessage_pb2.pyi │ │ │ ├── GetKeyboardSessionMessage.proto │ │ │ ├── GetKeyboardSessionMessage_pb2.py │ │ │ ├── GetKeyboardSessionMessage_pb2.pyi │ │ │ ├── GetRemoteTextInputSessionMessage.proto │ │ │ ├── GetRemoteTextInputSessionMessage_pb2.py │ │ │ ├── GetRemoteTextInputSessionMessage_pb2.pyi │ │ │ ├── GetVolumeMessage.proto │ │ │ ├── GetVolumeMessage_pb2.py │ │ │ ├── GetVolumeMessage_pb2.pyi │ │ │ ├── GetVolumeResultMessage.proto │ │ │ ├── GetVolumeResultMessage_pb2.py │ │ │ ├── GetVolumeResultMessage_pb2.pyi │ │ │ ├── KeyboardMessage.proto │ │ │ ├── KeyboardMessage_pb2.py │ │ │ ├── KeyboardMessage_pb2.pyi │ │ │ ├── LanguageOption.proto │ │ │ ├── LanguageOption_pb2.py │ │ │ ├── LanguageOption_pb2.pyi │ │ │ ├── ModifyOutputContextRequestMessage.proto │ │ │ ├── ModifyOutputContextRequestMessage_pb2.py │ │ │ ├── ModifyOutputContextRequestMessage_pb2.pyi │ │ │ ├── NotificationMessage.proto │ │ │ ├── NotificationMessage_pb2.py │ │ │ ├── NotificationMessage_pb2.pyi │ │ │ ├── NowPlayingClient.proto │ │ │ ├── NowPlayingClient_pb2.py │ │ │ ├── NowPlayingClient_pb2.pyi │ │ │ ├── NowPlayingInfo.proto │ │ │ ├── NowPlayingInfo_pb2.py │ │ │ ├── NowPlayingInfo_pb2.pyi │ │ │ ├── NowPlayingPlayer.proto │ │ │ ├── NowPlayingPlayer_pb2.py │ │ │ ├── NowPlayingPlayer_pb2.pyi │ │ │ ├── Origin.proto │ │ │ ├── OriginClientPropertiesMessage.proto │ │ │ ├── OriginClientPropertiesMessage_pb2.py │ │ │ ├── OriginClientPropertiesMessage_pb2.pyi │ │ │ ├── Origin_pb2.py │ │ │ ├── Origin_pb2.pyi │ │ │ ├── PlaybackQueue.proto │ │ │ ├── PlaybackQueueCapabilities.proto │ │ │ ├── PlaybackQueueCapabilities_pb2.py │ │ │ ├── PlaybackQueueCapabilities_pb2.pyi │ │ │ ├── PlaybackQueueContext.proto │ │ │ ├── PlaybackQueueContext_pb2.py │ │ │ ├── PlaybackQueueContext_pb2.pyi │ │ │ ├── PlaybackQueueRequestMessage.proto │ │ │ ├── PlaybackQueueRequestMessage_pb2.py │ │ │ ├── PlaybackQueueRequestMessage_pb2.pyi │ │ │ ├── PlaybackQueue_pb2.py │ │ │ ├── PlaybackQueue_pb2.pyi │ │ │ ├── PlayerClientPropertiesMessage.proto │ │ │ ├── PlayerClientPropertiesMessage_pb2.py │ │ │ ├── PlayerClientPropertiesMessage_pb2.pyi │ │ │ ├── PlayerPath.proto │ │ │ ├── PlayerPath_pb2.py │ │ │ ├── PlayerPath_pb2.pyi │ │ │ ├── ProtocolMessage.proto │ │ │ ├── ProtocolMessage_pb2.py │ │ │ ├── ProtocolMessage_pb2.pyi │ │ │ ├── RegisterForGameControllerEventsMessage.proto │ │ │ ├── RegisterForGameControllerEventsMessage_pb2.py │ │ │ ├── RegisterForGameControllerEventsMessage_pb2.pyi │ │ │ ├── RegisterHIDDeviceMessage.proto │ │ │ ├── RegisterHIDDeviceMessage_pb2.py │ │ │ ├── RegisterHIDDeviceMessage_pb2.pyi │ │ │ ├── RegisterHIDDeviceResultMessage.proto │ │ │ ├── RegisterHIDDeviceResultMessage_pb2.py │ │ │ ├── RegisterHIDDeviceResultMessage_pb2.pyi │ │ │ ├── RegisterVoiceInputDeviceMessage.proto │ │ │ ├── RegisterVoiceInputDeviceMessage_pb2.py │ │ │ ├── RegisterVoiceInputDeviceMessage_pb2.pyi │ │ │ ├── RegisterVoiceInputDeviceResponseMessage.proto │ │ │ ├── RegisterVoiceInputDeviceResponseMessage_pb2.py │ │ │ ├── RegisterVoiceInputDeviceResponseMessage_pb2.pyi │ │ │ ├── RemoteTextInputMessage.proto │ │ │ ├── RemoteTextInputMessage_pb2.py │ │ │ ├── RemoteTextInputMessage_pb2.pyi │ │ │ ├── RemoveClientMessage.proto │ │ │ ├── RemoveClientMessage_pb2.py │ │ │ ├── RemoveClientMessage_pb2.pyi │ │ │ ├── RemoveEndpointsMessage.proto │ │ │ ├── RemoveEndpointsMessage_pb2.py │ │ │ ├── RemoveEndpointsMessage_pb2.pyi │ │ │ ├── RemoveOutputDevicesMessage.proto │ │ │ ├── RemoveOutputDevicesMessage_pb2.py │ │ │ ├── RemoveOutputDevicesMessage_pb2.pyi │ │ │ ├── RemovePlayerMessage.proto │ │ │ ├── RemovePlayerMessage_pb2.py │ │ │ ├── RemovePlayerMessage_pb2.pyi │ │ │ ├── SendButtonEventMessage.proto │ │ │ ├── SendButtonEventMessage_pb2.py │ │ │ ├── SendButtonEventMessage_pb2.pyi │ │ │ ├── SendCommandMessage.proto │ │ │ ├── SendCommandMessage_pb2.py │ │ │ ├── SendCommandMessage_pb2.pyi │ │ │ ├── SendCommandResultMessage.proto │ │ │ ├── SendCommandResultMessage_pb2.py │ │ │ ├── SendCommandResultMessage_pb2.pyi │ │ │ ├── SendHIDEventMessage.proto │ │ │ ├── SendHIDEventMessage_pb2.py │ │ │ ├── SendHIDEventMessage_pb2.pyi │ │ │ ├── SendPackedVirtualTouchEventMessage.proto │ │ │ ├── SendPackedVirtualTouchEventMessage_pb2.py │ │ │ ├── SendPackedVirtualTouchEventMessage_pb2.pyi │ │ │ ├── SendVoiceInputMessage.proto │ │ │ ├── SendVoiceInputMessage_pb2.py │ │ │ ├── SendVoiceInputMessage_pb2.pyi │ │ │ ├── SetArtworkMessage.proto │ │ │ ├── SetArtworkMessage_pb2.py │ │ │ ├── SetArtworkMessage_pb2.pyi │ │ │ ├── SetConnectionStateMessage.proto │ │ │ ├── SetConnectionStateMessage_pb2.py │ │ │ ├── SetConnectionStateMessage_pb2.pyi │ │ │ ├── SetDefaultSupportedCommandsMessage.proto │ │ │ ├── SetDefaultSupportedCommandsMessage_pb2.py │ │ │ ├── SetDefaultSupportedCommandsMessage_pb2.pyi │ │ │ ├── SetDiscoveryModeMessage.proto │ │ │ ├── SetDiscoveryModeMessage_pb2.py │ │ │ ├── SetDiscoveryModeMessage_pb2.pyi │ │ │ ├── SetHiliteModeMessage.proto │ │ │ ├── SetHiliteModeMessage_pb2.py │ │ │ ├── SetHiliteModeMessage_pb2.pyi │ │ │ ├── SetNowPlayingClientMessage.proto │ │ │ ├── SetNowPlayingClientMessage_pb2.py │ │ │ ├── SetNowPlayingClientMessage_pb2.pyi │ │ │ ├── SetNowPlayingPlayerMessage.proto │ │ │ ├── SetNowPlayingPlayerMessage_pb2.py │ │ │ ├── SetNowPlayingPlayerMessage_pb2.pyi │ │ │ ├── SetRecordingStateMessage.proto │ │ │ ├── SetRecordingStateMessage_pb2.py │ │ │ ├── SetRecordingStateMessage_pb2.pyi │ │ │ ├── SetStateMessage.proto │ │ │ ├── SetStateMessage_pb2.py │ │ │ ├── SetStateMessage_pb2.pyi │ │ │ ├── SetVolumeMessage.proto │ │ │ ├── SetVolumeMessage_pb2.py │ │ │ ├── SetVolumeMessage_pb2.pyi │ │ │ ├── SupportedCommands.proto │ │ │ ├── SupportedCommands_pb2.py │ │ │ ├── SupportedCommands_pb2.pyi │ │ │ ├── TextInputMessage.proto │ │ │ ├── TextInputMessage_pb2.py │ │ │ ├── TextInputMessage_pb2.pyi │ │ │ ├── TransactionKey.proto │ │ │ ├── TransactionKey_pb2.py │ │ │ ├── TransactionKey_pb2.pyi │ │ │ ├── TransactionMessage.proto │ │ │ ├── TransactionMessage_pb2.py │ │ │ ├── TransactionMessage_pb2.pyi │ │ │ ├── TransactionPacket.proto │ │ │ ├── TransactionPacket_pb2.py │ │ │ ├── TransactionPacket_pb2.pyi │ │ │ ├── TransactionPackets.proto │ │ │ ├── TransactionPackets_pb2.py │ │ │ ├── TransactionPackets_pb2.pyi │ │ │ ├── UpdateClientMessage.proto │ │ │ ├── UpdateClientMessage_pb2.py │ │ │ ├── UpdateClientMessage_pb2.pyi │ │ │ ├── UpdateContentItemArtworkMessage.proto │ │ │ ├── UpdateContentItemArtworkMessage_pb2.py │ │ │ ├── UpdateContentItemArtworkMessage_pb2.pyi │ │ │ ├── UpdateContentItemMessage.proto │ │ │ ├── UpdateContentItemMessage_pb2.py │ │ │ ├── UpdateContentItemMessage_pb2.pyi │ │ │ ├── UpdateEndPointsMessage.proto │ │ │ ├── UpdateEndPointsMessage_pb2.py │ │ │ ├── UpdateEndPointsMessage_pb2.pyi │ │ │ ├── UpdateOutputDeviceMessage.proto │ │ │ ├── UpdateOutputDeviceMessage_pb2.py │ │ │ ├── UpdateOutputDeviceMessage_pb2.pyi │ │ │ ├── UpdatePlayerPath.proto │ │ │ ├── UpdatePlayerPath_pb2.py │ │ │ ├── UpdatePlayerPath_pb2.pyi │ │ │ ├── VirtualTouchDeviceDescriptorMessage.proto │ │ │ ├── VirtualTouchDeviceDescriptorMessage_pb2.py │ │ │ ├── VirtualTouchDeviceDescriptorMessage_pb2.pyi │ │ │ ├── VoiceInputDeviceDescriptorMessage.proto │ │ │ ├── VoiceInputDeviceDescriptorMessage_pb2.py │ │ │ ├── VoiceInputDeviceDescriptorMessage_pb2.pyi │ │ │ ├── VolumeControlAvailabilityMessage.proto │ │ │ ├── VolumeControlAvailabilityMessage_pb2.py │ │ │ ├── VolumeControlAvailabilityMessage_pb2.pyi │ │ │ ├── VolumeControlCapabilitiesDidChangeMessage.proto │ │ │ ├── VolumeControlCapabilitiesDidChangeMessage_pb2.py │ │ │ ├── VolumeControlCapabilitiesDidChangeMessage_pb2.pyi │ │ │ ├── VolumeDidChangeMessage.proto │ │ │ ├── VolumeDidChangeMessage_pb2.py │ │ │ ├── VolumeDidChangeMessage_pb2.pyi │ │ │ ├── WakeDeviceMessage.proto │ │ │ ├── WakeDeviceMessage_pb2.py │ │ │ ├── WakeDeviceMessage_pb2.pyi │ │ │ └── __init__.py │ │ ├── protocol.py │ │ └── server_auth.py │ └── raop │ │ ├── __init__.py │ │ ├── audio_source.py │ │ ├── fifo.py │ │ ├── packets.py │ │ ├── parsers.py │ │ ├── protocols │ │ ├── __init__.py │ │ ├── airplayv1.py │ │ └── airplayv2.py │ │ ├── stream_client.py │ │ └── timing.py ├── py.typed ├── scripts │ ├── __init__.py │ ├── atvlog.py │ ├── atvproxy.py │ ├── atvremote.py │ └── atvscript.py ├── settings.py ├── storage │ ├── __init__.py │ ├── file_storage.py │ └── memory_storage.py └── support │ ├── __init__.py │ ├── buffer.py │ ├── cache.py │ ├── chacha20.py │ ├── collections.py │ ├── device_info.py │ ├── dns.py │ ├── http.py │ ├── knock.py │ ├── metadata.py │ ├── net.py │ ├── opack.py │ ├── packet.py │ ├── pydantic_compat.py │ ├── rtsp.py │ ├── shield.py │ ├── state_producer.py │ ├── url.py │ └── variant.py ├── pylintrc ├── pyproject.toml ├── requirements ├── requirements.txt ├── requirements_docs.txt └── requirements_test.txt ├── scripts ├── api.py ├── audiogen.py ├── build_docs.sh ├── chickn.py ├── fake_device.py ├── features.py ├── protobuf.py ├── release.py ├── setup_dev_env.sh └── version.py ├── setup.cfg ├── setup.py └── tests ├── __init__.py ├── auth └── test_hap_tlv8.py ├── common_functional_tests.py ├── conftest.py ├── core ├── test_core.py ├── test_facade.py ├── test_mdns.py ├── test_mdns_functional.py ├── test_protocol.py ├── test_relayer.py └── test_scan.py ├── data ├── README ├── audio_10_frames.wav ├── audio_1_packet_metadata.wav ├── audio_3_packets.wav ├── only_metadata.wav ├── only_title.wav ├── static_3sec.ogg └── testfile.txt ├── fake_device ├── __init__.py ├── airplay.py ├── companion.py ├── dmap.py ├── mrp.py └── raop.py ├── fake_knock.py ├── fake_udns.py ├── protocols ├── airplay │ ├── auth │ │ ├── test_airplay_legacy_auth.py │ │ └── test_auth.py │ ├── conftest.py │ ├── test_airplay.py │ ├── test_airplay_interface.py │ ├── test_airplay_pair.py │ ├── test_airplay_player.py │ ├── test_airplay_scan.py │ ├── test_airplay_verify.py │ └── test_utils.py ├── companion │ ├── conftest.py │ ├── test_companion.py │ ├── test_companion_auth.py │ ├── test_companion_functional.py │ ├── test_companion_interface.py │ └── test_companion_scan.py ├── dmap │ ├── test_daap.py │ ├── test_dmap.py │ ├── test_dmap_functional.py │ ├── test_dmap_pairing.py │ ├── test_dmap_scan.py │ └── test_parser.py ├── mock_protocol.py ├── mrp │ ├── __init__.py │ ├── conftest.py │ ├── test_mrp.py │ ├── test_mrp_auth.py │ ├── test_mrp_functional.py │ ├── test_mrp_interface.py │ ├── test_mrp_scan.py │ ├── test_player_state.py │ └── test_protocol.py └── raop │ ├── conftest.py │ ├── test_fifo.py │ ├── test_parsers.py │ ├── test_raop.py │ ├── test_raop_functional.py │ └── test_raop_scan.py ├── scripts ├── conftest.py ├── test_atvremote.py └── test_atvscript.py ├── shared_helpers.py ├── storage └── test_memory_storage.py ├── support ├── dns_utils.py ├── pyatv.code-workspace ├── test_buffer.py ├── test_cache.py ├── test_chacha20.py ├── test_collections.py ├── test_device_info.py ├── test_dns.py ├── test_http.py ├── test_knock.py ├── test_metadata.py ├── test_net.py ├── test_opack.py ├── test_packet.py ├── test_shield.py ├── test_state_producer.py ├── test_support.py ├── test_url.py └── test_variant.py ├── test_conf.py ├── test_convert.py ├── test_helpers.py ├── test_interface.py ├── test_scan_functional.py ├── test_storage_functional.py ├── utils.py └── zeroconf_stub.py /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | branch: master 3 | 4 | coverage: 5 | range: "80..100" 6 | 7 | status: 8 | project: 9 | default: 10 | target: auto 11 | threshold: 5% 12 | 13 | flags: 14 | library: 15 | paths: 16 | - pyatv/ 17 | examples: 18 | paths: 19 | - examples/ 20 | configs: 21 | paths: 22 | - ".git*" 23 | - "*.yml" 24 | changelog: 25 | - CHANGES/ 26 | - CHANGES.md 27 | docs: 28 | paths: 29 | - docs/ 30 | - "*.md" 31 | - "*.rst" 32 | - "*.txt" 33 | scripts: 34 | paths: 35 | - scripts/ 36 | tests: 37 | paths: 38 | - tests/ 39 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = pyatv 3 | 4 | omit = 5 | pyatv/scripts/*.py 6 | pyatv/mrp/protobuf/*.py 7 | 8 | [report] 9 | # Regexes for lines to exclude from consideration 10 | exclude_lines = 11 | # Have to re-enable the standard pragma 12 | pragma: no cover 13 | 14 | # Don't complain about missing debug-only code: 15 | def __repr__ 16 | 17 | # Don't complain if tests don't hit defensive assertion code: 18 | raise AssertionError 19 | raise NotImplementedError 20 | raise exceptions.NotSupportedError 21 | 22 | # TYPE_CHECKING and @overload blocks are never executed during pytest run 23 | if TYPE_CHECKING: 24 | @overload 25 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [postlund] 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest an idea for this project 3 | labels: [feature] 4 | body: 5 | - type: textarea 6 | id: short-desc 7 | attributes: 8 | label: What feature would you like? 9 | validations: 10 | required: true 11 | - type: textarea 12 | id: solution 13 | attributes: 14 | label: Describe the solution you'd like 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: other 19 | attributes: 20 | label: Any other information to share? 21 | validations: 22 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/implementation-proposal.yml: -------------------------------------------------------------------------------- 1 | name: Implementation Proposal 2 | description: Suggest how a feature should be implemented 3 | labels: [feature] 4 | body: 5 | - type: textarea 6 | id: short-desc 7 | attributes: 8 | label: Short feature/function description 9 | validations: 10 | required: true 11 | - type: textarea 12 | id: to-be-done 13 | attributes: 14 | label: What needs to be done? 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: breaking-change 19 | attributes: 20 | label: Is this a breaking change? 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: other 25 | attributes: 26 | label: Anything else worth knowing? 27 | validations: 28 | required: true 29 | 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/investigation.yml: -------------------------------------------------------------------------------- 1 | name: Investigation 2 | description: Use for investigations or if something is unclear before creating an implementation proposal 3 | labels: [investigate] 4 | body: 5 | - type: textarea 6 | id: to-investigate 7 | attributes: 8 | label: What to investigate? 9 | validations: 10 | required: true 11 | - type: textarea 12 | id: expected-outcome 13 | attributes: 14 | label: Expected outcome 15 | validations: 16 | required: true 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/minor-change.yml: -------------------------------------------------------------------------------- 1 | name: Minor Change 2 | description: Use this to suggest minor changes to code or documentation. 3 | body: 4 | - type: textarea 5 | id: minor-change 6 | attributes: 7 | label: What to change? 8 | description: Please describe in as much detail as possible what to change. 9 | validations: 10 | required: true 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question-or-idea.yml: -------------------------------------------------------------------------------- 1 | name: Question or support 2 | description: If you are using pyatv and have questions regarding how to use it in any way (e.g. development), use this template. 3 | labels: [question] 4 | body: 5 | - type: textarea 6 | id: question-support 7 | attributes: 8 | label: What do you need help with? 9 | description: Please enter your question below. Make sure to include software versions, error logs, etc. when applicable. The more information you provide, the easier it will be to help. 10 | validations: 11 | required: true 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/requirements" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | reviewers: 10 | - postlund 11 | assignees: 12 | - postlund 13 | labels: 14 | - dependencies 15 | - package-ecosystem: docker 16 | directory: / 17 | schedule: 18 | interval: daily 19 | time: "04:00" 20 | open-pull-requests-limit: 3 21 | reviewers: 22 | - postlund 23 | assignees: 24 | - postlund 25 | labels: 26 | - dependencies 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # File based on .gitignore from home-assistant.io 2 | 3 | # Hide sublime text stuff 4 | *.sublime-project 5 | *.sublime-workspace 6 | 7 | # Hide some OS X stuff 8 | .DS_Store 9 | .AppleDouble 10 | .LSOverride 11 | Icon 12 | 13 | # Thumbnails 14 | ._* 15 | 16 | .idea 17 | 18 | # pytest 19 | .cache 20 | 21 | # GITHUB Proposed Python stuff: 22 | *.py[cod] 23 | 24 | # C extensions 25 | *.so 26 | 27 | # Packages 28 | *.egg 29 | *.egg-info 30 | dist 31 | build 32 | eggs 33 | .eggs 34 | parts 35 | bin 36 | var 37 | sdist 38 | develop-eggs 39 | .installed.cfg 40 | lib 41 | lib64 42 | include 43 | 44 | # Logs 45 | *.log 46 | pip-log.txt 47 | 48 | # Unit test / coverage reports 49 | .coverage* 50 | .tox 51 | nosetests.xml 52 | 53 | # Translations 54 | *.mo 55 | 56 | # Mr Developer 57 | .mr.developer.cfg 58 | .project 59 | .pydevproject 60 | 61 | .python-version 62 | 63 | # emacs auto backups 64 | *~ 65 | *# 66 | *.orig 67 | 68 | # venv stuff 69 | pyvenv.cfg 70 | pip-selfcheck.json 71 | venv 72 | .venv 73 | 74 | # vimmy stuff 75 | *.swp 76 | *.swo 77 | 78 | ctags.tmp 79 | 80 | # vagrant stuff 81 | virtualization/vagrant/setup_done 82 | virtualization/vagrant/.vagrant 83 | virtualization/vagrant/config 84 | 85 | # Visual Studio Code 86 | .vscode 87 | 88 | # Windows Explorer 89 | desktop.ini 90 | 91 | # Other directories 92 | share 93 | htmlcov 94 | 95 | artwork.png 96 | .Python 97 | .pytest_cache 98 | .mypy_cache 99 | coverage.xml 100 | 101 | # Temporary stuff can be placed here (logs, credentials, etc.) 102 | work/ -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: gitpod/workspace-full 2 | tasks: 3 | - name: Documentation 4 | command: ./scripts/build_docs.sh 5 | - name: Development 6 | before: "echo 'export PIP_USER=no' >> ~/.bashrc && export PIP_USER=no" 7 | init: ./scripts/setup_dev_env.sh && source bin/activate; python scripts/chickn.py 8 | command: source bin/activate 9 | 10 | ports: 11 | - port: 4000 # Used for documentation 12 | onOpen: open-preview 13 | visibility: private 14 | 15 | vscode: 16 | extensions: 17 | - ms-python.python 18 | - hbenl.test-adapter-converter 19 | - hbenl.vscode-test-explorer 20 | 21 | github: 22 | prebuilds: 23 | addBadge: true 24 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | See [this page](https://pyatv.dev/support/acknowledgements/#contributors) 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.13.2-alpine 2 | ARG VERSION 3 | 4 | WORKDIR . 5 | COPY dist/pyatv-${VERSION}-py3-none-any.whl . 6 | COPY requirements/requirements.txt . 7 | 8 | RUN apk add gcc musl-dev build-base linux-headers libffi-dev rust cargo openssl-dev git && \ 9 | pip install setuptools-rust && \ 10 | pip install -r requirements.txt && \ 11 | pip install pyatv-${VERSION}-py3-none-any.whl && \ 12 | apk del gcc musl-dev build-base linux-headers libffi-dev rust cargo openssl-dev git && \ 13 | rm pyatv-${VERSION}-py3-none-any.whl && \ 14 | rm requirements.txt && \ 15 | rm -rf /root/.cache /root/.cargo 16 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Pierre Ståhl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include CHANGES.md 2 | include README.md 3 | include LICENSE.md 4 | include CONTRIBUTORS.md 5 | include base_versions.txt 6 | graft pyatv 7 | graft tests 8 | graft docs 9 | graft examples 10 | global-exclude *~ __pycache__ 11 | recursive-exclude * *.py[co] .* 12 | prune docs/vendor 13 | prune docs/_site 14 | prune docs/.sass-cache 15 | -------------------------------------------------------------------------------- /base_versions.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.8.3,<5 2 | async-timeout==4.0.2 3 | cryptography==44.0.1 4 | chacha20poly1305-reuseable==0.13.2 5 | ifaddr==0.1.7 6 | miniaudio==1.45 7 | protobuf==6.30.2 8 | pydantic==1.10.10 9 | requests==2.30.0 10 | srptools==0.2.0 11 | tabulate==0.9.0 12 | tinytag==1.10.0 13 | zeroconf==0.129.0 14 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .jekyll-metadata 4 | vendor 5 | Gemfile.lock -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: template 3 | --- 4 | 5 | 18 | 19 |
20 |

404

21 | 22 |

Page not found :(

23 |

The requested page could not be found.

24 |
25 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | pyatv.dev -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Hello! This is where you manage which Jekyll version is used to run. 4 | # When you want to use a different version, change it below, save the 5 | # file and run `bundle install`. Run Jekyll with `bundle exec`, like so: 6 | # 7 | # bundle exec jekyll serve 8 | # 9 | # This will help ensure the proper Jekyll version is running. 10 | # Happy Jekylling! 11 | 12 | # If you want to use GitHub Pages, remove the "gem "jekyll"" above and 13 | # uncomment the line below. To upgrade, run `bundle update github-pages`. 14 | gem "github-pages", group: :jekyll_plugins 15 | 16 | # If you have any plugins, put them here! 17 | group :jekyll_plugins do 18 | gem "jekyll-feed", "~> 0.6" 19 | gem "jemoji" 20 | end 21 | 22 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 23 | # and associated library. 24 | install_if -> { RUBY_PLATFORM =~ %r!mingw|mswin|java! } do 25 | gem "tzinfo", "~> 1.2" 26 | gem "tzinfo-data" 27 | end 28 | 29 | # Performance-booster for watching directories on Windows 30 | gem "wdm", "~> 0.1.0", :install_if => Gem.win_platform? 31 | 32 | -------------------------------------------------------------------------------- /docs/_includes/api: -------------------------------------------------------------------------------- 1 | {%- assign module = include.i | split: "." | first -%} 2 | {%- if module == "pyatv" -%} 3 | {%- assign module = "" -%} 4 | {%- assign path = "#" | append: include.i | replace: "/", "." -%} 5 | {%- else -%} 6 | {%- assign path = "#pyatv." | append: include.i | replace: "/", "." -%} 7 | {%- endif -%} 8 | {%- assign url = '/api/' | append: module | append: path | relative_url -%} 9 | {%- assign path = include.i | replace: "/", "." -%} 10 | {{ 'PATH' | replace: "URL", url | replace: "PATH", path }} -------------------------------------------------------------------------------- /docs/_includes/atvremote_scan: -------------------------------------------------------------------------------- 1 | ```raw 2 | $ atvremote acan 3 | Scan Results 4 | ======================================== 5 | Name: Living Room 6 | Model/SW: Apple TV 4K, tvOS 16.6 build 20M73 7 | Address: 10.0.0.5 8 | MAC: AA:BB:CC:DD:EE:FF 9 | Deep Sleep: True 10 | Identifiers: 11 | - 01234567-89AB-CDEF-0123-4567890ABCDE 12 | - AA:BB:CC:DD:EE:FF 13 | - 01234567-89AB-CDEF-0123-AAAAAAAAAAAA 14 | - AABBCCDDEEFF 15 | Services: 16 | - Protocol: Companion, Port: 49153, Credentials:None, Requires Password: False, Password: None, Pairing: Mandatory 17 | - Protocol: AirPlay, Port: 7000, Credentials: None, Requires Password: False, Password: None, Pairing: Mandatory 18 | - Protocol: MRP, Port: 49154, Credentials: None, Requires Password: False, Password: None, Pairing: NotNeeded (Disabled) 19 | - Protocol: RAOP, Port: 7000, Credentials: None, Requires Password: False, Password: None, Pairing: Mandatory 20 | 21 | Name: Pierre's AirPort Express 22 | Model/SW: AirPort Express (gen 2), AirPortOS 7.8.1 23 | Address: 10.0.0.6 24 | MAC: BB:BB:BB:BB:BB:BB 25 | Deep Sleep: False 26 | Identifiers: 27 | - BB:BB:BB:BB:BB:BB 28 | - BBBBBBBBBBBB 29 | Services: 30 | - Protocol: AirPlay, Port: 7000, Credentials: None, Requires Password: False, Password: None, Pairing: NotNeeded 31 | - Protocol: RAOP, Port: 7000, Credentials: None, Requires Password: False, Password: None, Pairing: NotNeeded 32 | ``` -------------------------------------------------------------------------------- /docs/_includes/code: -------------------------------------------------------------------------------- 1 | {%- assign file = include.file -%} 2 | {%- assign name = file | replace: "_", "\_" | replace: "../", "" -%} 3 | {%- capture link -%} 4 | {{ name }} 5 | {%- endcapture -%} 6 | {{ link }} -------------------------------------------------------------------------------- /docs/_includes/issue: -------------------------------------------------------------------------------- 1 | {%- assign numbers = include.no | split: "," -%} 2 | {%- assign output = Nil -%} 3 | {%- for number in numbers -%} 4 | {%- capture link -%} 5 | #{{ number }} 6 | {%- endcapture -%} 7 | 8 | {%- if output == Nil -%} 9 | {%- assign output = link -%} 10 | {%- else -%} 11 | {%- assign output = output | append: ", " | append: link -%} 12 | {%- endif -%} 13 | {%- endfor -%} 14 | [{{ output }}] -------------------------------------------------------------------------------- /docs/_includes/pypi: -------------------------------------------------------------------------------- 1 | {%- assign package = include.package -%} 2 | {%- capture link -%} 3 | {{ package }} 4 | {%- endcapture -%} 5 | {{ link }} -------------------------------------------------------------------------------- /docs/assets/css/custom.css: -------------------------------------------------------------------------------- 1 | .center_box { 2 | margin: 0 auto; 3 | display: table; 4 | border: 1px solid #606c71; 5 | background: #f3f6fa; 6 | padding: 1em 3em 1em 3em; 7 | border-radius: 5px; 8 | font-weight: bold; 9 | text-align: center 10 | } 11 | 12 | .center_box p { 13 | margin: 0px 14 | } 15 | 16 | .api_feature { 17 | font-style: italic; 18 | margin-bottom: 1em; 19 | } -------------------------------------------------------------------------------- /docs/assets/css/hljs.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:0.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:bold}.hljs-number,.hljs-literal,.hljs-variable,.hljs-template-variable,.hljs-tag .hljs-attr{color:#008080}.hljs-string,.hljs-doctag{color:#d14}.hljs-title,.hljs-section,.hljs-selector-id{color:#900;font-weight:bold}.hljs-subst{font-weight:normal}.hljs-type,.hljs-class .hljs-title{color:#458;font-weight:bold}.hljs-tag,.hljs-name,.hljs-attribute{color:#000080;font-weight:normal}.hljs-regexp,.hljs-link{color:#009926}.hljs-symbol,.hljs-bullet{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:bold}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} -------------------------------------------------------------------------------- /docs/assets/css/style.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | @import "{{ site.theme }}"; 5 | @import "sanitize.css"; 6 | @import "normalize.css"; 7 | @import "hljs.css"; 8 | @import "pdoc.css"; 9 | @import "custom.css"; -------------------------------------------------------------------------------- /docs/development/audio.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: template 3 | title: Audio 4 | permalink: /development/audio/ 5 | link_group: development 6 | --- 7 | # Audio 8 | 9 | Protocols supporting volume controls can be controlled via the audio interface. 10 | 11 | ## Using the Audio API 12 | 13 | After connecting to a device, you get the apps interface via {% include api i="interface.AppleTV.audio" %}: 14 | 15 | ```python 16 | atv = await pyatv.connect(config, ...) 17 | audio = atv.audio 18 | ``` 19 | 20 | To get current volume level, use {% include api i="interface.Audio.volume" %}: 21 | 22 | ```python 23 | print("Volume:", audio.volume) 24 | ``` 25 | 26 | To change current volume, use {% include api i="interface.Audio.set_volume" %}: 27 | 28 | ```python 29 | await audio.set_volume(20.0) 30 | ``` 31 | 32 | The volume level is normalized in the interval 0.0-100.0, where 0.0 means 33 | the audio is muted. 34 | 35 | You can also step volume up or down using step level provided from the device (if available): 36 | 37 | ```python 38 | await audio.volume_up() 39 | await audio.volume_down() 40 | ``` 41 | 42 | The audio API supports push updates via a listener, as described [here](../listeners#audio-updates). 43 | -------------------------------------------------------------------------------- /docs/development/control.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: template 3 | title: Control 4 | permalink: /development/control/ 5 | link_group: development 6 | --- 7 | # Control 8 | 9 | Controlling a device is done with the remote control interface, 10 | {% include api i="interface.RemoteControl" %}. It allows you navigate the menus and 11 | change playback (play, pause, etc.). 12 | 13 | ## Using the Remote Control API 14 | 15 | After connecting to a device, you get the remote control via {% include api i="interface.AppleTV.remote_control" %}: 16 | 17 | ```python 18 | atv = await pyatv.connect(config, ...) 19 | rc = atv.remote_control 20 | ``` 21 | 22 | You can then control via the available functions: 23 | 24 | ```python 25 | await rc.up() 26 | await rc.select() 27 | await rc.volume_up() 28 | await rc.set_position(100) 29 | ``` 30 | 31 | All available actions can be found in {% include api i="interface.RemoteControl" %}. 32 | 33 | ## Input Actions 34 | 35 | Currently three types of input actions are supported: 36 | 37 | * Single tap ("click") 38 | * Double tap ("double click") 39 | * Hold 40 | 41 | These actions are supported by the following buttons: 42 | 43 | * Arrow keys (up, down, left, right) 44 | * Select 45 | * Menu 46 | * Home 47 | 48 | By default, {% include api i="const.InputAction.SingleTap" %} are used. Pass another `action` 49 | to use another input action: 50 | 51 | ```python 52 | await rc.menu(action=InputAction.Hold) 53 | await rc.home(action=InputAction.DoubleTap) 54 | ``` 55 | 56 | All input actions are specified in {% include api i="const.InputAction" %}. -------------------------------------------------------------------------------- /docs/development/development.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: template 3 | title: Development 4 | permalink: /development/ 5 | link_group: development 6 | --- 7 | # :construction_worker: Development 8 | 9 | These sections cover the basics on how you develop with pyatv. 10 | So if you are a developer and want to create some software that 11 | controls an Apple TV, you are in the right place. 12 | 13 | If you instead want develop pyatv itself, there are details for 14 | that over at the GitHub wiki. Click 15 | [here](https://github.com/postlund/pyatv/wiki) to go there. 16 | -------------------------------------------------------------------------------- /docs/development/device_info.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: template 3 | title: Device Information 4 | permalink: /development/device_info/ 5 | link_group: development 6 | --- 7 | # Device Information 8 | 9 | pyatv can extract various information about a device, e.g. which 10 | operating system (and version) it runs or its hardware model (3, 4K, etc.). 11 | This information is exposed via the interface {% include api i="interface.DeviceInfo" %}. 12 | 13 | ## Using the Device Information API 14 | 15 | After connecting to a device, you get device info via {% include api i="interface.AppleTV.device_info" %}: 16 | 17 | ```python 18 | atv = await pyatv.connect(config, ...) 19 | devinfo = atv.device_info 20 | ``` 21 | 22 | You can then access the actual information via properties: 23 | 24 | ```python 25 | print(devinfo.operating_system) 26 | print(devinfo.version) 27 | print(devinfo.mac) 28 | ``` 29 | 30 | Just printing `devinfo` will produce a summary of the device information 31 | (MAC-address is not included here): 32 | 33 | ```python 34 | >>> print(devinfo) 35 | 4K tvOS 13.3.1 build 17K795 36 | ``` -------------------------------------------------------------------------------- /docs/development/keyboard.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: template 3 | title: Keyboard 4 | permalink: /development/keyboard/ 5 | link_group: development 6 | --- 7 | # Keyboard 8 | 9 | It is possible to interact with the Apple TV virtual keyboard via the Keyboard interface. 10 | To use this interface, the Companion protocol must be available. 11 | 12 | ## Using the Keyboard API 13 | 14 | After connecting to a device, you get the keyboard interface via {% include api i="interface.AppleTV.keyboard" %}: 15 | 16 | ```python 17 | atv = await pyatv.connect(config, ...) 18 | keyboard = atv.keyboard 19 | ``` 20 | 21 | To check whether the virtual keyboard is focused and active, use {% include api i="interface.Keyboard.text_focus_state" %}: 22 | 23 | ```python 24 | print("Keyboard focus state:", keyboard.text_focus_state) 25 | ``` 26 | 27 | To fetch the current virtual keyboard text content, use {% include api i="interface.Keyboard.text_get" %}: 28 | 29 | ```python 30 | print("Keyboard text:", await keyboard.text_get()) 31 | ``` 32 | 33 | To set (replace) the virtual keyboard text, use {% include api i="interface.Keyboard.text_set" %}: 34 | 35 | ```python 36 | await keyboard.text_set("text to set") 37 | ``` 38 | 39 | To append to the virtual keyboard text, use {% include api i="interface.Keyboard.text_append" %}: 40 | 41 | ```python 42 | await keyboard.text_append("text to append") 43 | ``` 44 | 45 | Finally, to clear the virtual keyboard text, use {% include api i="interface.Keyboard.text_clear" %}: 46 | 47 | ```python 48 | await keyboard.text_clear() 49 | ``` 50 | 51 | The keyboard API supports push updates via a listener, as described [here](../listeners#keyboard-updates). 52 | -------------------------------------------------------------------------------- /docs/development/logging.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: template 3 | title: Logging 4 | permalink: /development/logging/ 5 | link_group: development 6 | --- 7 | # Logging 8 | 9 | In case you need to troubleshoot something, you can enable additional log points in pyatv. 10 | This page describes how you do that. 11 | 12 | # Log points 13 | 14 | To enable full logging, use this: 15 | 16 | ```python 17 | logging.basicConfig( 18 | level=logging.DEBUG, 19 | datefmt="%Y-%m-%d %H:%M:%S", 20 | format="%(asctime)s %(levelname)s [%(name)s]: %(message)s", 21 | ) 22 | ``` 23 | 24 | This output format is preferred as it is compatible with [atvlog](../../documentation/atvlog) and 25 | contains the most useful information. 26 | 27 | In case you need to troubleshoot MDNS/Zeroconf traffic, use this: 28 | 29 | ```python 30 | logging.getLogger( 31 | "pyatv.support.mdns" 32 | ).level = logging.TRAFFIC # pylint: disable=no-member 33 | ``` 34 | 35 | *NOTE: This section is WIP for now as most interesting log points are internal. In the future, 36 | a `log` module will be added to simplify enabling log points.* 37 | 38 | # Bundled scripts 39 | 40 | You can enable additional debugging information by specifying either `--verbose` or `--debug.`. 41 | 42 | # Output line cropping 43 | 44 | By default pyatv will limit some log points in length, mainly due to an excessive amount of 45 | data might be logged otherwise. This mainly applies to binary data (raw protocol data) and 46 | protobuf messages. These limits can be overridden by setting the following environment variables: 47 | 48 | ```shell 49 | $ export PYATV_BINARY_MAX_LINE=1000 50 | $ export PYATV_PROTOBUF_MAX_LINE=1000 51 | $ atvremote --debug ... playing 52 | ``` 53 | 54 | In general, you shouldn't have to change these, but under some cicrumstances the complete 55 | logs might be deseriable. 56 | -------------------------------------------------------------------------------- /docs/development/power_management.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: template 3 | title: Power Management 4 | permalink: /development/power_management/ 5 | link_group: development 6 | --- 7 | # Power Management 8 | 9 | Power management of a device is done with the power interface, 10 | {% include api i="interface.Power" %}. It allows you to turn on, turn off and get current 11 | power state. This interface is currently only supported by devices running tvOS. 12 | 13 | ## Using the Power Management API 14 | 15 | After connecting to a device, you get the power management via {% include api i="interface.AppleTV.power" %}: 16 | 17 | ```python 18 | atv = await pyatv.connect(config, ...) 19 | pwrc = atv.power 20 | ``` 21 | 22 | You can then control via the available functions: 23 | 24 | ```python 25 | await pwrc.turn_on() 26 | await pwrc.turn_off() 27 | ``` 28 | 29 | To get current power state use following property: 30 | 31 | ```python 32 | @property 33 | def power_state(self) -> const.PowerState: 34 | ``` 35 | 36 | ## Waiting for State Change 37 | 38 | It is possible to pass `await_new_state` set to `True` when turning on 39 | or off a device to have pyatv wait for a state change. E.g. calling 40 | {% include api i="interface.Power.turn_off" %} will block until the device 41 | has powered off: 42 | 43 | ```python 44 | await pwrc.turn_off(await_new_state=True) 45 | ``` 46 | 47 | If the device is already off, it will return immediately. 48 | 49 | To not block indefinitely, use `wait_for` with a timeout: 50 | 51 | ```python 52 | await asyncio.wait_for(pwrc.turn_off(await_new_state=True), timeout=5) 53 | ``` 54 | -------------------------------------------------------------------------------- /docs/development/services.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: template 3 | title: Services 4 | permalink: /development/services/ 5 | link_group: development 6 | --- 7 | # Services 8 | 9 | This page has not yet been migrated to GitHub Pages. Come back later... -------------------------------------------------------------------------------- /docs/development/testing.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: template 3 | title: Services 4 | permalink: /development/testing/ 5 | link_group: development 6 | --- 7 | # Testing 8 | 9 | This page has not yet been migrated to GitHub Pages. Come back later... -------------------------------------------------------------------------------- /docs/documentation/atvlog.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: template 3 | title: atvlog 4 | permalink: /documentation/atvlog/ 5 | link_group: documentation 6 | --- 7 | # Table of Contents 8 | {:.no_toc} 9 | * TOC 10 | {:toc} 11 | 12 | # atvlog 13 | 14 | The `atvlog` script simplifies log inspection by generating an HTML file with basic 15 | live filtering capabilities. 16 | 17 | *Note: This is an incubating script and may change behavior with short notice.* 18 | 19 | # Features 20 | 21 | Log output from the following tools are supported as input: 22 | 23 | * atvremote and atvscript 24 | * Home Assistant log 25 | 26 | A special `markdown` mode is supported, which extracts a log from the following 27 | format: 28 | 29 | ~~~ 30 | text here is ignored 31 | ```log 32 | log data here 33 | ``` 34 | also ignored 35 | ~~~ 36 | 37 | Filtering can be performed on the following attributes: 38 | 39 | * Include entries based on regexp 40 | * Exclude entries based on regexp (performed prior to include regexp) 41 | * Log levels 42 | * Date can be stripped for more compact log 43 | 44 | # Serving output via web server 45 | 46 | It is possible to serve the generated log output via a built in web server using flag `-w`: 47 | 48 | ```shell 49 | $ atvlog -w pyatv.log 50 | Press ENTER to quit 51 | ``` 52 | 53 | Then visit `http://:8008` to see the log. The port number can be changed 54 | with `-p xxxx`. 55 | 56 | # Examples 57 | 58 | ```shell 59 | $ atvlog pyatv.log # Print output to stdout 60 | $ atvlog --output pyatv.html pyatv.log 61 | $ cat pyatv.log | atvlog - # Read from stdin 62 | $ cat markdown.log | atvlog --format=markdown - 63 | ``` 64 | 65 | -------------------------------------------------------------------------------- /docs/documentation/workspace.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "../.." 5 | } 6 | ] 7 | } -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/postlund/pyatv/2fbdbd444b80048876e2ab6726ac945901e883b4/docs/favicon.ico -------------------------------------------------------------------------------- /docs/support/support.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: template 3 | title: Support 4 | permalink: /support/ 5 | link_group: support 6 | --- 7 | # :fire: Support 8 | 9 | If you are having problems or questions, this section aims to help you. 10 | Check out the subpages above. 11 | 12 | In case you have found a potential bug or need help, please write issues on GitHub: 13 | 14 | **:bug: [I have found a bug](https://github.com/postlund/pyatv/issues/new?assignees=&labels=bug&template=bug_report.md&title=)** 15 | 16 | **:raised_hands: [I need help](https://github.com/postlund/pyatv/issues/new?assignees=&labels=question&template=question-or-idea.md&title=)** 17 | 18 | **:question: [Something is missing...](https://github.com/postlund/pyatv/issues/new?assignees=&labels=feature&template=feature_request.md&title=)** -------------------------------------------------------------------------------- /examples/__init__.py: -------------------------------------------------------------------------------- 1 | """Package with pyatv examples.""" 2 | -------------------------------------------------------------------------------- /examples/auto_connect.py: -------------------------------------------------------------------------------- 1 | """Simple example that connects to a device with autodiscover.""" 2 | 3 | import asyncio 4 | 5 | from pyatv import helpers 6 | 7 | 8 | # Method that is dispatched by the asyncio event loop 9 | async def print_what_is_playing(atv): 10 | """Print what is playing for the discovered device.""" 11 | playing = await atv.metadata.playing() 12 | print("Currently playing:") 13 | print(playing) 14 | 15 | 16 | asyncio.get_event_loop().run_until_complete(helpers.auto_connect(print_what_is_playing)) 17 | -------------------------------------------------------------------------------- /examples/connect_with_credentials.py: -------------------------------------------------------------------------------- 1 | """Find a specific device, restore credentials and connect. 2 | 3 | Call script like this: 4 | 5 | python connect_with_credentials.py 0 6 | 7 | This connect to the first device in DEVICES (index 0). If you add 8 | another device, use 1 instead of 0 and so on. You can easily 9 | look up based on name as well (not demonstrated here). 10 | """ 11 | 12 | import asyncio 13 | import sys 14 | 15 | import pyatv 16 | from pyatv.const import Protocol 17 | 18 | LOOP = asyncio.get_event_loop() 19 | 20 | # This can be stored in a file for instance 21 | DEVICES = [ 22 | { 23 | "name": "Living Room", 24 | "identifiers": {"aabbccddeeff", "123456789"}, 25 | "credentials": { 26 | Protocol.AirPlay: "abcdef", 27 | Protocol.Companion: "foobar", 28 | Protocol.RAOP: "123456", 29 | }, 30 | }, 31 | # Add more devices here 32 | ] 33 | 34 | 35 | async def print_what_is_playing(device, loop): 36 | """Find a device and print what is playing.""" 37 | print(f"Discovering {device['name']} on network...") 38 | confs = await pyatv.scan(loop, identifier=device["identifiers"]) 39 | 40 | if not confs: 41 | print("Device could not be found", file=sys.stderr) 42 | return 43 | 44 | conf = confs[0] 45 | for protocol, credentials in device["credentials"].items(): 46 | conf.set_credentials(protocol, credentials) 47 | 48 | print(f"Connecting to {conf.address}") 49 | atv = await pyatv.connect(conf, loop) 50 | 51 | try: 52 | playing = await atv.metadata.playing() 53 | print("Currently playing:") 54 | print(playing) 55 | finally: 56 | await asyncio.gather(*atv.close()) 57 | 58 | 59 | if __name__ == "__main__": 60 | LOOP.run_until_complete(print_what_is_playing(DEVICES[int(sys.argv[1])], LOOP)) 61 | -------------------------------------------------------------------------------- /examples/manual_connect.py: -------------------------------------------------------------------------------- 1 | """Simple example that shows how to manually connect to an Apple TV.""" 2 | 3 | import asyncio 4 | 5 | from pyatv import conf, connect 6 | from pyatv.const import Protocol 7 | 8 | # Enter config used to connect 9 | NAME = "My Apple TV" 10 | ADDRESS = "10.0.10.22" 11 | HSGID = "00000000-1111-2222-3333-444444444444" 12 | 13 | LOOP = asyncio.get_event_loop() 14 | 15 | 16 | # Method that is dispatched by the asyncio event loop 17 | async def print_what_is_playing(loop): 18 | """Connect to device and print what is playing.""" 19 | config = conf.AppleTV(ADDRESS, NAME) 20 | config.add_service( 21 | conf.ManualService("some_id", Protocol.DMAP, 3689, {}, credentials=HSGID) 22 | ) 23 | 24 | print(f"Connecting to {config.address}") 25 | atv = await connect(config, loop) 26 | 27 | try: 28 | print(await atv.metadata.playing()) 29 | finally: 30 | # Do not forget to close 31 | atv.close() 32 | 33 | 34 | if __name__ == "__main__": 35 | # Setup event loop and connect 36 | LOOP.run_until_complete(print_what_is_playing(LOOP)) 37 | -------------------------------------------------------------------------------- /examples/pairing.py: -------------------------------------------------------------------------------- 1 | """Simple example showing of pairing.""" 2 | 3 | import asyncio 4 | import sys 5 | 6 | from pyatv import pair, scan 7 | from pyatv.const import Protocol 8 | 9 | LOOP = asyncio.get_event_loop() 10 | 11 | 12 | # Method that is dispatched by the asyncio event loop 13 | async def pair_with_device(loop): 14 | """Make it possible to pair with device.""" 15 | atvs = await scan(loop, timeout=5, protocol=Protocol.AirPlay) 16 | 17 | if not atvs: 18 | print("No device found", file=sys.stderr) 19 | return 20 | 21 | pairing = await pair(atvs[0], Protocol.MRP, loop) 22 | await pairing.begin() 23 | 24 | pin = int(input("Enter PIN: ")) 25 | pairing.pin(pin) 26 | await pairing.finish() 27 | 28 | # Give some feedback about the process 29 | if pairing.has_paired: 30 | print("Paired with device!") 31 | print("Credentials:", pairing.service.credentials) 32 | else: 33 | print("Did not pair with device!") 34 | 35 | await pairing.close() 36 | 37 | 38 | if __name__ == "__main__": 39 | # Setup event loop and connect 40 | LOOP.run_until_complete(pair_with_device(LOOP)) 41 | -------------------------------------------------------------------------------- /examples/play_url.py: -------------------------------------------------------------------------------- 1 | """Example of playing a video file via AirPlay. 2 | 3 | python play_url.py 4 | """ 5 | 6 | import asyncio 7 | import sys 8 | 9 | import pyatv 10 | from pyatv.const import Protocol 11 | 12 | 13 | async def play_url( 14 | device_id: str, airplay_credentials: str, url: str, loop: asyncio.AbstractEventLoop 15 | ): 16 | """Connect to an Apple TV and stream file via AirPlay.""" 17 | print("* Discovering device on network...") 18 | atvs = await pyatv.scan(loop, identifier=device_id) 19 | if not atvs: 20 | print("* Device found", file=sys.stderr) 21 | return 22 | 23 | conf = atvs[0] 24 | conf.set_credentials(Protocol.AirPlay, airplay_credentials) 25 | atv = await pyatv.connect(conf, loop) 26 | 27 | try: 28 | print(f"* Streaming {url} to {conf.address}") 29 | await atv.stream.play_url(url) 30 | finally: 31 | await atv.close() 32 | 33 | 34 | if __name__ == "__main__": 35 | asyncio.get_event_loop().run_until_complete( 36 | play_url(sys.argv[1], sys.argv[2], sys.argv[3], asyncio.get_event_loop()) 37 | ) 38 | -------------------------------------------------------------------------------- /examples/scan_and_connect.py: -------------------------------------------------------------------------------- 1 | """Simple example that scans for devices and connects to first one found.""" 2 | 3 | import asyncio 4 | import sys 5 | 6 | import pyatv 7 | 8 | LOOP = asyncio.get_event_loop() 9 | 10 | 11 | # Method that is dispatched by the asyncio event loop 12 | async def print_what_is_playing(loop): 13 | """Find a device and print what is playing.""" 14 | print("Discovering devices on network...") 15 | atvs = await pyatv.scan(loop, timeout=5) 16 | 17 | if not atvs: 18 | print("No device found", file=sys.stderr) 19 | return 20 | 21 | print(f"Connecting to {atvs[0].address}") 22 | atv = await pyatv.connect(atvs[0], loop) 23 | 24 | try: 25 | playing = await atv.metadata.playing() 26 | print("Currently playing:") 27 | print(playing) 28 | finally: 29 | # Do not forget to close 30 | atv.close() 31 | 32 | 33 | if __name__ == "__main__": 34 | # Setup event loop and connect 35 | LOOP.run_until_complete(print_what_is_playing(LOOP)) 36 | -------------------------------------------------------------------------------- /examples/storage.py: -------------------------------------------------------------------------------- 1 | """Simple example using file based storage.""" 2 | 3 | import asyncio 4 | import sys 5 | 6 | from pyatv import connect, scan 7 | from pyatv.storage.file_storage import FileStorage 8 | 9 | LOOP = asyncio.get_event_loop() 10 | 11 | 12 | async def connect_with_storage(host): 13 | """Connect to a device using a storage.""" 14 | loop = asyncio.get_event_loop() 15 | 16 | # Load the same storage that pyatv uses internally (e.g. in atvremote) 17 | storage = FileStorage.default_storage(loop) 18 | await storage.load() 19 | 20 | atvs = await scan(loop, timeout=5, hosts=[host], storage=storage) 21 | 22 | if not atvs: 23 | print("Device not found", file=sys.stderr) 24 | return 25 | 26 | atv = await connect(atvs[0], loop, storage=storage) 27 | print(await atv.metadata.playing()) 28 | 29 | atv.close() 30 | 31 | 32 | if __name__ == "__main__": 33 | asyncio.run(connect_with_storage(sys.argv[1])) 34 | -------------------------------------------------------------------------------- /examples/stream.py: -------------------------------------------------------------------------------- 1 | """Example of streaming a file and printing status updates. 2 | 3 | python stream.py 10.0.0.4 file.mp3 4 | """ 5 | 6 | import asyncio 7 | import sys 8 | 9 | import pyatv 10 | from pyatv.interface import Playing, PushListener 11 | 12 | LOOP = asyncio.get_event_loop() 13 | 14 | 15 | class PushUpdatePrinter(PushListener): 16 | """Print push updates to console.""" 17 | 18 | def playstatus_update(self, updater, playstatus: Playing) -> None: 19 | """Inform about changes to what is currently playing.""" 20 | print(30 * "-" + "\n", playstatus) 21 | 22 | def playstatus_error(self, updater, exception: Exception) -> None: 23 | """Inform about an error when updating play status.""" 24 | print("Error:", exception) 25 | 26 | 27 | async def stream_with_push_updates( 28 | address: str, filename: str, loop: asyncio.AbstractEventLoop 29 | ): 30 | """Find a device and print what is playing.""" 31 | print("* Discovering device on network...") 32 | atvs = await pyatv.scan(loop, hosts=[address], timeout=5) 33 | 34 | if not atvs: 35 | print("* Device found", file=sys.stderr) 36 | return 37 | 38 | conf = atvs[0] 39 | 40 | print("* Connecting to", conf.address) 41 | atv = await pyatv.connect(conf, loop) 42 | 43 | listener = PushUpdatePrinter() 44 | atv.push_updater.listener = listener 45 | atv.push_updater.start() 46 | 47 | try: 48 | print("* Starting to stream", filename) 49 | await atv.stream.stream_file(filename) 50 | await asyncio.sleep(1) 51 | finally: 52 | atv.close() 53 | 54 | 55 | if __name__ == "__main__": 56 | LOOP.run_until_complete(stream_with_push_updates(sys.argv[1], sys.argv[2], LOOP)) 57 | -------------------------------------------------------------------------------- /pyatv/auth/server_auth.py: -------------------------------------------------------------------------------- 1 | """Shared server authentication constants.""" 2 | 3 | PIN_CODE = 1111 4 | CLIENT_IDENTIFIER = "4D797FD3-3538-427E-A47B-A32FC6CF3A6A" 5 | CLIENT_CREDENTIALS = ( 6 | "E734EA6C2B6257DE72355E472AA05A4C487E6B463C029ED306DF2F01B5636B58:" 7 | + "80FD8265B0748DA90BC5C5294DABE394D3D47199994AE96AC73EE45C783537B1:" 8 | + "35443739374644332D333533382D343237452D413437422D41333246433643463" 9 | + "3413641:34443739374644332D333533382D343237452D413437422D413332464" 10 | + "336434633413641" 11 | ) 12 | SERVER_IDENTIFIER = "5D797FD3-3538-427E-A47B-A32FC6CF3A6A" 13 | PRIVATE_KEY = 32 * b"\xaa" 14 | -------------------------------------------------------------------------------- /pyatv/protocols/companion/keyed_archiver.py: -------------------------------------------------------------------------------- 1 | """Support for working with NSKeyedArchiver serialized data.""" 2 | 3 | import plistlib 4 | from typing import Any, List, Optional, Tuple 5 | 6 | 7 | def read_archive_properties(archive, *paths: List[str]) -> Tuple[Optional[Any], ...]: 8 | """Get properties from NSKeyedArchiver encoded PList. 9 | 10 | In the absence of a robust NSKeyedArchiver implementation, read one or 11 | more properties from the archived plist by following UID references. 12 | """ 13 | data = plistlib.loads(archive) 14 | results = [] 15 | 16 | objects = data["$objects"] 17 | for path in paths: 18 | element = data["$top"] 19 | try: 20 | for key in path: 21 | element = element[key] 22 | if isinstance(element, plistlib.UID): 23 | element = objects[element] 24 | results.append(element) 25 | except (IndexError, KeyError): 26 | results.append(None) 27 | 28 | return tuple(results) 29 | -------------------------------------------------------------------------------- /pyatv/protocols/companion/plist_payloads/__init__.py: -------------------------------------------------------------------------------- 1 | """Module with helpers for generating plist payloads for Companion protocol.""" 2 | 3 | from .rti_text_operations import * # noqa 4 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/AudioFadeMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/PlayerPath.proto"; 5 | 6 | extend ProtocolMessage { 7 | optional AudioFadeMessage audioFadeMessage = 88; 8 | } 9 | 10 | message AudioFadeMessage { 11 | optional PlayerPath playerPath = 1; 12 | optional int32 fadeType = 2; 13 | } 14 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/AudioFadeMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.PlayerPath_pb2 11 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 12 | import typing 13 | 14 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 15 | 16 | @typing.final 17 | class AudioFadeMessage(google.protobuf.message.Message): 18 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 19 | 20 | PLAYERPATH_FIELD_NUMBER: builtins.int 21 | FADETYPE_FIELD_NUMBER: builtins.int 22 | fadeType: builtins.int 23 | @property 24 | def playerPath(self) -> pyatv.protocols.mrp.protobuf.PlayerPath_pb2.PlayerPath: ... 25 | def __init__( 26 | self, 27 | *, 28 | playerPath: pyatv.protocols.mrp.protobuf.PlayerPath_pb2.PlayerPath | None = ..., 29 | fadeType: builtins.int | None = ..., 30 | ) -> None: ... 31 | def HasField(self, field_name: typing.Literal["fadeType", b"fadeType", "playerPath", b"playerPath"]) -> builtins.bool: ... 32 | def ClearField(self, field_name: typing.Literal["fadeType", b"fadeType", "playerPath", b"playerPath"]) -> None: ... 33 | 34 | global___AudioFadeMessage = AudioFadeMessage 35 | 36 | AUDIOFADEMESSAGE_FIELD_NUMBER: builtins.int 37 | audioFadeMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___AudioFadeMessage] 38 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/AudioFadeResponseMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional AudioFadeResponseMessage audioFadeResponseMessage = 89; 7 | } 8 | 9 | message AudioFadeResponseMessage { 10 | optional int64 fadeDuration = 1; 11 | } 12 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/AudioFadeResponseMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class AudioFadeResponseMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | FADEDURATION_FIELD_NUMBER: builtins.int 20 | fadeDuration: builtins.int 21 | def __init__( 22 | self, 23 | *, 24 | fadeDuration: builtins.int | None = ..., 25 | ) -> None: ... 26 | def HasField(self, field_name: typing.Literal["fadeDuration", b"fadeDuration"]) -> builtins.bool: ... 27 | def ClearField(self, field_name: typing.Literal["fadeDuration", b"fadeDuration"]) -> None: ... 28 | 29 | global___AudioFadeResponseMessage = AudioFadeResponseMessage 30 | 31 | AUDIOFADERESPONSEMESSAGE_FIELD_NUMBER: builtins.int 32 | audioFadeResponseMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___AudioFadeResponseMessage] 33 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/AudioFormatSettingsMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | message AudioFormatSettings { 4 | optional bytes formatSettingsPlistData = 1; 5 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/AudioFormatSettingsMessage_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: pyatv/protocols/mrp/protobuf/AudioFormatSettingsMessage.proto 5 | # Protobuf Python Version: 6.30.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 6, 15 | 30, 16 | 2, 17 | '', 18 | 'pyatv/protocols/mrp/protobuf/AudioFormatSettingsMessage.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | 26 | 27 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n=pyatv/protocols/mrp/protobuf/AudioFormatSettingsMessage.proto\"6\n\x13\x41udioFormatSettings\x12\x1f\n\x17\x66ormatSettingsPlistData\x18\x01 \x01(\x0c') 28 | 29 | _globals = globals() 30 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 31 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pyatv.protocols.mrp.protobuf.AudioFormatSettingsMessage_pb2', _globals) 32 | if not _descriptor._USE_C_DESCRIPTORS: 33 | DESCRIPTOR._loaded_options = None 34 | _globals['_AUDIOFORMATSETTINGS']._serialized_start=65 35 | _globals['_AUDIOFORMATSETTINGS']._serialized_end=119 36 | # @@protoc_insertion_point(module_scope) 37 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/AudioFormatSettingsMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import typing 10 | 11 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 12 | 13 | @typing.final 14 | class AudioFormatSettings(google.protobuf.message.Message): 15 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 16 | 17 | FORMATSETTINGSPLISTDATA_FIELD_NUMBER: builtins.int 18 | formatSettingsPlistData: builtins.bytes 19 | def __init__( 20 | self, 21 | *, 22 | formatSettingsPlistData: builtins.bytes | None = ..., 23 | ) -> None: ... 24 | def HasField(self, field_name: typing.Literal["formatSettingsPlistData", b"formatSettingsPlistData"]) -> builtins.bool: ... 25 | def ClearField(self, field_name: typing.Literal["formatSettingsPlistData", b"formatSettingsPlistData"]) -> None: ... 26 | 27 | global___AudioFormatSettings = AudioFormatSettings 28 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/ClientUpdatesConfigMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional ClientUpdatesConfigMessage clientUpdatesConfigMessage = 21; 7 | } 8 | 9 | message ClientUpdatesConfigMessage { 10 | optional bool artworkUpdates = 1; 11 | optional bool nowPlayingUpdates = 2; 12 | optional bool volumeUpdates = 3; 13 | optional bool keyboardUpdates = 4; 14 | optional bool outputDeviceUpdates = 5; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/CommandOptions.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/Common.proto"; 4 | 5 | message CommandOptions { 6 | optional string sourceId = 2; 7 | optional string mediaType = 3; 8 | optional bool externalPlayerCommand = 4; 9 | optional float skipInterval = 5; 10 | optional float playbackRate = 6; 11 | optional float rating = 7; 12 | optional bool negative = 8; 13 | optional double playbackPosition = 9; 14 | optional RepeatMode.Enum repeatMode = 10; 15 | optional ShuffleMode.Enum shuffleMode = 11; 16 | optional uint64 trackID = 12; 17 | optional int64 radioStationID = 13; 18 | optional string radioStationHash = 14; 19 | optional bytes systemAppPlaybackQueueData = 15; 20 | optional string destinationAppDisplayID = 16; 21 | optional uint32 sendOptions = 17; 22 | optional bool requestDefermentToPlaybackQueuePosition = 18; 23 | optional string contextID = 19; 24 | optional bool shouldOverrideManuallyCuratedQueue = 20; 25 | optional string stationURL = 21; 26 | optional bool shouldBeginRadioPlayback = 22; 27 | optional int32 playbackQueueInsertionPosition = 23; 28 | optional string contentItemID = 24; 29 | optional int32 playbackQueueOffset = 25; 30 | optional int32 playbackQueueDestinationOffset = 26; 31 | optional bytes languageOption = 27; 32 | optional bytes playbackQueueContext = 28; 33 | optional string insertAfterContentItemID = 29; 34 | optional string nowPlayingContentItemID = 30; 35 | optional int32 replaceIntent = 31; 36 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/Common.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | message RepeatMode { 4 | enum Enum { 5 | Unknown = 0; 6 | Off = 1; 7 | One = 2; 8 | All = 3; 9 | } 10 | } 11 | 12 | message ShuffleMode { 13 | enum Enum { 14 | Unknown = 0; 15 | Off = 1; 16 | Albums = 2; 17 | Songs = 3; 18 | } 19 | } 20 | 21 | message DeviceClass { 22 | enum Enum { 23 | Invalid = 0; 24 | iPhone = 1; 25 | iPod = 2; 26 | iPad = 3; 27 | AppleTV = 4; 28 | iFPGA = 5; 29 | Watch = 6; 30 | Accessory = 7; 31 | Bridge = 8; 32 | Mac = 9; 33 | } 34 | } 35 | 36 | message DeviceType { 37 | enum Enum { 38 | Unknown = 0; 39 | AirPlay = 1; 40 | Bluetooth = 2; 41 | CarPlay = 3; 42 | BuiltIn = 4; 43 | Wired = 5; 44 | } 45 | } 46 | 47 | message DeviceSubType { 48 | enum Enum { 49 | Default = 0; 50 | Speaker = 1; 51 | Headphones = 2; 52 | Headset = 3; 53 | Receiver = 4; 54 | LineOut = 5; 55 | USB = 6; 56 | DisplayPort = 7; 57 | HDMI = 8; 58 | LowEnergy = 9; 59 | SPDIF = 10; 60 | TV = 11; 61 | HomePod = 12; 62 | AppleTV = 13; 63 | Vehicle = 14; 64 | Cluster = 15; 65 | SetTopBox = 16; 66 | TVStick = 17; 67 | } 68 | } 69 | 70 | message PlaybackState { 71 | enum Enum { 72 | Unknown = 0; 73 | Playing = 1; 74 | Paused = 2; 75 | Stopped = 3; 76 | Interrupted = 4; 77 | Seeking = 5; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/ConfigureConnectionMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional ConfigureConnectionMessage configureConnectionMessage = 94; 7 | } 8 | 9 | message ConfigureConnectionMessage { 10 | optional string groupID = 1; 11 | } 12 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/ConfigureConnectionMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class ConfigureConnectionMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | GROUPID_FIELD_NUMBER: builtins.int 20 | groupID: builtins.str 21 | def __init__( 22 | self, 23 | *, 24 | groupID: builtins.str | None = ..., 25 | ) -> None: ... 26 | def HasField(self, field_name: typing.Literal["groupID", b"groupID"]) -> builtins.bool: ... 27 | def ClearField(self, field_name: typing.Literal["groupID", b"groupID"]) -> None: ... 28 | 29 | global___ConfigureConnectionMessage = ConfigureConnectionMessage 30 | 31 | CONFIGURECONNECTIONMESSAGE_FIELD_NUMBER: builtins.int 32 | configureConnectionMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___ConfigureConnectionMessage] 33 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/ContentItem.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ContentItemMetadata.proto"; 4 | import "pyatv/protocols/mrp/protobuf/LanguageOption.proto"; 5 | 6 | message LanguageOptionGroup { 7 | optional bool allowEmptySelection = 1; 8 | optional LanguageOption defaultLanguageOption = 2; 9 | repeated LanguageOption languageOptions = 3; 10 | } 11 | 12 | message ContentItem { 13 | optional string identifier = 1; 14 | optional ContentItemMetadata metadata = 2; 15 | optional bytes artworkData = 3; 16 | optional string info = 4; 17 | repeated LanguageOptionGroup availableLanguageOptions = 5; 18 | repeated LanguageOption currentLanguageOptions = 6; 19 | // optional Lyrics lyrics = 7; 20 | // repeated Sections sections = 8; 21 | optional string parentIdentifier = 9; 22 | optional string ancestorIdentifier = 10; 23 | optional string queueIdentifier = 11; 24 | optional string requestIdentifier = 12; 25 | optional int32 artworkDataWidth = 13; 26 | optional int32 artworkDataHeight = 14; 27 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/CryptoPairingMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional CryptoPairingMessage cryptoPairingMessage = 39; 7 | } 8 | 9 | message CryptoPairingMessage { 10 | optional bytes pairingData = 1; // Example: <00010006 0101> 11 | optional int32 status = 2; // Example: 0 12 | optional bool isRetrying = 3; 13 | optional bool isUsingSystemPairing = 4; 14 | optional int32 state = 5; 15 | } 16 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/GenericMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional GenericMessage genericMessage = 46; 7 | } 8 | 9 | message GenericMessage { 10 | optional string key = 1; 11 | optional bytes value = 2; 12 | } 13 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/GenericMessage_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: pyatv/protocols/mrp/protobuf/GenericMessage.proto 5 | # Protobuf Python Version: 6.30.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 6, 15 | 30, 16 | 2, 17 | '', 18 | 'pyatv/protocols/mrp/protobuf/GenericMessage.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | from pyatv.protocols.mrp.protobuf import ProtocolMessage_pb2 as pyatv_dot_protocols_dot_mrp_dot_protobuf_dot_ProtocolMessage__pb2 26 | 27 | 28 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n1pyatv/protocols/mrp/protobuf/GenericMessage.proto\x1a\x32pyatv/protocols/mrp/protobuf/ProtocolMessage.proto\",\n\x0eGenericMessage\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:9\n\x0egenericMessage\x12\x10.ProtocolMessage\x18. \x01(\x0b\x32\x0f.GenericMessage') 29 | 30 | _globals = globals() 31 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 32 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pyatv.protocols.mrp.protobuf.GenericMessage_pb2', _globals) 33 | if not _descriptor._USE_C_DESCRIPTORS: 34 | DESCRIPTOR._loaded_options = None 35 | _globals['_GENERICMESSAGE']._serialized_start=105 36 | _globals['_GENERICMESSAGE']._serialized_end=149 37 | # @@protoc_insertion_point(module_scope) 38 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/GenericMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class GenericMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | KEY_FIELD_NUMBER: builtins.int 20 | VALUE_FIELD_NUMBER: builtins.int 21 | key: builtins.str 22 | value: builtins.bytes 23 | def __init__( 24 | self, 25 | *, 26 | key: builtins.str | None = ..., 27 | value: builtins.bytes | None = ..., 28 | ) -> None: ... 29 | def HasField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> builtins.bool: ... 30 | def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... 31 | 32 | global___GenericMessage = GenericMessage 33 | 34 | GENERICMESSAGE_FIELD_NUMBER: builtins.int 35 | genericMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___GenericMessage] 36 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/GetKeyboardSessionMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional string getKeyboardSessionMessage = 29; 7 | } 8 | 9 | message GetKeyboardSessionMessage { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/GetKeyboardSessionMessage_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: pyatv/protocols/mrp/protobuf/GetKeyboardSessionMessage.proto 5 | # Protobuf Python Version: 6.30.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 6, 15 | 30, 16 | 2, 17 | '', 18 | 'pyatv/protocols/mrp/protobuf/GetKeyboardSessionMessage.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | from pyatv.protocols.mrp.protobuf import ProtocolMessage_pb2 as pyatv_dot_protocols_dot_mrp_dot_protobuf_dot_ProtocolMessage__pb2 26 | 27 | 28 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n None: ... 22 | 23 | global___GetKeyboardSessionMessage = GetKeyboardSessionMessage 24 | 25 | GETKEYBOARDSESSIONMESSAGE_FIELD_NUMBER: builtins.int 26 | getKeyboardSessionMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, builtins.str] 27 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/GetRemoteTextInputSessionMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional GetRemoteTextInputSessionMessage getRemoteTextInputSessionMessage = 72; 7 | } 8 | 9 | message GetRemoteTextInputSessionMessage { 10 | } 11 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/GetRemoteTextInputSessionMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class GetRemoteTextInputSessionMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | def __init__( 20 | self, 21 | ) -> None: ... 22 | 23 | global___GetRemoteTextInputSessionMessage = GetRemoteTextInputSessionMessage 24 | 25 | GETREMOTETEXTINPUTSESSIONMESSAGE_FIELD_NUMBER: builtins.int 26 | getRemoteTextInputSessionMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___GetRemoteTextInputSessionMessage] 27 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/GetVolumeMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional GetVolumeMessage getVolumeMessage = 53; 7 | } 8 | 9 | message GetVolumeMessage { 10 | optional string outputDeviceUID = 1; 11 | } 12 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/GetVolumeMessage_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: pyatv/protocols/mrp/protobuf/GetVolumeMessage.proto 5 | # Protobuf Python Version: 6.30.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 6, 15 | 30, 16 | 2, 17 | '', 18 | 'pyatv/protocols/mrp/protobuf/GetVolumeMessage.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | from pyatv.protocols.mrp.protobuf import ProtocolMessage_pb2 as pyatv_dot_protocols_dot_mrp_dot_protobuf_dot_ProtocolMessage__pb2 26 | 27 | 28 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n3pyatv/protocols/mrp/protobuf/GetVolumeMessage.proto\x1a\x32pyatv/protocols/mrp/protobuf/ProtocolMessage.proto\"+\n\x10GetVolumeMessage\x12\x17\n\x0foutputDeviceUID\x18\x01 \x01(\t:=\n\x10getVolumeMessage\x12\x10.ProtocolMessage\x18\x35 \x01(\x0b\x32\x11.GetVolumeMessage') 29 | 30 | _globals = globals() 31 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 32 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pyatv.protocols.mrp.protobuf.GetVolumeMessage_pb2', _globals) 33 | if not _descriptor._USE_C_DESCRIPTORS: 34 | DESCRIPTOR._loaded_options = None 35 | _globals['_GETVOLUMEMESSAGE']._serialized_start=107 36 | _globals['_GETVOLUMEMESSAGE']._serialized_end=150 37 | # @@protoc_insertion_point(module_scope) 38 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/GetVolumeMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class GetVolumeMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | OUTPUTDEVICEUID_FIELD_NUMBER: builtins.int 20 | outputDeviceUID: builtins.str 21 | def __init__( 22 | self, 23 | *, 24 | outputDeviceUID: builtins.str | None = ..., 25 | ) -> None: ... 26 | def HasField(self, field_name: typing.Literal["outputDeviceUID", b"outputDeviceUID"]) -> builtins.bool: ... 27 | def ClearField(self, field_name: typing.Literal["outputDeviceUID", b"outputDeviceUID"]) -> None: ... 28 | 29 | global___GetVolumeMessage = GetVolumeMessage 30 | 31 | GETVOLUMEMESSAGE_FIELD_NUMBER: builtins.int 32 | getVolumeMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___GetVolumeMessage] 33 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/GetVolumeResultMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional GetVolumeResultMessage getVolumeResultMessage = 54; 7 | } 8 | 9 | message GetVolumeResultMessage { 10 | optional float volume = 1; 11 | } 12 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/GetVolumeResultMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class GetVolumeResultMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | VOLUME_FIELD_NUMBER: builtins.int 20 | volume: builtins.float 21 | def __init__( 22 | self, 23 | *, 24 | volume: builtins.float | None = ..., 25 | ) -> None: ... 26 | def HasField(self, field_name: typing.Literal["volume", b"volume"]) -> builtins.bool: ... 27 | def ClearField(self, field_name: typing.Literal["volume", b"volume"]) -> None: ... 28 | 29 | global___GetVolumeResultMessage = GetVolumeResultMessage 30 | 31 | GETVOLUMERESULTMESSAGE_FIELD_NUMBER: builtins.int 32 | getVolumeResultMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___GetVolumeResultMessage] 33 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/LanguageOption.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | message LanguageOption { 4 | optional int32 type = 1; 5 | optional string languageTag = 2; 6 | repeated string characteristics = 3; 7 | optional string displayName = 4; 8 | optional string identifier = 5; 9 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/LanguageOption_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: pyatv/protocols/mrp/protobuf/LanguageOption.proto 5 | # Protobuf Python Version: 6.30.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 6, 15 | 30, 16 | 2, 17 | '', 18 | 'pyatv/protocols/mrp/protobuf/LanguageOption.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | 26 | 27 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n1pyatv/protocols/mrp/protobuf/LanguageOption.proto\"u\n\x0eLanguageOption\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x13\n\x0blanguageTag\x18\x02 \x01(\t\x12\x17\n\x0f\x63haracteristics\x18\x03 \x03(\t\x12\x13\n\x0b\x64isplayName\x18\x04 \x01(\t\x12\x12\n\nidentifier\x18\x05 \x01(\t') 28 | 29 | _globals = globals() 30 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 31 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pyatv.protocols.mrp.protobuf.LanguageOption_pb2', _globals) 32 | if not _descriptor._USE_C_DESCRIPTORS: 33 | DESCRIPTOR._loaded_options = None 34 | _globals['_LANGUAGEOPTION']._serialized_start=53 35 | _globals['_LANGUAGEOPTION']._serialized_end=170 36 | # @@protoc_insertion_point(module_scope) 37 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/LanguageOption_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import collections.abc 8 | import google.protobuf.descriptor 9 | import google.protobuf.internal.containers 10 | import google.protobuf.message 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class LanguageOption(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | TYPE_FIELD_NUMBER: builtins.int 20 | LANGUAGETAG_FIELD_NUMBER: builtins.int 21 | CHARACTERISTICS_FIELD_NUMBER: builtins.int 22 | DISPLAYNAME_FIELD_NUMBER: builtins.int 23 | IDENTIFIER_FIELD_NUMBER: builtins.int 24 | type: builtins.int 25 | languageTag: builtins.str 26 | displayName: builtins.str 27 | identifier: builtins.str 28 | @property 29 | def characteristics(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... 30 | def __init__( 31 | self, 32 | *, 33 | type: builtins.int | None = ..., 34 | languageTag: builtins.str | None = ..., 35 | characteristics: collections.abc.Iterable[builtins.str] | None = ..., 36 | displayName: builtins.str | None = ..., 37 | identifier: builtins.str | None = ..., 38 | ) -> None: ... 39 | def HasField(self, field_name: typing.Literal["displayName", b"displayName", "identifier", b"identifier", "languageTag", b"languageTag", "type", b"type"]) -> builtins.bool: ... 40 | def ClearField(self, field_name: typing.Literal["characteristics", b"characteristics", "displayName", b"displayName", "identifier", b"identifier", "languageTag", b"languageTag", "type", b"type"]) -> None: ... 41 | 42 | global___LanguageOption = LanguageOption 43 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/ModifyOutputContextRequestMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional ModifyOutputContextRequestMessage modifyOutputContextRequestMessage = 52; 7 | } 8 | 9 | message ModifyOutputContextRequestType { 10 | enum Enum { 11 | SharedAudioPresentation = 1; 12 | } 13 | } 14 | 15 | message ModifyOutputContextRequestMessage { 16 | optional ModifyOutputContextRequestType.Enum type = 1; 17 | repeated string addingDevices = 2; 18 | repeated string removingDevices = 3; 19 | repeated string settingDevices = 4; 20 | repeated string clusterAwareAddingDevices = 5; 21 | repeated string clusterAwareRemovingDevices = 6; 22 | repeated string clusterAwareSettingDevices = 7; 23 | } 24 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/NotificationMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional NotificationMessage notificationMessage = 16; 7 | } 8 | 9 | message NotificationMessage { 10 | repeated string notification = 1; 11 | repeated bytes userInfo = 2; 12 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/NotificationMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import collections.abc 8 | import google.protobuf.descriptor 9 | import google.protobuf.internal.containers 10 | import google.protobuf.internal.extension_dict 11 | import google.protobuf.message 12 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 13 | import typing 14 | 15 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 16 | 17 | @typing.final 18 | class NotificationMessage(google.protobuf.message.Message): 19 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 20 | 21 | NOTIFICATION_FIELD_NUMBER: builtins.int 22 | USERINFO_FIELD_NUMBER: builtins.int 23 | @property 24 | def notification(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... 25 | @property 26 | def userInfo(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.bytes]: ... 27 | def __init__( 28 | self, 29 | *, 30 | notification: collections.abc.Iterable[builtins.str] | None = ..., 31 | userInfo: collections.abc.Iterable[builtins.bytes] | None = ..., 32 | ) -> None: ... 33 | def ClearField(self, field_name: typing.Literal["notification", b"notification", "userInfo", b"userInfo"]) -> None: ... 34 | 35 | global___NotificationMessage = NotificationMessage 36 | 37 | NOTIFICATIONMESSAGE_FIELD_NUMBER: builtins.int 38 | notificationMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___NotificationMessage] 39 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/NowPlayingClient.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | message NowPlayingClient { 4 | optional int32 processIdentifier = 1; 5 | optional string bundleIdentifier = 2; 6 | optional string parentApplicationBundleIdentifier = 3; 7 | optional int32 processUserIdentifier = 4; 8 | optional int32 nowPlayingVisibility = 5; 9 | // optional TintColor tintColor = 6; 10 | optional string displayName = 7; 11 | repeated string bundleIdentifierHierarchys = 8; 12 | } 13 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/NowPlayingInfo.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/Common.proto"; 4 | 5 | message NowPlayingInfo { 6 | optional string album = 1; 7 | optional string artist = 2; 8 | optional double duration = 3; 9 | optional double elapsedTime = 4; 10 | optional float playbackRate = 5; 11 | optional RepeatMode.Enum repeatMode = 6; 12 | optional ShuffleMode.Enum shuffleMode = 7; 13 | optional double timestamp = 8; 14 | optional string title = 9; 15 | optional uint64 uniqueIdentifier = 10; 16 | optional bool isExplicitTrack = 11; 17 | optional bool isMusicApp = 12; 18 | optional int64 radioStationIdentifier = 13; 19 | optional string radioStationHash = 14; 20 | optional string radioStationName = 15; 21 | optional bytes artworkDataDigest = 16; 22 | optional bool isAlwaysLive = 17; 23 | optional bool isAdvertisement = 18; 24 | } 25 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/NowPlayingPlayer.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | message NowPlayingPlayer { 4 | optional string identifier = 1; 5 | optional string displayName = 2; 6 | optional bool isDefaultPlayer = 3; 7 | optional int32 audioSessionType = 4; 8 | optional int64 mxSessionIDs = 5; 9 | optional uint32 audioSessionID = 6; 10 | optional string iconURL = 7; 11 | } 12 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/NowPlayingPlayer_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: pyatv/protocols/mrp/protobuf/NowPlayingPlayer.proto 5 | # Protobuf Python Version: 6.30.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 6, 15 | 30, 16 | 2, 17 | '', 18 | 'pyatv/protocols/mrp/protobuf/NowPlayingPlayer.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | 26 | 27 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n3pyatv/protocols/mrp/protobuf/NowPlayingPlayer.proto\"\xad\x01\n\x10NowPlayingPlayer\x12\x12\n\nidentifier\x18\x01 \x01(\t\x12\x13\n\x0b\x64isplayName\x18\x02 \x01(\t\x12\x17\n\x0fisDefaultPlayer\x18\x03 \x01(\x08\x12\x18\n\x10\x61udioSessionType\x18\x04 \x01(\x05\x12\x14\n\x0cmxSessionIDs\x18\x05 \x01(\x03\x12\x16\n\x0e\x61udioSessionID\x18\x06 \x01(\r\x12\x0f\n\x07iconURL\x18\x07 \x01(\t') 28 | 29 | _globals = globals() 30 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 31 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pyatv.protocols.mrp.protobuf.NowPlayingPlayer_pb2', _globals) 32 | if not _descriptor._USE_C_DESCRIPTORS: 33 | DESCRIPTOR._loaded_options = None 34 | _globals['_NOWPLAYINGPLAYER']._serialized_start=56 35 | _globals['_NOWPLAYINGPLAYER']._serialized_end=229 36 | # @@protoc_insertion_point(module_scope) 37 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/Origin.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/DeviceInfoMessage.proto"; 4 | 5 | message Origin { 6 | enum Type { 7 | Unknown = 0; 8 | Local = 1; 9 | Custom = 2; 10 | } 11 | 12 | optional Type type = 1; 13 | optional string displayName = 2; 14 | optional int32 identifier = 3; 15 | optional DeviceInfoMessage deviceInfo = 4; 16 | optional bool isLocallyHosted = 5; 17 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/OriginClientPropertiesMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional OriginClientPropertiesMessage originClientPropertiesMessage = 87; 7 | } 8 | 9 | message OriginClientPropertiesMessage { 10 | optional double lastPlayingTimestamp = 1; 11 | } 12 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/OriginClientPropertiesMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class OriginClientPropertiesMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | LASTPLAYINGTIMESTAMP_FIELD_NUMBER: builtins.int 20 | lastPlayingTimestamp: builtins.float 21 | def __init__( 22 | self, 23 | *, 24 | lastPlayingTimestamp: builtins.float | None = ..., 25 | ) -> None: ... 26 | def HasField(self, field_name: typing.Literal["lastPlayingTimestamp", b"lastPlayingTimestamp"]) -> builtins.bool: ... 27 | def ClearField(self, field_name: typing.Literal["lastPlayingTimestamp", b"lastPlayingTimestamp"]) -> None: ... 28 | 29 | global___OriginClientPropertiesMessage = OriginClientPropertiesMessage 30 | 31 | ORIGINCLIENTPROPERTIESMESSAGE_FIELD_NUMBER: builtins.int 32 | originClientPropertiesMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___OriginClientPropertiesMessage] 33 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/PlaybackQueue.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ContentItem.proto"; 4 | import "pyatv/protocols/mrp/protobuf/PlaybackQueueContext.proto"; 5 | import "pyatv/protocols/mrp/protobuf/PlayerPath.proto"; 6 | 7 | message PlaybackQueue { 8 | optional int32 location = 1; 9 | repeated ContentItem contentItems = 2; 10 | optional PlaybackQueueContext context = 3; 11 | optional string requestId = 4; 12 | optional PlayerPath resolvedPlayerPath = 5; 13 | optional bool sendingPlaybackQueueTransaction = 6; 14 | optional string queueIdentifier = 7; 15 | } 16 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/PlaybackQueueCapabilities.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | message PlaybackQueueCapabilities { 4 | optional bool requestByRange = 1; 5 | optional bool requestByIdentifiers = 2; 6 | optional bool requestByRequest = 3; 7 | } 8 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/PlaybackQueueCapabilities_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: pyatv/protocols/mrp/protobuf/PlaybackQueueCapabilities.proto 5 | # Protobuf Python Version: 6.30.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 6, 15 | 30, 16 | 2, 17 | '', 18 | 'pyatv/protocols/mrp/protobuf/PlaybackQueueCapabilities.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | 26 | 27 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n None: ... 30 | def HasField(self, field_name: typing.Literal["requestByIdentifiers", b"requestByIdentifiers", "requestByRange", b"requestByRange", "requestByRequest", b"requestByRequest"]) -> builtins.bool: ... 31 | def ClearField(self, field_name: typing.Literal["requestByIdentifiers", b"requestByIdentifiers", "requestByRange", b"requestByRange", "requestByRequest", b"requestByRequest"]) -> None: ... 32 | 33 | global___PlaybackQueueCapabilities = PlaybackQueueCapabilities 34 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/PlaybackQueueContext.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | message PlaybackQueueContext { 4 | optional string revision = 1; 5 | } 6 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/PlaybackQueueContext_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: pyatv/protocols/mrp/protobuf/PlaybackQueueContext.proto 5 | # Protobuf Python Version: 6.30.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 6, 15 | 30, 16 | 2, 17 | '', 18 | 'pyatv/protocols/mrp/protobuf/PlaybackQueueContext.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | 26 | 27 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n7pyatv/protocols/mrp/protobuf/PlaybackQueueContext.proto\"(\n\x14PlaybackQueueContext\x12\x10\n\x08revision\x18\x01 \x01(\t') 28 | 29 | _globals = globals() 30 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 31 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pyatv.protocols.mrp.protobuf.PlaybackQueueContext_pb2', _globals) 32 | if not _descriptor._USE_C_DESCRIPTORS: 33 | DESCRIPTOR._loaded_options = None 34 | _globals['_PLAYBACKQUEUECONTEXT']._serialized_start=59 35 | _globals['_PLAYBACKQUEUECONTEXT']._serialized_end=99 36 | # @@protoc_insertion_point(module_scope) 37 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/PlaybackQueueContext_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import typing 10 | 11 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 12 | 13 | @typing.final 14 | class PlaybackQueueContext(google.protobuf.message.Message): 15 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 16 | 17 | REVISION_FIELD_NUMBER: builtins.int 18 | revision: builtins.str 19 | def __init__( 20 | self, 21 | *, 22 | revision: builtins.str | None = ..., 23 | ) -> None: ... 24 | def HasField(self, field_name: typing.Literal["revision", b"revision"]) -> builtins.bool: ... 25 | def ClearField(self, field_name: typing.Literal["revision", b"revision"]) -> None: ... 26 | 27 | global___PlaybackQueueContext = PlaybackQueueContext 28 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/PlaybackQueueRequestMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/PlaybackQueueContext.proto"; 5 | import "pyatv/protocols/mrp/protobuf/PlayerPath.proto"; 6 | 7 | extend ProtocolMessage { 8 | optional PlaybackQueueRequestMessage playbackQueueRequestMessage = 37; 9 | } 10 | 11 | message PlaybackQueueRequestMessage { 12 | optional int32 location = 1; 13 | optional int32 length = 2; 14 | optional bool includeMetadata = 3; 15 | optional double artworkWidth = 4; 16 | optional double artworkHeight = 5; 17 | optional bool includeLyrics = 6; 18 | optional bool includeSections = 7; 19 | optional bool includeInfo = 8; 20 | optional bool includeLanguageOptions = 9; 21 | optional PlaybackQueueContext context = 10; 22 | optional string requestID = 11; 23 | repeated string contentItemIdentifiers = 12; 24 | optional bool returnContentItemAssetsInUserCompletion = 13; 25 | optional PlayerPath playerPath = 14; 26 | optional int32 cachingPolicy = 15; 27 | optional string label = 16; 28 | optional bool isLegacyNowPlayingInfoRequest = 17; 29 | } 30 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/PlayerClientPropertiesMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/PlayerPath.proto"; 5 | 6 | extend ProtocolMessage { 7 | optional PlayerClientPropertiesMessage playerClientPropertiesMessage = 86; 8 | } 9 | 10 | message PlayerClientPropertiesMessage { 11 | optional PlayerPath playerPath = 1; 12 | optional double lastPlayingTimestamp = 2; 13 | } 14 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/PlayerClientPropertiesMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.PlayerPath_pb2 11 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 12 | import typing 13 | 14 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 15 | 16 | @typing.final 17 | class PlayerClientPropertiesMessage(google.protobuf.message.Message): 18 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 19 | 20 | PLAYERPATH_FIELD_NUMBER: builtins.int 21 | LASTPLAYINGTIMESTAMP_FIELD_NUMBER: builtins.int 22 | lastPlayingTimestamp: builtins.float 23 | @property 24 | def playerPath(self) -> pyatv.protocols.mrp.protobuf.PlayerPath_pb2.PlayerPath: ... 25 | def __init__( 26 | self, 27 | *, 28 | playerPath: pyatv.protocols.mrp.protobuf.PlayerPath_pb2.PlayerPath | None = ..., 29 | lastPlayingTimestamp: builtins.float | None = ..., 30 | ) -> None: ... 31 | def HasField(self, field_name: typing.Literal["lastPlayingTimestamp", b"lastPlayingTimestamp", "playerPath", b"playerPath"]) -> builtins.bool: ... 32 | def ClearField(self, field_name: typing.Literal["lastPlayingTimestamp", b"lastPlayingTimestamp", "playerPath", b"playerPath"]) -> None: ... 33 | 34 | global___PlayerClientPropertiesMessage = PlayerClientPropertiesMessage 35 | 36 | PLAYERCLIENTPROPERTIESMESSAGE_FIELD_NUMBER: builtins.int 37 | playerClientPropertiesMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___PlayerClientPropertiesMessage] 38 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/PlayerPath.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/Origin.proto"; 4 | import "pyatv/protocols/mrp/protobuf/NowPlayingClient.proto"; 5 | import "pyatv/protocols/mrp/protobuf/NowPlayingPlayer.proto"; 6 | 7 | message PlayerPath { 8 | optional Origin origin = 1; 9 | optional NowPlayingClient client = 2; 10 | optional NowPlayingPlayer player = 3; 11 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/PlayerPath_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import pyatv.protocols.mrp.protobuf.NowPlayingClient_pb2 10 | import pyatv.protocols.mrp.protobuf.NowPlayingPlayer_pb2 11 | import pyatv.protocols.mrp.protobuf.Origin_pb2 12 | import typing 13 | 14 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 15 | 16 | @typing.final 17 | class PlayerPath(google.protobuf.message.Message): 18 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 19 | 20 | ORIGIN_FIELD_NUMBER: builtins.int 21 | CLIENT_FIELD_NUMBER: builtins.int 22 | PLAYER_FIELD_NUMBER: builtins.int 23 | @property 24 | def origin(self) -> pyatv.protocols.mrp.protobuf.Origin_pb2.Origin: ... 25 | @property 26 | def client(self) -> pyatv.protocols.mrp.protobuf.NowPlayingClient_pb2.NowPlayingClient: ... 27 | @property 28 | def player(self) -> pyatv.protocols.mrp.protobuf.NowPlayingPlayer_pb2.NowPlayingPlayer: ... 29 | def __init__( 30 | self, 31 | *, 32 | origin: pyatv.protocols.mrp.protobuf.Origin_pb2.Origin | None = ..., 33 | client: pyatv.protocols.mrp.protobuf.NowPlayingClient_pb2.NowPlayingClient | None = ..., 34 | player: pyatv.protocols.mrp.protobuf.NowPlayingPlayer_pb2.NowPlayingPlayer | None = ..., 35 | ) -> None: ... 36 | def HasField(self, field_name: typing.Literal["client", b"client", "origin", b"origin", "player", b"player"]) -> builtins.bool: ... 37 | def ClearField(self, field_name: typing.Literal["client", b"client", "origin", b"origin", "player", b"player"]) -> None: ... 38 | 39 | global___PlayerPath = PlayerPath 40 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RegisterForGameControllerEventsMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional RegisterForGameControllerEventsMessage registerForGameControllerEventsMessage = 27; 7 | } 8 | 9 | message RegisterForGameControllerEventsMessage { 10 | enum InputModeFlags { 11 | None = 0; 12 | Motion = 1; 13 | Buttons = 2; 14 | Digitizer = 3; 15 | } 16 | 17 | optional InputModeFlags inputModeFlags = 1; 18 | } 19 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RegisterHIDDeviceMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/VirtualTouchDeviceDescriptorMessage.proto"; 5 | 6 | extend ProtocolMessage { 7 | optional RegisterHIDDeviceMessage registerHIDDeviceMessage = 11; 8 | } 9 | 10 | message RegisterHIDDeviceMessage { 11 | optional VirtualTouchDeviceDescriptor deviceDescriptor = 1; 12 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RegisterHIDDeviceMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import pyatv.protocols.mrp.protobuf.VirtualTouchDeviceDescriptorMessage_pb2 12 | import typing 13 | 14 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 15 | 16 | @typing.final 17 | class RegisterHIDDeviceMessage(google.protobuf.message.Message): 18 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 19 | 20 | DEVICEDESCRIPTOR_FIELD_NUMBER: builtins.int 21 | @property 22 | def deviceDescriptor(self) -> pyatv.protocols.mrp.protobuf.VirtualTouchDeviceDescriptorMessage_pb2.VirtualTouchDeviceDescriptor: ... 23 | def __init__( 24 | self, 25 | *, 26 | deviceDescriptor: pyatv.protocols.mrp.protobuf.VirtualTouchDeviceDescriptorMessage_pb2.VirtualTouchDeviceDescriptor | None = ..., 27 | ) -> None: ... 28 | def HasField(self, field_name: typing.Literal["deviceDescriptor", b"deviceDescriptor"]) -> builtins.bool: ... 29 | def ClearField(self, field_name: typing.Literal["deviceDescriptor", b"deviceDescriptor"]) -> None: ... 30 | 31 | global___RegisterHIDDeviceMessage = RegisterHIDDeviceMessage 32 | 33 | REGISTERHIDDEVICEMESSAGE_FIELD_NUMBER: builtins.int 34 | registerHIDDeviceMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___RegisterHIDDeviceMessage] 35 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RegisterHIDDeviceResultMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional RegisterHIDDeviceResultMessage registerHIDDeviceResultMessage = 12; 7 | } 8 | 9 | message RegisterHIDDeviceResultMessage { 10 | optional int32 errorCode = 1; 11 | optional int32 deviceIdentifier = 2; 12 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RegisterHIDDeviceResultMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class RegisterHIDDeviceResultMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | ERRORCODE_FIELD_NUMBER: builtins.int 20 | DEVICEIDENTIFIER_FIELD_NUMBER: builtins.int 21 | errorCode: builtins.int 22 | deviceIdentifier: builtins.int 23 | def __init__( 24 | self, 25 | *, 26 | errorCode: builtins.int | None = ..., 27 | deviceIdentifier: builtins.int | None = ..., 28 | ) -> None: ... 29 | def HasField(self, field_name: typing.Literal["deviceIdentifier", b"deviceIdentifier", "errorCode", b"errorCode"]) -> builtins.bool: ... 30 | def ClearField(self, field_name: typing.Literal["deviceIdentifier", b"deviceIdentifier", "errorCode", b"errorCode"]) -> None: ... 31 | 32 | global___RegisterHIDDeviceResultMessage = RegisterHIDDeviceResultMessage 33 | 34 | REGISTERHIDDEVICERESULTMESSAGE_FIELD_NUMBER: builtins.int 35 | registerHIDDeviceResultMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___RegisterHIDDeviceResultMessage] 36 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RegisterVoiceInputDeviceMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/VoiceInputDeviceDescriptorMessage.proto"; 5 | 6 | extend ProtocolMessage { 7 | optional RegisterVoiceInputDeviceMessage registerVoiceInputDeviceMessage = 33; 8 | } 9 | 10 | message RegisterVoiceInputDeviceMessage { 11 | optional VoiceInputDeviceDescriptor deviceDescriptor = 1; 12 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RegisterVoiceInputDeviceMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import pyatv.protocols.mrp.protobuf.VoiceInputDeviceDescriptorMessage_pb2 12 | import typing 13 | 14 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 15 | 16 | @typing.final 17 | class RegisterVoiceInputDeviceMessage(google.protobuf.message.Message): 18 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 19 | 20 | DEVICEDESCRIPTOR_FIELD_NUMBER: builtins.int 21 | @property 22 | def deviceDescriptor(self) -> pyatv.protocols.mrp.protobuf.VoiceInputDeviceDescriptorMessage_pb2.VoiceInputDeviceDescriptor: ... 23 | def __init__( 24 | self, 25 | *, 26 | deviceDescriptor: pyatv.protocols.mrp.protobuf.VoiceInputDeviceDescriptorMessage_pb2.VoiceInputDeviceDescriptor | None = ..., 27 | ) -> None: ... 28 | def HasField(self, field_name: typing.Literal["deviceDescriptor", b"deviceDescriptor"]) -> builtins.bool: ... 29 | def ClearField(self, field_name: typing.Literal["deviceDescriptor", b"deviceDescriptor"]) -> None: ... 30 | 31 | global___RegisterVoiceInputDeviceMessage = RegisterVoiceInputDeviceMessage 32 | 33 | REGISTERVOICEINPUTDEVICEMESSAGE_FIELD_NUMBER: builtins.int 34 | registerVoiceInputDeviceMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___RegisterVoiceInputDeviceMessage] 35 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RegisterVoiceInputDeviceResponseMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional RegisterVoiceInputDeviceResponseMessage registerVoiceInputDeviceResponseMessage = 34; 7 | } 8 | 9 | message RegisterVoiceInputDeviceResponseMessage { 10 | optional int32 deviceID = 1; 11 | optional int32 errorCode = 2; 12 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RegisterVoiceInputDeviceResponseMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class RegisterVoiceInputDeviceResponseMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | DEVICEID_FIELD_NUMBER: builtins.int 20 | ERRORCODE_FIELD_NUMBER: builtins.int 21 | deviceID: builtins.int 22 | errorCode: builtins.int 23 | def __init__( 24 | self, 25 | *, 26 | deviceID: builtins.int | None = ..., 27 | errorCode: builtins.int | None = ..., 28 | ) -> None: ... 29 | def HasField(self, field_name: typing.Literal["deviceID", b"deviceID", "errorCode", b"errorCode"]) -> builtins.bool: ... 30 | def ClearField(self, field_name: typing.Literal["deviceID", b"deviceID", "errorCode", b"errorCode"]) -> None: ... 31 | 32 | global___RegisterVoiceInputDeviceResponseMessage = RegisterVoiceInputDeviceResponseMessage 33 | 34 | REGISTERVOICEINPUTDEVICERESPONSEMESSAGE_FIELD_NUMBER: builtins.int 35 | registerVoiceInputDeviceResponseMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___RegisterVoiceInputDeviceResponseMessage] 36 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RemoteTextInputMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional RemoteTextInputMessage remoteTextInputMessage = 71; 7 | } 8 | 9 | message RemoteTextInputMessage { 10 | optional double timestamp = 1; 11 | optional uint64 version = 2; 12 | optional bytes data = 3; 13 | } 14 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RemoteTextInputMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class RemoteTextInputMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | TIMESTAMP_FIELD_NUMBER: builtins.int 20 | VERSION_FIELD_NUMBER: builtins.int 21 | DATA_FIELD_NUMBER: builtins.int 22 | timestamp: builtins.float 23 | version: builtins.int 24 | data: builtins.bytes 25 | def __init__( 26 | self, 27 | *, 28 | timestamp: builtins.float | None = ..., 29 | version: builtins.int | None = ..., 30 | data: builtins.bytes | None = ..., 31 | ) -> None: ... 32 | def HasField(self, field_name: typing.Literal["data", b"data", "timestamp", b"timestamp", "version", b"version"]) -> builtins.bool: ... 33 | def ClearField(self, field_name: typing.Literal["data", b"data", "timestamp", b"timestamp", "version", b"version"]) -> None: ... 34 | 35 | global___RemoteTextInputMessage = RemoteTextInputMessage 36 | 37 | REMOTETEXTINPUTMESSAGE_FIELD_NUMBER: builtins.int 38 | remoteTextInputMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___RemoteTextInputMessage] 39 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RemoveClientMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/NowPlayingClient.proto"; 5 | 6 | extend ProtocolMessage { 7 | optional RemoveClientMessage removeClientMessage = 57; 8 | } 9 | 10 | message RemoveClientMessage { 11 | optional NowPlayingClient client = 1; 12 | } 13 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RemoveClientMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.NowPlayingClient_pb2 11 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 12 | import typing 13 | 14 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 15 | 16 | @typing.final 17 | class RemoveClientMessage(google.protobuf.message.Message): 18 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 19 | 20 | CLIENT_FIELD_NUMBER: builtins.int 21 | @property 22 | def client(self) -> pyatv.protocols.mrp.protobuf.NowPlayingClient_pb2.NowPlayingClient: ... 23 | def __init__( 24 | self, 25 | *, 26 | client: pyatv.protocols.mrp.protobuf.NowPlayingClient_pb2.NowPlayingClient | None = ..., 27 | ) -> None: ... 28 | def HasField(self, field_name: typing.Literal["client", b"client"]) -> builtins.bool: ... 29 | def ClearField(self, field_name: typing.Literal["client", b"client"]) -> None: ... 30 | 31 | global___RemoveClientMessage = RemoveClientMessage 32 | 33 | REMOVECLIENTMESSAGE_FIELD_NUMBER: builtins.int 34 | removeClientMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___RemoveClientMessage] 35 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RemoveEndpointsMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional RemoveEndpointsMessage removeEndpointsMessage = 84; 7 | } 8 | 9 | message RemoveEndpointsMessage { 10 | repeated string endpointUIDs = 1; 11 | } 12 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RemoveEndpointsMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import collections.abc 8 | import google.protobuf.descriptor 9 | import google.protobuf.internal.containers 10 | import google.protobuf.internal.extension_dict 11 | import google.protobuf.message 12 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 13 | import typing 14 | 15 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 16 | 17 | @typing.final 18 | class RemoveEndpointsMessage(google.protobuf.message.Message): 19 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 20 | 21 | ENDPOINTUIDS_FIELD_NUMBER: builtins.int 22 | @property 23 | def endpointUIDs(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... 24 | def __init__( 25 | self, 26 | *, 27 | endpointUIDs: collections.abc.Iterable[builtins.str] | None = ..., 28 | ) -> None: ... 29 | def ClearField(self, field_name: typing.Literal["endpointUIDs", b"endpointUIDs"]) -> None: ... 30 | 31 | global___RemoveEndpointsMessage = RemoveEndpointsMessage 32 | 33 | REMOVEENDPOINTSMESSAGE_FIELD_NUMBER: builtins.int 34 | removeEndpointsMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___RemoveEndpointsMessage] 35 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RemoveOutputDevicesMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional RemoveOutputDevicesMessage removeOutputDevicesMessage = 70; 7 | } 8 | 9 | message RemoveOutputDevicesMessage { 10 | repeated string outputDeviceUIDs = 1; 11 | optional string endpointUID = 2; 12 | } 13 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RemoveOutputDevicesMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import collections.abc 8 | import google.protobuf.descriptor 9 | import google.protobuf.internal.containers 10 | import google.protobuf.internal.extension_dict 11 | import google.protobuf.message 12 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 13 | import typing 14 | 15 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 16 | 17 | @typing.final 18 | class RemoveOutputDevicesMessage(google.protobuf.message.Message): 19 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 20 | 21 | OUTPUTDEVICEUIDS_FIELD_NUMBER: builtins.int 22 | ENDPOINTUID_FIELD_NUMBER: builtins.int 23 | endpointUID: builtins.str 24 | @property 25 | def outputDeviceUIDs(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... 26 | def __init__( 27 | self, 28 | *, 29 | outputDeviceUIDs: collections.abc.Iterable[builtins.str] | None = ..., 30 | endpointUID: builtins.str | None = ..., 31 | ) -> None: ... 32 | def HasField(self, field_name: typing.Literal["endpointUID", b"endpointUID"]) -> builtins.bool: ... 33 | def ClearField(self, field_name: typing.Literal["endpointUID", b"endpointUID", "outputDeviceUIDs", b"outputDeviceUIDs"]) -> None: ... 34 | 35 | global___RemoveOutputDevicesMessage = RemoveOutputDevicesMessage 36 | 37 | REMOVEOUTPUTDEVICESMESSAGE_FIELD_NUMBER: builtins.int 38 | removeOutputDevicesMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___RemoveOutputDevicesMessage] 39 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RemovePlayerMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/PlayerPath.proto"; 5 | 6 | extend ProtocolMessage { 7 | optional RemovePlayerMessage removePlayerMessage = 58; 8 | } 9 | 10 | message RemovePlayerMessage { 11 | optional PlayerPath playerPath = 1; 12 | } 13 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/RemovePlayerMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.PlayerPath_pb2 11 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 12 | import typing 13 | 14 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 15 | 16 | @typing.final 17 | class RemovePlayerMessage(google.protobuf.message.Message): 18 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 19 | 20 | PLAYERPATH_FIELD_NUMBER: builtins.int 21 | @property 22 | def playerPath(self) -> pyatv.protocols.mrp.protobuf.PlayerPath_pb2.PlayerPath: ... 23 | def __init__( 24 | self, 25 | *, 26 | playerPath: pyatv.protocols.mrp.protobuf.PlayerPath_pb2.PlayerPath | None = ..., 27 | ) -> None: ... 28 | def HasField(self, field_name: typing.Literal["playerPath", b"playerPath"]) -> builtins.bool: ... 29 | def ClearField(self, field_name: typing.Literal["playerPath", b"playerPath"]) -> None: ... 30 | 31 | global___RemovePlayerMessage = RemovePlayerMessage 32 | 33 | REMOVEPLAYERMESSAGE_FIELD_NUMBER: builtins.int 34 | removePlayerMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___RemovePlayerMessage] 35 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SendButtonEventMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional SendButtonEventMessage sendButtonEventMessage = 43; 7 | } 8 | 9 | message SendButtonEventMessage { 10 | optional uint32 usagePage = 1; 11 | optional uint32 usage = 2; 12 | optional bool buttonDown = 3; 13 | } 14 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SendButtonEventMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class SendButtonEventMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | USAGEPAGE_FIELD_NUMBER: builtins.int 20 | USAGE_FIELD_NUMBER: builtins.int 21 | BUTTONDOWN_FIELD_NUMBER: builtins.int 22 | usagePage: builtins.int 23 | usage: builtins.int 24 | buttonDown: builtins.bool 25 | def __init__( 26 | self, 27 | *, 28 | usagePage: builtins.int | None = ..., 29 | usage: builtins.int | None = ..., 30 | buttonDown: builtins.bool | None = ..., 31 | ) -> None: ... 32 | def HasField(self, field_name: typing.Literal["buttonDown", b"buttonDown", "usage", b"usage", "usagePage", b"usagePage"]) -> builtins.bool: ... 33 | def ClearField(self, field_name: typing.Literal["buttonDown", b"buttonDown", "usage", b"usage", "usagePage", b"usagePage"]) -> None: ... 34 | 35 | global___SendButtonEventMessage = SendButtonEventMessage 36 | 37 | SENDBUTTONEVENTMESSAGE_FIELD_NUMBER: builtins.int 38 | sendButtonEventMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___SendButtonEventMessage] 39 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SendCommandMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/CommandInfo.proto"; 5 | import "pyatv/protocols/mrp/protobuf/CommandOptions.proto"; 6 | import "pyatv/protocols/mrp/protobuf/PlayerPath.proto"; 7 | 8 | extend ProtocolMessage { 9 | optional SendCommandMessage sendCommandMessage = 6; 10 | } 11 | 12 | message SendCommandMessage { 13 | optional Command command = 1; 14 | optional CommandOptions options = 2; 15 | optional PlayerPath playerPath = 3; 16 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SendHIDEventMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional SendHIDEventMessage sendHIDEventMessage = 13; 7 | } 8 | 9 | message SendHIDEventMessage { 10 | // This data corresponds to a "keyboardEvent" in IOHIDEvent.h encoded as raw 11 | // data. Here is one source: 12 | // 13 | // https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-308/IOHIDFamily/IOHIDEvent.h.auto.html 14 | // 15 | // The interesting parts are: 16 | // - usagePage (UInt32) 17 | // - usage (Uint32) 18 | // - down (bool) 19 | // 20 | // The parameters usagePage and usage corresponds to the key being pressed. 21 | // It is mapped to the USB HID values, which can be found here: 22 | // 23 | // https://github.com/Daij-Djan/DDHidLib/blob/master/usb_hid_usages.txt 24 | // 25 | // Pressing left key would for instance map to usagePage=0x01, usage=0x8B. In 26 | // the hid data, these values are stored as big endian uint16 values in the 27 | // mentioned order. So the same example would be: 0x0001008B0001, assuming 28 | // down = true (key being pressed). For each key press, the same usagePage 29 | // and usage are sent with down=true and down=false (key down + key up). 30 | // 31 | // There is a bit of magic in the raw data that's just not decoded yet, but 32 | // that doesn't matter. Just use this and it will work: 33 | // 34 | // 438922cf0802000000000000000000000100000000000000020000002000000003000000010000000000000000000000000001000000 35 | // 36 | // corresponds to the values above, e.g. 0001008B0001. The first 8 37 | // bytes is a timestamp (mach AbsoluteTime). It's a bit tricky to derive but 38 | // tvOS seems to accept old timestamps here. So it's probably fine to send 39 | // anything. 40 | optional bytes hidEventData = 1; 41 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SendPackedVirtualTouchEventMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional SendPackedVirtualTouchEventMessage sendPackedVirtualTouchEventMessage = 47; 7 | } 8 | 9 | message SendPackedVirtualTouchEventMessage { 10 | 11 | // Corresponds to "phase" in data 12 | enum Phase { 13 | Began = 1; 14 | Moved = 2; 15 | Stationary = 3; 16 | Ended = 4; 17 | Cancelled = 5; 18 | } 19 | 20 | // The packed version of VirtualTouchEvent contains X, Y, phase, deviceID 21 | // and finger stored as a byte array. Each value is written as 16bit little 22 | // endian integers. 23 | optional bytes data = 1; 24 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SendVoiceInputMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/AudioFormatSettingsMessage.proto"; 5 | 6 | extend ProtocolMessage { 7 | optional SendVoiceInputMessage sendVoiceInputMessage = 36; 8 | } 9 | 10 | message AudioStreamPacketDescription { 11 | optional int64 startOffset = 1; 12 | optional uint32 variableFramesInPacket = 2; 13 | optional uint32 dataByteSize = 3; 14 | } 15 | 16 | message AudioBuffer { 17 | optional AudioFormatSettings formatSettings = 1; 18 | optional int64 packetCapacity = 2; 19 | optional int64 maximumPacketSize = 3; 20 | optional int64 packetCount = 4; 21 | optional bytes contents = 5; 22 | repeated AudioStreamPacketDescription packetDescriptions = 6; 23 | } 24 | 25 | message AudioTime { 26 | optional double timestamp = 1; 27 | optional double sampleRate = 2; 28 | } 29 | 30 | message AudioDataBlock { 31 | optional AudioBuffer buffer = 1; 32 | optional AudioTime time = 2; 33 | optional double gain = 3; 34 | } 35 | 36 | message SendVoiceInputMessage { 37 | optional AudioDataBlock dataBlock = 1; 38 | } 39 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetArtworkMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional SetArtworkMessage setArtworkMessage = 10; 7 | } 8 | 9 | message SetArtworkMessage { 10 | optional bytes jpegData = 1; 11 | } 12 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetArtworkMessage_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: pyatv/protocols/mrp/protobuf/SetArtworkMessage.proto 5 | # Protobuf Python Version: 6.30.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 6, 15 | 30, 16 | 2, 17 | '', 18 | 'pyatv/protocols/mrp/protobuf/SetArtworkMessage.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | from pyatv.protocols.mrp.protobuf import ProtocolMessage_pb2 as pyatv_dot_protocols_dot_mrp_dot_protobuf_dot_ProtocolMessage__pb2 26 | 27 | 28 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n4pyatv/protocols/mrp/protobuf/SetArtworkMessage.proto\x1a\x32pyatv/protocols/mrp/protobuf/ProtocolMessage.proto\"%\n\x11SetArtworkMessage\x12\x10\n\x08jpegData\x18\x01 \x01(\x0c:?\n\x11setArtworkMessage\x12\x10.ProtocolMessage\x18\n \x01(\x0b\x32\x12.SetArtworkMessage') 29 | 30 | _globals = globals() 31 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 32 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pyatv.protocols.mrp.protobuf.SetArtworkMessage_pb2', _globals) 33 | if not _descriptor._USE_C_DESCRIPTORS: 34 | DESCRIPTOR._loaded_options = None 35 | _globals['_SETARTWORKMESSAGE']._serialized_start=108 36 | _globals['_SETARTWORKMESSAGE']._serialized_end=145 37 | # @@protoc_insertion_point(module_scope) 38 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetArtworkMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class SetArtworkMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | JPEGDATA_FIELD_NUMBER: builtins.int 20 | jpegData: builtins.bytes 21 | def __init__( 22 | self, 23 | *, 24 | jpegData: builtins.bytes | None = ..., 25 | ) -> None: ... 26 | def HasField(self, field_name: typing.Literal["jpegData", b"jpegData"]) -> builtins.bool: ... 27 | def ClearField(self, field_name: typing.Literal["jpegData", b"jpegData"]) -> None: ... 28 | 29 | global___SetArtworkMessage = SetArtworkMessage 30 | 31 | SETARTWORKMESSAGE_FIELD_NUMBER: builtins.int 32 | setArtworkMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___SetArtworkMessage] 33 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetConnectionStateMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional SetConnectionStateMessage setConnectionStateMessage = 42; 7 | } 8 | 9 | message SetConnectionStateMessage { 10 | enum ConnectionState { 11 | None = 0; 12 | Connecting = 1; 13 | Connected = 2; 14 | Disconnected = 3; 15 | } 16 | 17 | optional ConnectionState state = 1; 18 | } 19 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetDefaultSupportedCommandsMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/NowPlayingInfo.proto"; 5 | import "pyatv/protocols/mrp/protobuf/PlaybackQueue.proto"; 6 | import "pyatv/protocols/mrp/protobuf/SupportedCommands.proto"; 7 | import "pyatv/protocols/mrp/protobuf/PlaybackQueueCapabilities.proto"; 8 | import "pyatv/protocols/mrp/protobuf/PlayerPath.proto"; 9 | import "pyatv/protocols/mrp/protobuf/PlaybackQueueRequestMessage.proto"; 10 | import "pyatv/protocols/mrp/protobuf/Common.proto"; 11 | 12 | extend ProtocolMessage { 13 | optional SetDefaultSupportedCommandsMessage setDefaultSupportedCommandsMessage = 75; 14 | } 15 | 16 | // Seems to be the same as SetStateMessage 17 | message SetDefaultSupportedCommandsMessage { 18 | optional NowPlayingInfo nowPlayingInfo = 1; 19 | optional SupportedCommands supportedCommands = 2; 20 | optional PlaybackQueue playbackQueue = 3; 21 | optional string displayID = 4; 22 | optional string displayName = 5; 23 | optional PlaybackState.Enum playbackState = 6; 24 | optional PlaybackQueueCapabilities playbackQueueCapabilities = 8; 25 | optional PlayerPath playerPath = 9; 26 | optional PlaybackQueueRequestMessage request = 10; 27 | optional double playbackStateTimestamp = 11; 28 | } 29 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetDiscoveryModeMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional SetDiscoveryModeMessage setDiscoveryModeMessage = 82; 7 | } 8 | 9 | message SetDiscoveryModeMessage { 10 | optional int32 mode = 1; 11 | optional int32 features = 2; 12 | } 13 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetDiscoveryModeMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class SetDiscoveryModeMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | MODE_FIELD_NUMBER: builtins.int 20 | FEATURES_FIELD_NUMBER: builtins.int 21 | mode: builtins.int 22 | features: builtins.int 23 | def __init__( 24 | self, 25 | *, 26 | mode: builtins.int | None = ..., 27 | features: builtins.int | None = ..., 28 | ) -> None: ... 29 | def HasField(self, field_name: typing.Literal["features", b"features", "mode", b"mode"]) -> builtins.bool: ... 30 | def ClearField(self, field_name: typing.Literal["features", b"features", "mode", b"mode"]) -> None: ... 31 | 32 | global___SetDiscoveryModeMessage = SetDiscoveryModeMessage 33 | 34 | SETDISCOVERYMODEMESSAGE_FIELD_NUMBER: builtins.int 35 | setDiscoveryModeMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___SetDiscoveryModeMessage] 36 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetHiliteModeMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional SetHiliteModeMessage setHiliteModeMessage = 44; 7 | } 8 | 9 | message SetHiliteModeMessage { 10 | optional int32 hiliteMode = 1; 11 | } 12 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetHiliteModeMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class SetHiliteModeMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | HILITEMODE_FIELD_NUMBER: builtins.int 20 | hiliteMode: builtins.int 21 | def __init__( 22 | self, 23 | *, 24 | hiliteMode: builtins.int | None = ..., 25 | ) -> None: ... 26 | def HasField(self, field_name: typing.Literal["hiliteMode", b"hiliteMode"]) -> builtins.bool: ... 27 | def ClearField(self, field_name: typing.Literal["hiliteMode", b"hiliteMode"]) -> None: ... 28 | 29 | global___SetHiliteModeMessage = SetHiliteModeMessage 30 | 31 | SETHILITEMODEMESSAGE_FIELD_NUMBER: builtins.int 32 | setHiliteModeMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___SetHiliteModeMessage] 33 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetNowPlayingClientMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/NowPlayingClient.proto"; 5 | 6 | extend ProtocolMessage { 7 | optional SetNowPlayingClientMessage setNowPlayingClientMessage = 50; 8 | } 9 | 10 | message SetNowPlayingClientMessage { 11 | optional NowPlayingClient client = 1; 12 | } 13 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetNowPlayingClientMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.NowPlayingClient_pb2 11 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 12 | import typing 13 | 14 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 15 | 16 | @typing.final 17 | class SetNowPlayingClientMessage(google.protobuf.message.Message): 18 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 19 | 20 | CLIENT_FIELD_NUMBER: builtins.int 21 | @property 22 | def client(self) -> pyatv.protocols.mrp.protobuf.NowPlayingClient_pb2.NowPlayingClient: ... 23 | def __init__( 24 | self, 25 | *, 26 | client: pyatv.protocols.mrp.protobuf.NowPlayingClient_pb2.NowPlayingClient | None = ..., 27 | ) -> None: ... 28 | def HasField(self, field_name: typing.Literal["client", b"client"]) -> builtins.bool: ... 29 | def ClearField(self, field_name: typing.Literal["client", b"client"]) -> None: ... 30 | 31 | global___SetNowPlayingClientMessage = SetNowPlayingClientMessage 32 | 33 | SETNOWPLAYINGCLIENTMESSAGE_FIELD_NUMBER: builtins.int 34 | setNowPlayingClientMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___SetNowPlayingClientMessage] 35 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetNowPlayingPlayerMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/PlayerPath.proto"; 5 | 6 | extend ProtocolMessage { 7 | optional SetNowPlayingPlayerMessage setNowPlayingPlayerMessage = 51; 8 | } 9 | 10 | message SetNowPlayingPlayerMessage { 11 | optional PlayerPath playerPath = 1; 12 | } 13 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetNowPlayingPlayerMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.PlayerPath_pb2 11 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 12 | import typing 13 | 14 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 15 | 16 | @typing.final 17 | class SetNowPlayingPlayerMessage(google.protobuf.message.Message): 18 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 19 | 20 | PLAYERPATH_FIELD_NUMBER: builtins.int 21 | @property 22 | def playerPath(self) -> pyatv.protocols.mrp.protobuf.PlayerPath_pb2.PlayerPath: ... 23 | def __init__( 24 | self, 25 | *, 26 | playerPath: pyatv.protocols.mrp.protobuf.PlayerPath_pb2.PlayerPath | None = ..., 27 | ) -> None: ... 28 | def HasField(self, field_name: typing.Literal["playerPath", b"playerPath"]) -> builtins.bool: ... 29 | def ClearField(self, field_name: typing.Literal["playerPath", b"playerPath"]) -> None: ... 30 | 31 | global___SetNowPlayingPlayerMessage = SetNowPlayingPlayerMessage 32 | 33 | SETNOWPLAYINGPLAYERMESSAGE_FIELD_NUMBER: builtins.int 34 | setNowPlayingPlayerMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___SetNowPlayingPlayerMessage] 35 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetRecordingStateMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional SetRecordingStateMessage setRecordingStateMessage = 35; 7 | } 8 | 9 | message SetRecordingStateMessage { 10 | enum RecordingState { 11 | Unknown = 0; 12 | Recording = 1; 13 | NotRecording = 2; 14 | } 15 | 16 | optional RecordingState state = 1; 17 | } 18 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetStateMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/NowPlayingInfo.proto"; 5 | import "pyatv/protocols/mrp/protobuf/PlaybackQueue.proto"; 6 | import "pyatv/protocols/mrp/protobuf/SupportedCommands.proto"; 7 | import "pyatv/protocols/mrp/protobuf/PlaybackQueueCapabilities.proto"; 8 | import "pyatv/protocols/mrp/protobuf/PlayerPath.proto"; 9 | import "pyatv/protocols/mrp/protobuf/PlaybackQueueRequestMessage.proto"; 10 | import "pyatv/protocols/mrp/protobuf/Common.proto"; 11 | 12 | extend ProtocolMessage { 13 | optional SetStateMessage setStateMessage = 9; 14 | } 15 | 16 | message SetStateMessage { 17 | optional NowPlayingInfo nowPlayingInfo = 1; 18 | optional SupportedCommands supportedCommands = 2; 19 | optional PlaybackQueue playbackQueue = 3; 20 | optional string displayID = 4; 21 | optional string displayName = 5; 22 | optional PlaybackState.Enum playbackState = 6; 23 | optional PlaybackQueueCapabilities playbackQueueCapabilities = 8; 24 | optional PlayerPath playerPath = 9; 25 | optional PlaybackQueueRequestMessage request = 10; 26 | optional double playbackStateTimestamp = 11; 27 | } 28 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetVolumeMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional SetVolumeMessage setVolumeMessage = 55; 7 | } 8 | 9 | message SetVolumeMessage { 10 | optional float volume = 1; 11 | optional string outputDeviceUID = 2; 12 | } 13 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SetVolumeMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class SetVolumeMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | VOLUME_FIELD_NUMBER: builtins.int 20 | OUTPUTDEVICEUID_FIELD_NUMBER: builtins.int 21 | volume: builtins.float 22 | outputDeviceUID: builtins.str 23 | def __init__( 24 | self, 25 | *, 26 | volume: builtins.float | None = ..., 27 | outputDeviceUID: builtins.str | None = ..., 28 | ) -> None: ... 29 | def HasField(self, field_name: typing.Literal["outputDeviceUID", b"outputDeviceUID", "volume", b"volume"]) -> builtins.bool: ... 30 | def ClearField(self, field_name: typing.Literal["outputDeviceUID", b"outputDeviceUID", "volume", b"volume"]) -> None: ... 31 | 32 | global___SetVolumeMessage = SetVolumeMessage 33 | 34 | SETVOLUMEMESSAGE_FIELD_NUMBER: builtins.int 35 | setVolumeMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___SetVolumeMessage] 36 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SupportedCommands.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/CommandInfo.proto"; 4 | 5 | message SupportedCommands { 6 | repeated CommandInfo supportedCommands = 1; 7 | } 8 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SupportedCommands_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: pyatv/protocols/mrp/protobuf/SupportedCommands.proto 5 | # Protobuf Python Version: 6.30.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 6, 15 | 30, 16 | 2, 17 | '', 18 | 'pyatv/protocols/mrp/protobuf/SupportedCommands.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | from pyatv.protocols.mrp.protobuf import CommandInfo_pb2 as pyatv_dot_protocols_dot_mrp_dot_protobuf_dot_CommandInfo__pb2 26 | 27 | 28 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n4pyatv/protocols/mrp/protobuf/SupportedCommands.proto\x1a.pyatv/protocols/mrp/protobuf/CommandInfo.proto\"<\n\x11SupportedCommands\x12\'\n\x11supportedCommands\x18\x01 \x03(\x0b\x32\x0c.CommandInfo') 29 | 30 | _globals = globals() 31 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 32 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pyatv.protocols.mrp.protobuf.SupportedCommands_pb2', _globals) 33 | if not _descriptor._USE_C_DESCRIPTORS: 34 | DESCRIPTOR._loaded_options = None 35 | _globals['_SUPPORTEDCOMMANDS']._serialized_start=104 36 | _globals['_SUPPORTEDCOMMANDS']._serialized_end=164 37 | # @@protoc_insertion_point(module_scope) 38 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/SupportedCommands_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import collections.abc 8 | import google.protobuf.descriptor 9 | import google.protobuf.internal.containers 10 | import google.protobuf.message 11 | import pyatv.protocols.mrp.protobuf.CommandInfo_pb2 12 | import typing 13 | 14 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 15 | 16 | @typing.final 17 | class SupportedCommands(google.protobuf.message.Message): 18 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 19 | 20 | SUPPORTEDCOMMANDS_FIELD_NUMBER: builtins.int 21 | @property 22 | def supportedCommands(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[pyatv.protocols.mrp.protobuf.CommandInfo_pb2.CommandInfo]: ... 23 | def __init__( 24 | self, 25 | *, 26 | supportedCommands: collections.abc.Iterable[pyatv.protocols.mrp.protobuf.CommandInfo_pb2.CommandInfo] | None = ..., 27 | ) -> None: ... 28 | def ClearField(self, field_name: typing.Literal["supportedCommands", b"supportedCommands"]) -> None: ... 29 | 30 | global___SupportedCommands = SupportedCommands 31 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/TextInputMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional TextInputMessage textInputMessage = 30; 7 | } 8 | 9 | message ActionType { 10 | enum Enum { 11 | Unknown = 0; 12 | Insert = 1; 13 | Set = 2; 14 | Delete = 3; 15 | ClearAction = 4; // "Clear" clashes with something, making mypy unhappy 16 | } 17 | } 18 | 19 | message TextInputMessage { 20 | optional double timestamp = 1; 21 | optional string text = 2; 22 | optional ActionType.Enum actionType = 3; 23 | } 24 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/TransactionKey.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | message TransactionKey { 4 | optional string identifier = 1; 5 | optional bytes userData = 2; 6 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/TransactionKey_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: pyatv/protocols/mrp/protobuf/TransactionKey.proto 5 | # Protobuf Python Version: 6.30.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 6, 15 | 30, 16 | 2, 17 | '', 18 | 'pyatv/protocols/mrp/protobuf/TransactionKey.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | 26 | 27 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n1pyatv/protocols/mrp/protobuf/TransactionKey.proto\"6\n\x0eTransactionKey\x12\x12\n\nidentifier\x18\x01 \x01(\t\x12\x10\n\x08userData\x18\x02 \x01(\x0c') 28 | 29 | _globals = globals() 30 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 31 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pyatv.protocols.mrp.protobuf.TransactionKey_pb2', _globals) 32 | if not _descriptor._USE_C_DESCRIPTORS: 33 | DESCRIPTOR._loaded_options = None 34 | _globals['_TRANSACTIONKEY']._serialized_start=53 35 | _globals['_TRANSACTIONKEY']._serialized_end=107 36 | # @@protoc_insertion_point(module_scope) 37 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/TransactionKey_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import typing 10 | 11 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 12 | 13 | @typing.final 14 | class TransactionKey(google.protobuf.message.Message): 15 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 16 | 17 | IDENTIFIER_FIELD_NUMBER: builtins.int 18 | USERDATA_FIELD_NUMBER: builtins.int 19 | identifier: builtins.str 20 | userData: builtins.bytes 21 | def __init__( 22 | self, 23 | *, 24 | identifier: builtins.str | None = ..., 25 | userData: builtins.bytes | None = ..., 26 | ) -> None: ... 27 | def HasField(self, field_name: typing.Literal["identifier", b"identifier", "userData", b"userData"]) -> builtins.bool: ... 28 | def ClearField(self, field_name: typing.Literal["identifier", b"identifier", "userData", b"userData"]) -> None: ... 29 | 30 | global___TransactionKey = TransactionKey 31 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/TransactionMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/TransactionPackets.proto"; 4 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 5 | import "pyatv/protocols/mrp/protobuf/PlayerPath.proto"; 6 | 7 | extend ProtocolMessage { 8 | optional TransactionMessage transactionMessage = 38; 9 | } 10 | 11 | message TransactionMessage { 12 | optional uint64 name = 1; 13 | optional TransactionPackets packets = 2; 14 | optional PlayerPath playerPath = 3; 15 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/TransactionPacket.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/TransactionKey.proto"; 4 | 5 | message TransactionPacket { 6 | optional TransactionKey key = 1; 7 | optional bytes packetData = 2; 8 | optional string identifier = 3; 9 | optional uint64 totalLength = 4; 10 | optional uint64 totalWritePosition = 5; 11 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/TransactionPacket_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import pyatv.protocols.mrp.protobuf.TransactionKey_pb2 10 | import typing 11 | 12 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 13 | 14 | @typing.final 15 | class TransactionPacket(google.protobuf.message.Message): 16 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 17 | 18 | KEY_FIELD_NUMBER: builtins.int 19 | PACKETDATA_FIELD_NUMBER: builtins.int 20 | IDENTIFIER_FIELD_NUMBER: builtins.int 21 | TOTALLENGTH_FIELD_NUMBER: builtins.int 22 | TOTALWRITEPOSITION_FIELD_NUMBER: builtins.int 23 | packetData: builtins.bytes 24 | identifier: builtins.str 25 | totalLength: builtins.int 26 | totalWritePosition: builtins.int 27 | @property 28 | def key(self) -> pyatv.protocols.mrp.protobuf.TransactionKey_pb2.TransactionKey: ... 29 | def __init__( 30 | self, 31 | *, 32 | key: pyatv.protocols.mrp.protobuf.TransactionKey_pb2.TransactionKey | None = ..., 33 | packetData: builtins.bytes | None = ..., 34 | identifier: builtins.str | None = ..., 35 | totalLength: builtins.int | None = ..., 36 | totalWritePosition: builtins.int | None = ..., 37 | ) -> None: ... 38 | def HasField(self, field_name: typing.Literal["identifier", b"identifier", "key", b"key", "packetData", b"packetData", "totalLength", b"totalLength", "totalWritePosition", b"totalWritePosition"]) -> builtins.bool: ... 39 | def ClearField(self, field_name: typing.Literal["identifier", b"identifier", "key", b"key", "packetData", b"packetData", "totalLength", b"totalLength", "totalWritePosition", b"totalWritePosition"]) -> None: ... 40 | 41 | global___TransactionPacket = TransactionPacket 42 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/TransactionPackets.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/TransactionPacket.proto"; 4 | 5 | message TransactionPackets { 6 | repeated TransactionPacket packets = 1; 7 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/TransactionPackets_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: pyatv/protocols/mrp/protobuf/TransactionPackets.proto 5 | # Protobuf Python Version: 6.30.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 6, 15 | 30, 16 | 2, 17 | '', 18 | 'pyatv/protocols/mrp/protobuf/TransactionPackets.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | from pyatv.protocols.mrp.protobuf import TransactionPacket_pb2 as pyatv_dot_protocols_dot_mrp_dot_protobuf_dot_TransactionPacket__pb2 26 | 27 | 28 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n5pyatv/protocols/mrp/protobuf/TransactionPackets.proto\x1a\x34pyatv/protocols/mrp/protobuf/TransactionPacket.proto\"9\n\x12TransactionPackets\x12#\n\x07packets\x18\x01 \x03(\x0b\x32\x12.TransactionPacket') 29 | 30 | _globals = globals() 31 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 32 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pyatv.protocols.mrp.protobuf.TransactionPackets_pb2', _globals) 33 | if not _descriptor._USE_C_DESCRIPTORS: 34 | DESCRIPTOR._loaded_options = None 35 | _globals['_TRANSACTIONPACKETS']._serialized_start=111 36 | _globals['_TRANSACTIONPACKETS']._serialized_end=168 37 | # @@protoc_insertion_point(module_scope) 38 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/TransactionPackets_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import collections.abc 8 | import google.protobuf.descriptor 9 | import google.protobuf.internal.containers 10 | import google.protobuf.message 11 | import pyatv.protocols.mrp.protobuf.TransactionPacket_pb2 12 | import typing 13 | 14 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 15 | 16 | @typing.final 17 | class TransactionPackets(google.protobuf.message.Message): 18 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 19 | 20 | PACKETS_FIELD_NUMBER: builtins.int 21 | @property 22 | def packets(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[pyatv.protocols.mrp.protobuf.TransactionPacket_pb2.TransactionPacket]: ... 23 | def __init__( 24 | self, 25 | *, 26 | packets: collections.abc.Iterable[pyatv.protocols.mrp.protobuf.TransactionPacket_pb2.TransactionPacket] | None = ..., 27 | ) -> None: ... 28 | def ClearField(self, field_name: typing.Literal["packets", b"packets"]) -> None: ... 29 | 30 | global___TransactionPackets = TransactionPackets 31 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/UpdateClientMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/NowPlayingClient.proto"; 5 | 6 | extend ProtocolMessage { 7 | optional UpdateClientMessage updateClientMessage = 59; 8 | } 9 | 10 | message UpdateClientMessage { 11 | optional NowPlayingClient client = 1; 12 | } 13 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/UpdateClientMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.NowPlayingClient_pb2 11 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 12 | import typing 13 | 14 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 15 | 16 | @typing.final 17 | class UpdateClientMessage(google.protobuf.message.Message): 18 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 19 | 20 | CLIENT_FIELD_NUMBER: builtins.int 21 | @property 22 | def client(self) -> pyatv.protocols.mrp.protobuf.NowPlayingClient_pb2.NowPlayingClient: ... 23 | def __init__( 24 | self, 25 | *, 26 | client: pyatv.protocols.mrp.protobuf.NowPlayingClient_pb2.NowPlayingClient | None = ..., 27 | ) -> None: ... 28 | def HasField(self, field_name: typing.Literal["client", b"client"]) -> builtins.bool: ... 29 | def ClearField(self, field_name: typing.Literal["client", b"client"]) -> None: ... 30 | 31 | global___UpdateClientMessage = UpdateClientMessage 32 | 33 | UPDATECLIENTMESSAGE_FIELD_NUMBER: builtins.int 34 | updateClientMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___UpdateClientMessage] 35 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/UpdateContentItemArtworkMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/ContentItem.proto"; 5 | import "pyatv/protocols/mrp/protobuf/PlayerPath.proto"; 6 | 7 | extend ProtocolMessage { 8 | optional UpdateContentItemArtworkMessage updateContentItemArtworkMessage = 61; 9 | } 10 | 11 | message UpdateContentItemArtworkMessage { 12 | repeated ContentItem contentItems = 1; 13 | optional PlayerPath playerPath = 2; 14 | } 15 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/UpdateContentItemMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/ContentItem.proto"; 5 | import "pyatv/protocols/mrp/protobuf/PlayerPath.proto"; 6 | 7 | extend ProtocolMessage { 8 | optional UpdateContentItemMessage updateContentItemMessage = 60; 9 | } 10 | 11 | message UpdateContentItemMessage { 12 | repeated ContentItem contentItems = 1; 13 | optional PlayerPath playerPath = 2; 14 | } 15 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/UpdateEndPointsMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional UpdateEndPointsMessage updateEndPointsMessage = 83; 7 | } 8 | 9 | message AVEndpointDescriptor { 10 | optional string name = 1; 11 | optional string uniqueIdentifier = 2; 12 | // repeated ... outputDevices = 3; 13 | // optional ... designatedGroupLeader = 4; 14 | optional bool isLocalEndpoint = 5; 15 | optional string instanceIdentifier = 6; 16 | optional bool isProxyGroupPlayer = 7; 17 | optional int32 connectionType = 8; 18 | optional bool canModifyGroupMembership = 9; 19 | // repeated ... _personalOutputDevices = 10; 20 | } 21 | 22 | message UpdateEndPointsMessage { 23 | optional AVEndpointDescriptor endpoints = 1; 24 | optional int32 endpointFeatures = 2; 25 | } 26 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/UpdatePlayerPath.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/PlayerPath.proto"; 5 | 6 | extend ProtocolMessage { 7 | optional UpdatePlayerMessage updatePlayerMessage = 62; 8 | } 9 | 10 | message UpdatePlayerMessage { 11 | optional PlayerPath playerPath = 1; 12 | } 13 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/UpdatePlayerPath_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.PlayerPath_pb2 11 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 12 | import typing 13 | 14 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 15 | 16 | @typing.final 17 | class UpdatePlayerMessage(google.protobuf.message.Message): 18 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 19 | 20 | PLAYERPATH_FIELD_NUMBER: builtins.int 21 | @property 22 | def playerPath(self) -> pyatv.protocols.mrp.protobuf.PlayerPath_pb2.PlayerPath: ... 23 | def __init__( 24 | self, 25 | *, 26 | playerPath: pyatv.protocols.mrp.protobuf.PlayerPath_pb2.PlayerPath | None = ..., 27 | ) -> None: ... 28 | def HasField(self, field_name: typing.Literal["playerPath", b"playerPath"]) -> builtins.bool: ... 29 | def ClearField(self, field_name: typing.Literal["playerPath", b"playerPath"]) -> None: ... 30 | 31 | global___UpdatePlayerMessage = UpdatePlayerMessage 32 | 33 | UPDATEPLAYERMESSAGE_FIELD_NUMBER: builtins.int 34 | updatePlayerMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___UpdatePlayerMessage] 35 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/VirtualTouchDeviceDescriptorMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | message VirtualTouchDeviceDescriptor { 4 | optional bool absolute = 1; 5 | optional bool integratedDisplay = 2; 6 | optional float screenSizeWidth = 3; 7 | optional float screenSizeHeight = 4; 8 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/VirtualTouchDeviceDescriptorMessage_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: pyatv/protocols/mrp/protobuf/VirtualTouchDeviceDescriptorMessage.proto 5 | # Protobuf Python Version: 6.30.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 6, 15 | 30, 16 | 2, 17 | '', 18 | 'pyatv/protocols/mrp/protobuf/VirtualTouchDeviceDescriptorMessage.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | 26 | 27 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\nFpyatv/protocols/mrp/protobuf/VirtualTouchDeviceDescriptorMessage.proto\"~\n\x1cVirtualTouchDeviceDescriptor\x12\x10\n\x08\x61\x62solute\x18\x01 \x01(\x08\x12\x19\n\x11integratedDisplay\x18\x02 \x01(\x08\x12\x17\n\x0fscreenSizeWidth\x18\x03 \x01(\x02\x12\x18\n\x10screenSizeHeight\x18\x04 \x01(\x02') 28 | 29 | _globals = globals() 30 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 31 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pyatv.protocols.mrp.protobuf.VirtualTouchDeviceDescriptorMessage_pb2', _globals) 32 | if not _descriptor._USE_C_DESCRIPTORS: 33 | DESCRIPTOR._loaded_options = None 34 | _globals['_VIRTUALTOUCHDEVICEDESCRIPTOR']._serialized_start=74 35 | _globals['_VIRTUALTOUCHDEVICEDESCRIPTOR']._serialized_end=200 36 | # @@protoc_insertion_point(module_scope) 37 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/VirtualTouchDeviceDescriptorMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import typing 10 | 11 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 12 | 13 | @typing.final 14 | class VirtualTouchDeviceDescriptor(google.protobuf.message.Message): 15 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 16 | 17 | ABSOLUTE_FIELD_NUMBER: builtins.int 18 | INTEGRATEDDISPLAY_FIELD_NUMBER: builtins.int 19 | SCREENSIZEWIDTH_FIELD_NUMBER: builtins.int 20 | SCREENSIZEHEIGHT_FIELD_NUMBER: builtins.int 21 | absolute: builtins.bool 22 | integratedDisplay: builtins.bool 23 | screenSizeWidth: builtins.float 24 | screenSizeHeight: builtins.float 25 | def __init__( 26 | self, 27 | *, 28 | absolute: builtins.bool | None = ..., 29 | integratedDisplay: builtins.bool | None = ..., 30 | screenSizeWidth: builtins.float | None = ..., 31 | screenSizeHeight: builtins.float | None = ..., 32 | ) -> None: ... 33 | def HasField(self, field_name: typing.Literal["absolute", b"absolute", "integratedDisplay", b"integratedDisplay", "screenSizeHeight", b"screenSizeHeight", "screenSizeWidth", b"screenSizeWidth"]) -> builtins.bool: ... 34 | def ClearField(self, field_name: typing.Literal["absolute", b"absolute", "integratedDisplay", b"integratedDisplay", "screenSizeHeight", b"screenSizeHeight", "screenSizeWidth", b"screenSizeWidth"]) -> None: ... 35 | 36 | global___VirtualTouchDeviceDescriptor = VirtualTouchDeviceDescriptor 37 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/VoiceInputDeviceDescriptorMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/AudioFormatSettingsMessage.proto"; 4 | 5 | message VoiceInputDeviceDescriptor { 6 | optional AudioFormatSettings defaultFormat = 1; 7 | repeated AudioFormatSettings supportedFormats = 2; 8 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/VoiceInputDeviceDescriptorMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import collections.abc 8 | import google.protobuf.descriptor 9 | import google.protobuf.internal.containers 10 | import google.protobuf.message 11 | import pyatv.protocols.mrp.protobuf.AudioFormatSettingsMessage_pb2 12 | import typing 13 | 14 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 15 | 16 | @typing.final 17 | class VoiceInputDeviceDescriptor(google.protobuf.message.Message): 18 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 19 | 20 | DEFAULTFORMAT_FIELD_NUMBER: builtins.int 21 | SUPPORTEDFORMATS_FIELD_NUMBER: builtins.int 22 | @property 23 | def defaultFormat(self) -> pyatv.protocols.mrp.protobuf.AudioFormatSettingsMessage_pb2.AudioFormatSettings: ... 24 | @property 25 | def supportedFormats(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[pyatv.protocols.mrp.protobuf.AudioFormatSettingsMessage_pb2.AudioFormatSettings]: ... 26 | def __init__( 27 | self, 28 | *, 29 | defaultFormat: pyatv.protocols.mrp.protobuf.AudioFormatSettingsMessage_pb2.AudioFormatSettings | None = ..., 30 | supportedFormats: collections.abc.Iterable[pyatv.protocols.mrp.protobuf.AudioFormatSettingsMessage_pb2.AudioFormatSettings] | None = ..., 31 | ) -> None: ... 32 | def HasField(self, field_name: typing.Literal["defaultFormat", b"defaultFormat"]) -> builtins.bool: ... 33 | def ClearField(self, field_name: typing.Literal["defaultFormat", b"defaultFormat", "supportedFormats", b"supportedFormats"]) -> None: ... 34 | 35 | global___VoiceInputDeviceDescriptor = VoiceInputDeviceDescriptor 36 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/VolumeControlAvailabilityMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional VolumeControlAvailabilityMessage volumeControlAvailabilityMessage = 22; 7 | } 8 | 9 | message VolumeCapabilities { 10 | // This is really a bitmap but protobuf has no type for that, so lets just add a "Both" 11 | // option since only two values exist anyway 12 | enum Enum { 13 | None = 0; 14 | Relative = 1; 15 | Absolute = 2; 16 | Both = 3; 17 | } 18 | } 19 | 20 | message VolumeControlAvailabilityMessage { 21 | optional bool volumeControlAvailable = 1; 22 | optional VolumeCapabilities.Enum volumeCapabilities = 2; 23 | } 24 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/VolumeControlCapabilitiesDidChangeMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | import "pyatv/protocols/mrp/protobuf/VolumeControlAvailabilityMessage.proto"; 5 | 6 | extend ProtocolMessage { 7 | optional VolumeControlCapabilitiesDidChangeMessage volumeControlCapabilitiesDidChangeMessage = 68; 8 | } 9 | 10 | message VolumeControlCapabilitiesDidChangeMessage { 11 | optional VolumeControlAvailabilityMessage capabilities = 1; 12 | optional string endpointUID = 3; 13 | optional string outputDeviceUID = 4; 14 | } 15 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/VolumeDidChangeMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional VolumeDidChangeMessage volumeDidChangeMessage = 56; 7 | } 8 | 9 | message VolumeDidChangeMessage { 10 | optional float volume = 1; 11 | optional string endpointUID = 2; 12 | optional string outputDeviceUID = 3; 13 | } -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/VolumeDidChangeMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class VolumeDidChangeMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | VOLUME_FIELD_NUMBER: builtins.int 20 | ENDPOINTUID_FIELD_NUMBER: builtins.int 21 | OUTPUTDEVICEUID_FIELD_NUMBER: builtins.int 22 | volume: builtins.float 23 | endpointUID: builtins.str 24 | outputDeviceUID: builtins.str 25 | def __init__( 26 | self, 27 | *, 28 | volume: builtins.float | None = ..., 29 | endpointUID: builtins.str | None = ..., 30 | outputDeviceUID: builtins.str | None = ..., 31 | ) -> None: ... 32 | def HasField(self, field_name: typing.Literal["endpointUID", b"endpointUID", "outputDeviceUID", b"outputDeviceUID", "volume", b"volume"]) -> builtins.bool: ... 33 | def ClearField(self, field_name: typing.Literal["endpointUID", b"endpointUID", "outputDeviceUID", b"outputDeviceUID", "volume", b"volume"]) -> None: ... 34 | 35 | global___VolumeDidChangeMessage = VolumeDidChangeMessage 36 | 37 | VOLUMEDIDCHANGEMESSAGE_FIELD_NUMBER: builtins.int 38 | volumeDidChangeMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___VolumeDidChangeMessage] 39 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/WakeDeviceMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "pyatv/protocols/mrp/protobuf/ProtocolMessage.proto"; 4 | 5 | extend ProtocolMessage { 6 | optional WakeDeviceMessage wakeDeviceMessage = 45; 7 | } 8 | 9 | message WakeDeviceMessage { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/WakeDeviceMessage_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: pyatv/protocols/mrp/protobuf/WakeDeviceMessage.proto 5 | # Protobuf Python Version: 6.30.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 6, 15 | 30, 16 | 2, 17 | '', 18 | 'pyatv/protocols/mrp/protobuf/WakeDeviceMessage.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | from pyatv.protocols.mrp.protobuf import ProtocolMessage_pb2 as pyatv_dot_protocols_dot_mrp_dot_protobuf_dot_ProtocolMessage__pb2 26 | 27 | 28 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n4pyatv/protocols/mrp/protobuf/WakeDeviceMessage.proto\x1a\x32pyatv/protocols/mrp/protobuf/ProtocolMessage.proto\"\x13\n\x11WakeDeviceMessage:?\n\x11wakeDeviceMessage\x12\x10.ProtocolMessage\x18- \x01(\x0b\x32\x12.WakeDeviceMessage') 29 | 30 | _globals = globals() 31 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 32 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pyatv.protocols.mrp.protobuf.WakeDeviceMessage_pb2', _globals) 33 | if not _descriptor._USE_C_DESCRIPTORS: 34 | DESCRIPTOR._loaded_options = None 35 | _globals['_WAKEDEVICEMESSAGE']._serialized_start=108 36 | _globals['_WAKEDEVICEMESSAGE']._serialized_end=127 37 | # @@protoc_insertion_point(module_scope) 38 | -------------------------------------------------------------------------------- /pyatv/protocols/mrp/protobuf/WakeDeviceMessage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2 11 | import typing 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | @typing.final 16 | class WakeDeviceMessage(google.protobuf.message.Message): 17 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 18 | 19 | def __init__( 20 | self, 21 | ) -> None: ... 22 | 23 | global___WakeDeviceMessage = WakeDeviceMessage 24 | 25 | WAKEDEVICEMESSAGE_FIELD_NUMBER: builtins.int 26 | wakeDeviceMessage: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[pyatv.protocols.mrp.protobuf.ProtocolMessage_pb2.ProtocolMessage, global___WakeDeviceMessage] 27 | -------------------------------------------------------------------------------- /pyatv/protocols/raop/packets.py: -------------------------------------------------------------------------------- 1 | """Packet formats used by RAOP.""" 2 | 3 | from pyatv.support.packet import defpacket 4 | 5 | RtpHeader = defpacket("RtpHeader", proto="B", type="B", seqno="H") 6 | 7 | TimingPacket = RtpHeader.extend( 8 | "TimingPacket", 9 | padding="I", 10 | reftime_sec="I", 11 | reftime_frac="I", 12 | recvtime_sec="I", 13 | recvtime_frac="I", 14 | sendtime_sec="I", 15 | sendtime_frac="I", 16 | ) 17 | 18 | SyncPacket = RtpHeader.extend( 19 | "SyncPacket", 20 | now_without_latency="I", 21 | last_sync_sec="I", 22 | last_sync_frac="I", 23 | now="I", 24 | ) 25 | 26 | # NB: Audio payload is not included here, shall be appended manually 27 | AudioPacketHeader = RtpHeader.extend( 28 | "AudioPacketHeader", 29 | timestamp="I", 30 | ssrc="I", 31 | ) 32 | 33 | RetransmitReqeust = RtpHeader.extend( 34 | "RetransmitPacket", lost_seqno="H", lost_packets="H" 35 | ) 36 | -------------------------------------------------------------------------------- /pyatv/protocols/raop/timing.py: -------------------------------------------------------------------------------- 1 | """Methods for working with time and synchronization in RAOP. 2 | 3 | The timing routines in this module is based on the excellent work of RAOP-Player: 4 | https://github.com/philippe44/RAOP-Player 5 | """ 6 | 7 | from time import time_ns 8 | from typing import Tuple 9 | 10 | 11 | def ntp_now() -> int: 12 | """Return current time in NTP format.""" 13 | now_us = time_ns() / 1000 14 | seconds = int(now_us / 1000000) 15 | frac = int(now_us - seconds * 1000000) 16 | return (seconds + 0x83AA7E80) << 32 | (int((frac << 32) / 1000000)) 17 | 18 | 19 | def ntp2parts(ntp: int) -> Tuple[int, int]: 20 | """Split NTP time into seconds and fraction.""" 21 | return ntp >> 32, ntp & 0xFFFFFFFF 22 | 23 | 24 | def ntp2ts(ntp: int, rate: int) -> int: 25 | """Convert NTP time into timestamp.""" 26 | return int((ntp >> 16) * rate) >> 16 27 | 28 | 29 | def ts2ntp(timestamp: int, rate: int) -> int: 30 | """Convert timestamp into NTP time.""" 31 | return int(int(timestamp << 16) / rate) << 16 32 | 33 | 34 | def ntp2ms(ntp: int) -> int: 35 | """Convert NTP time to milliseconds.""" 36 | return ((ntp >> 10) * 1000) >> 22 37 | 38 | 39 | def ts2ms(timestamp: int, rate: int) -> int: 40 | """Convert timestamp to milliseconds.""" 41 | return ntp2ms(ts2ntp(timestamp, rate)) 42 | -------------------------------------------------------------------------------- /pyatv/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/postlund/pyatv/2fbdbd444b80048876e2ab6726ac945901e883b4/pyatv/py.typed -------------------------------------------------------------------------------- /pyatv/storage/memory_storage.py: -------------------------------------------------------------------------------- 1 | """Memory storage module.""" 2 | 3 | from pyatv.storage import AbstractStorage 4 | 5 | __pdoc_dev_page__ = "/development/storage" 6 | 7 | 8 | class MemoryStorage(AbstractStorage): 9 | """Memory based storage module. 10 | 11 | This storage module stores settings in memory and everything stored with it will 12 | be forgotten when restarting the python interpreter. 13 | """ 14 | 15 | async def save(self) -> None: 16 | """Save settings to active storage.""" 17 | self.mark_as_saved() 18 | 19 | async def load(self) -> None: 20 | """Load settings from active storage.""" 21 | 22 | def __str__(self) -> str: 23 | """Return string representation of MemoryStorage.""" 24 | return "MemoryStorage" 25 | -------------------------------------------------------------------------------- /pyatv/support/cache.py: -------------------------------------------------------------------------------- 1 | """Simple LRU cache for data based on an identifier.""" 2 | 3 | from collections import OrderedDict 4 | 5 | 6 | class Cache: 7 | """Implementation of simple LRU cache.""" 8 | 9 | def __init__(self, limit=16): 10 | """Initialize a new Cache instance.""" 11 | self.limit = limit 12 | self.data = OrderedDict() 13 | 14 | def empty(self): 15 | """Return if cache is empty or not.""" 16 | return not self.data 17 | 18 | def put(self, identifier, data): 19 | """Put something in the cache.""" 20 | try: 21 | self.data.pop(identifier) 22 | except KeyError: 23 | if len(self.data) >= self.limit: 24 | self.data.popitem(last=False) 25 | finally: 26 | self.data[identifier] = data 27 | 28 | def get(self, identifier): 29 | """Get something from the cache.""" 30 | value = self.data.pop(identifier) 31 | self.data[identifier] = value 32 | return value 33 | 34 | def latest(self): 35 | """Return identifier of last recently used identifier.""" 36 | if self.empty(): 37 | return None 38 | return list(self.data.keys())[-1] 39 | 40 | def __contains__(self, identifier): 41 | """Check if something is in the cache.""" 42 | return identifier in self.data 43 | 44 | def __len__(self): 45 | """Return number of elements in cache.""" 46 | return len(self.data) 47 | -------------------------------------------------------------------------------- /pyatv/support/metadata.py: -------------------------------------------------------------------------------- 1 | """Convenience methods for extracting metadata from an audio file.""" 2 | 3 | import asyncio 4 | import io 5 | from typing import Union 6 | 7 | from tinytag import TinyTag 8 | 9 | from pyatv.interface import MediaMetadata 10 | 11 | EMPTY_METADATA = MediaMetadata(None, None, None, None) 12 | 13 | 14 | def _open_file(file: io.BufferedIOBase) -> TinyTag: 15 | start_position = file.tell() 16 | in_file = TinyTag.get(file_obj=file) 17 | file.seek(start_position) 18 | return in_file 19 | 20 | 21 | async def get_metadata(file: Union[str, io.BufferedIOBase]) -> MediaMetadata: 22 | """Extract metadata from a file and return it.""" 23 | loop = asyncio.get_event_loop() 24 | 25 | # TODO: TinyTag will always start by seeking to the end of a 26 | # file, which isn't possible for streaming buffers. So this 27 | # works as long as the entire file is in the buffer, otherwise 28 | # it will fail. Hopefully this can be fixed by using mutagen 29 | # directly, but will require some manual handling. 30 | if isinstance(file, str): 31 | tag = await loop.run_in_executor(None, TinyTag.get, file) 32 | else: 33 | tag = await loop.run_in_executor(None, _open_file, file) 34 | 35 | return MediaMetadata( 36 | title=tag.title, 37 | artist=tag.artist, 38 | album=tag.album, 39 | duration=tag.duration, 40 | ) 41 | 42 | 43 | def merge_into(base: MediaMetadata, new_metadata: MediaMetadata) -> MediaMetadata: 44 | """Merge missing fields into base metadata. 45 | 46 | Updates all fields with a None value in "new" with corresponding values from 47 | "new_metadata". Returns "base" again. 48 | """ 49 | for field in base.__dataclass_fields__.keys(): 50 | if getattr(base, field) is None: 51 | setattr(base, field, getattr(new_metadata, field)) 52 | return base 53 | -------------------------------------------------------------------------------- /pyatv/support/packet.py: -------------------------------------------------------------------------------- 1 | """Generic utility for encoding and decoding binary packets.""" 2 | 3 | from collections import namedtuple 4 | import struct 5 | 6 | 7 | def defpacket(name: str, **kwargs): 8 | """Define a protocol packet.""" 9 | fmt: str = ">" + "".join(kwargs.values()) 10 | msg_type = namedtuple(name, kwargs.keys()) # type: ignore 11 | 12 | class _MessageType: 13 | length = struct.calcsize(fmt) 14 | 15 | @staticmethod 16 | def decode(data: bytes, allow_excessive=False): 17 | """Decode binary data as message.""" 18 | return msg_type._make( 19 | struct.unpack( 20 | fmt, data if not allow_excessive else data[0 : struct.calcsize(fmt)] 21 | ) 22 | ) 23 | 24 | @staticmethod 25 | def encode(*args) -> bytes: 26 | """Encode a message into binary data.""" 27 | return struct.pack(fmt, *args) 28 | 29 | @staticmethod 30 | def extend(ext_name, **ext_kwargs): 31 | """Extend a message type with additional fields.""" 32 | fields = {**kwargs, **ext_kwargs} 33 | return defpacket(ext_name, **fields) 34 | 35 | return _MessageType 36 | -------------------------------------------------------------------------------- /pyatv/support/pydantic_compat.py: -------------------------------------------------------------------------------- 1 | """Compatibility module for pydantic. 2 | 3 | This module provides some compatibility methods to support both pydantic v1 and v2 in 4 | pyatv. Ideally only v2 should be supported, but due to Home Assistant being stuck at v1 5 | for now, backwards compatibility will be provided until that is resolved. More info in 6 | https://github.com/postlund/pyatv/issues/2261. 7 | 8 | The idea is that the rest of the code never imports anything directly from pydantic, 9 | but instead getting import from here. That makes it easy to remove these changes 10 | later on. 11 | """ 12 | 13 | from typing import Any, Mapping 14 | 15 | # pylint: disable=unused-import 16 | 17 | try: 18 | from pydantic.v1 import BaseModel, Field, ValidationError # noqa 19 | from pydantic.v1 import validator as field_validator # noqa 20 | except ImportError: 21 | from pydantic import BaseModel, Field, ValidationError # noqa 22 | from pydantic import validator as field_validator # noqa 23 | 24 | # pylint: enable=unused-import 25 | 26 | 27 | def model_copy(model: BaseModel, /, update: Mapping[str, Any]) -> BaseModel: 28 | """Model copy compatible with pydantic v2. 29 | 30 | Seems like pydantic v1 carries over keys with None values even though target model 31 | doesn't have the key. Not the case with v2. This method removes keys with None 32 | values. 33 | """ 34 | return model.copy( 35 | update={key: value for key, value in update.items() if value is not None} 36 | ) 37 | -------------------------------------------------------------------------------- /pyatv/support/url.py: -------------------------------------------------------------------------------- 1 | """Helpers for working with URLs.""" 2 | 3 | from urllib.parse import urlparse 4 | 5 | 6 | def is_url(url): 7 | """Check if something is a URL.""" 8 | url_parts = urlparse(url) 9 | return bool(url_parts.scheme and url_parts.netloc) 10 | 11 | 12 | def is_url_or_scheme(url): 13 | """Check if something is a URL or a URL scheme.""" 14 | url_parts = urlparse(url) 15 | return bool(url_parts.scheme) 16 | -------------------------------------------------------------------------------- /pyatv/support/variant.py: -------------------------------------------------------------------------------- 1 | """Module to read and write Google protobuf variants.""" 2 | 3 | 4 | def read_variant(variant): 5 | """Read and parse a binary protobuf variant value.""" 6 | result = 0 7 | cnt = 0 8 | for data in variant: 9 | result |= (data & 0x7F) << (7 * cnt) 10 | cnt += 1 11 | if not data & 0x80: 12 | return result, variant[cnt:] 13 | raise ValueError("invalid variant") 14 | 15 | 16 | def write_variant(number): 17 | """Convert an integer to a protobuf variant binary buffer.""" 18 | if number < 128: 19 | return bytes([number]) 20 | return bytes([(number & 0x7F) | 0x80]) + write_variant(number >> 7) 21 | -------------------------------------------------------------------------------- /pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | load-plugins=pylint.extensions.no_self_use 3 | reports=no 4 | 5 | [BASIC] 6 | good-names= 7 | ex, 8 | rd, 9 | f, 10 | i, 11 | T, 12 | ws 13 | 14 | [FORMAT] 15 | max-line-length=88 16 | 17 | [TYPECHECK] 18 | ignore=protobuf 19 | ignored-modules=pyatv.protocols.mrp.protobuf 20 | ignored-classes= 21 | ProtocolMessage, 22 | SetConnectionStateMessage, 23 | SetStateMessage, 24 | ContentItemMetadata, 25 | CommandInfo, 26 | PlaybackState, 27 | ShuffleMode, 28 | RepeatMode, 29 | HandlerReturnStatus, 30 | SendError, 31 | DeviceClass 32 | 33 | disable= 34 | broad-except, 35 | cyclic-import, # TODO: Bug in pylint? 36 | locally-disabled, 37 | duplicate-code, 38 | fixme, 39 | unused-argument, 40 | no-self-use, 41 | too-few-public-methods, 42 | too-many-public-methods, 43 | too-many-arguments, 44 | too-many-instance-attributes, 45 | too-many-lines, 46 | too-many-positional-arguments 47 | 48 | [EXCEPTIONS] 49 | overgeneral-exceptions=builtins.Exception 50 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | target-version = ["py35", "py36", "py37", "py38"] 3 | extend-exclude = '(protobuf/(__init__|.*_pb2).py)|__pycache__' 4 | include = '(pyatv|tests|examples|scripts).*\.py' 5 | 6 | [tool.isort] 7 | skip_glob = "pyatv/protocols/mrp/protobuf/*.py" 8 | profile = "black" 9 | force_sort_within_sections = true 10 | known_first_party = [ 11 | "pyatv", 12 | "tests", 13 | "scripts", 14 | ] 15 | forced_separate = [ 16 | "tests", 17 | "scripts", 18 | ] 19 | 20 | [[tool.mypy.overrides]] 21 | module = [ 22 | "miniaudio", 23 | "audio_metadata", 24 | "srptools", 25 | ] 26 | ignore_missing_imports = true 27 | -------------------------------------------------------------------------------- /requirements/requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.11.16 2 | async-timeout==5.0.1 3 | cryptography==44.0.2 4 | chacha20poly1305-reuseable==0.13.2 5 | ifaddr==0.2.0 6 | ifaddr==0.2.0 7 | miniaudio==1.61 8 | protobuf==6.30.2 9 | pydantic==2.11.3 10 | requests==2.32.3 11 | srptools==1.0.1 12 | tabulate==0.9.0 13 | tinytag==2.1.0 14 | zeroconf==0.146.3 -------------------------------------------------------------------------------- /requirements/requirements_docs.txt: -------------------------------------------------------------------------------- 1 | codespell==2.4.1 2 | pdoc3==0.11.6 3 | -------------------------------------------------------------------------------- /requirements/requirements_test.txt: -------------------------------------------------------------------------------- 1 | black==25.1.0 2 | deepdiff==8.4.2 3 | flake8==7.2.0 4 | isort==6.0.1 5 | mutagen==1.47.0 6 | pyfakefs==5.8.0 7 | pylint==3.3.6 8 | pytest==8.3.5 9 | pytest-asyncio==0.26.0 10 | pytest-cov==6.1.1 11 | pytest-timeout==2.3.1 12 | pytest-aiohttp==1.0.5 13 | pytest_httpserver==1.1.2 14 | pytest-xdist==3.6.1 15 | pydocstyle==6.3.0 16 | mypy==1.15.0 17 | mypy-protobuf==3.6.0 18 | types-protobuf==5.29.1.20250403 19 | types-requests==2.32.0.20250328 20 | types-tabulate==0.9.0.20241207 21 | -------------------------------------------------------------------------------- /scripts/build_docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | run() 4 | { 5 | docker run -p 4000:4000 --rm \ 6 | -v "$PWD:/srv/jekyll" \ 7 | -v "$PWD/docs/vendor/bundle:/usr/local/bundle" \ 8 | -w /srv/jekyll/docs \ 9 | -it jekyll/jekyll:3.8 "$@" 10 | } 11 | 12 | if [[ $GITPOD_INSTANCE_ID ]]; then 13 | cd docs && bundle install && bundle update github-pages && bundle exec jekyll serve --incremental --watch 14 | else 15 | run bundle update github-pages 16 | run jekyll serve --incremental --watch 17 | fi 18 | -------------------------------------------------------------------------------- /scripts/features.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Create feature list enum from code.""" 3 | from pyatv import interface 4 | 5 | FEATURE_TEMPLATE = """# This enum is generated by scripts/features.py 6 | class FeatureName(Enum): 7 | \"\"\"All supported features.\"\"\" 8 | 9 | {enum} 10 | """ 11 | 12 | 13 | ENUM_VALUE_TEMPLATE = """ {name} = {index} 14 | \"\"\"{doc}\"\"\" 15 | """ 16 | 17 | 18 | def main(): 19 | """Script starts here.""" 20 | # This is an internal script that is allowed to peek into internals 21 | features = interface._ALL_FEATURES # pylint: disable=protected-access 22 | enum_values = "\n".join( 23 | ENUM_VALUE_TEMPLATE.format(name=tmp[0], index=index, doc=tmp[1]) 24 | for index, tmp in features.items() 25 | ) 26 | print(FEATURE_TEMPLATE.format(enum=enum_values)) 27 | 28 | print("Next free index:", max(features.keys()) + 1) 29 | 30 | 31 | if __name__ == "__main__": 32 | main() 33 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [aliases] 2 | test=pytest 3 | 4 | [bdist_wheel] 5 | universal = 0 6 | 7 | [tool:pytest] 8 | testpaths = tests 9 | norecursedirs = .git 10 | 11 | [flake8] 12 | exclude = .venv,.git,.tox,docs,venv,bin,lib,deps,build 13 | max-line-length = 88 14 | ignore = E203, W503 15 | 16 | [pydocstyle] 17 | match_dir = ^((?!\.|www_static).)*$ 18 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Test suite for pyatv.""" 2 | -------------------------------------------------------------------------------- /tests/data/README: -------------------------------------------------------------------------------- 1 | Audio files in this directory have been generated by the 2 | commands below in case they ever need to be re-generated. 3 | 4 | Only metadata, no audio: 5 | 6 | python ../../scripts/audiogen.py --title pyatv --artist postlund --album raop -n 0 -o only_metadata.wav 7 | 8 | Audio, 10 frames (=20 samples): 9 | 10 | python ../../scripts/audiogen.py -n 10 -o audio_10_frames.wav 11 | 12 | Audio, 352 * 3 frames = 3 packets (=1056 samples): 13 | 14 | python ../../scripts/audiogen.py -n 1056 -o audio_3_packets.wav 15 | 16 | Audio, 8 seconds static (no sound), ogg for compression: 17 | 18 | python ../../scripts/audiogen.py -n 132300 -s -o static_3sec.wav 19 | ffmpeg -i static_3sec.wav static_3sec.ogg 20 | 21 | Audio, 1 packets (=352 samples), with metadata: 22 | 23 | python ../../scripts/audiogen.py --title pyatv --artist postlund --album raop -n 352 -o audio_1_packet_metadata.wav 24 | -------------------------------------------------------------------------------- /tests/data/audio_10_frames.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/postlund/pyatv/2fbdbd444b80048876e2ab6726ac945901e883b4/tests/data/audio_10_frames.wav -------------------------------------------------------------------------------- /tests/data/audio_1_packet_metadata.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/postlund/pyatv/2fbdbd444b80048876e2ab6726ac945901e883b4/tests/data/audio_1_packet_metadata.wav -------------------------------------------------------------------------------- /tests/data/audio_3_packets.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/postlund/pyatv/2fbdbd444b80048876e2ab6726ac945901e883b4/tests/data/audio_3_packets.wav -------------------------------------------------------------------------------- /tests/data/only_metadata.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/postlund/pyatv/2fbdbd444b80048876e2ab6726ac945901e883b4/tests/data/only_metadata.wav -------------------------------------------------------------------------------- /tests/data/only_title.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/postlund/pyatv/2fbdbd444b80048876e2ab6726ac945901e883b4/tests/data/only_title.wav -------------------------------------------------------------------------------- /tests/data/static_3sec.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/postlund/pyatv/2fbdbd444b80048876e2ab6726ac945901e883b4/tests/data/static_3sec.ogg -------------------------------------------------------------------------------- /tests/data/testfile.txt: -------------------------------------------------------------------------------- 1 | a file for testing -------------------------------------------------------------------------------- /tests/fake_knock.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | 4 | import pytest 5 | 6 | _LOGGER = logging.getLogger(__name__) 7 | 8 | 9 | class KnockServer(asyncio.Protocol): 10 | def __init__(self, port): 11 | self.got_knock = False 12 | self.count = 0 13 | self.port = port 14 | 15 | def connection_made(self, transport): 16 | own = transport.get_extra_info("sockname") 17 | peername = transport.get_extra_info("peername") 18 | _LOGGER.debug("Knock on %s:%d from %s:%d", *own, *peername) 19 | self.transport = transport 20 | 21 | def connection_lost(self, exc): 22 | self.got_knock = True 23 | self.count += 1 24 | self.transport = None 25 | 26 | def data_received(self, data): 27 | assert False, "no data shall be received" 28 | 29 | 30 | async def create_knock_server(port, loop): 31 | server = KnockServer(port) 32 | return await loop.create_server(lambda: server, "127.0.0.1", port), server 33 | -------------------------------------------------------------------------------- /tests/protocols/airplay/conftest.py: -------------------------------------------------------------------------------- 1 | """Shared test code for AirPlay test cases.""" 2 | 3 | import asyncio 4 | 5 | import pytest 6 | import pytest_asyncio 7 | 8 | from pyatv.conf import AppleTV, ManualService 9 | from pyatv.const import Protocol 10 | from pyatv.support.http import http_connect 11 | 12 | from tests.fake_device import FakeAppleTV 13 | 14 | 15 | @pytest_asyncio.fixture(name="airplay_device") 16 | async def airplay_device_fixture(): 17 | fake_atv = FakeAppleTV(asyncio.get_running_loop(), test_mode=False) 18 | fake_atv.add_service(Protocol.AirPlay) 19 | await fake_atv.start() 20 | yield fake_atv 21 | await fake_atv.stop() 22 | 23 | 24 | @pytest_asyncio.fixture(name="client_connection") 25 | async def client_connection_fixture(airplay_device): 26 | yield await http_connect("127.0.0.1", airplay_device.get_port(Protocol.AirPlay)) 27 | 28 | 29 | @pytest.fixture(name="airplay_usecase") 30 | def airplay_usecase_fixture(airplay_device): 31 | yield airplay_device.get_usecase(Protocol.AirPlay) 32 | 33 | 34 | @pytest.fixture(name="airplay_state") 35 | def airplay_state_fixture(airplay_device): 36 | yield airplay_device.get_state(Protocol.AirPlay) 37 | 38 | 39 | @pytest.fixture(name="airplay_properties") 40 | def airplay_properties_fixture(): 41 | yield {} 42 | 43 | 44 | @pytest.fixture(name="airplay_conf") 45 | def airplay_conf_fixture(airplay_device, airplay_properties): 46 | service = ManualService( 47 | "airplay_id", 48 | Protocol.AirPlay, 49 | airplay_device.get_port(Protocol.AirPlay), 50 | airplay_properties, 51 | ) 52 | conf = AppleTV("127.0.0.1", "Apple TV") 53 | conf.add_service(service) 54 | yield conf 55 | -------------------------------------------------------------------------------- /tests/protocols/airplay/test_airplay_interface.py: -------------------------------------------------------------------------------- 1 | """Unit tests for interface implementations in pyatv.protocols.airplay.""" 2 | 3 | import pytest 4 | 5 | from pyatv.const import FeatureName, FeatureState 6 | from pyatv.protocols.airplay import AirPlayFeatures 7 | from pyatv.protocols.airplay.utils import parse_features 8 | 9 | # AirPlayFeatures 10 | 11 | 12 | @pytest.mark.parametrize( 13 | "flags,expected_state", 14 | [ 15 | ("0x0,0x0", FeatureState.Unavailable), 16 | ("0x1,0x0", FeatureState.Available), # VideoV1 17 | ("0x00000000,0x20000", FeatureState.Available), # VideoV2 18 | ], 19 | ) 20 | def test_feature_play_url(flags, expected_state): 21 | features = AirPlayFeatures(parse_features(flags)) 22 | assert features.get_feature(FeatureName.PlayUrl).state == expected_state 23 | -------------------------------------------------------------------------------- /tests/protocols/airplay/test_airplay_scan.py: -------------------------------------------------------------------------------- 1 | """Scanning tests with fake mDNS responder..""" 2 | 3 | from ipaddress import ip_address 4 | 5 | import pytest 6 | 7 | from pyatv.const import Protocol 8 | 9 | from tests import fake_udns 10 | from tests.conftest import Scanner 11 | from tests.utils import assert_device 12 | 13 | IP_1 = "10.0.0.1" 14 | 15 | AIRPLAY_NAME = "AirPlay ATV" 16 | AIRPLAY_ID = "AA:BB:CC:DD:EE:FF" 17 | 18 | pytestmark = pytest.mark.asyncio 19 | 20 | 21 | async def test_multicast_scan_airplay_device(udns_server, multicast_scan: Scanner): 22 | udns_server.add_service( 23 | fake_udns.airplay_service(AIRPLAY_NAME, AIRPLAY_ID, addresses=[IP_1]) 24 | ) 25 | 26 | atvs = await multicast_scan() 27 | assert len(atvs) == 1 28 | assert atvs[0].name == AIRPLAY_NAME 29 | assert atvs[0].identifier == AIRPLAY_ID 30 | assert atvs[0].address == ip_address(IP_1) 31 | 32 | 33 | async def test_unicast_scan_airplay(udns_server, unicast_scan: Scanner): 34 | udns_server.add_service( 35 | fake_udns.airplay_service(AIRPLAY_NAME, AIRPLAY_ID, addresses=[IP_1], port=7000) 36 | ) 37 | 38 | atvs = await unicast_scan() 39 | assert len(atvs) == 1 40 | 41 | assert_device( 42 | atvs[0], 43 | AIRPLAY_NAME, 44 | ip_address(IP_1), 45 | AIRPLAY_ID, 46 | Protocol.AirPlay, 47 | 7000, 48 | ) 49 | -------------------------------------------------------------------------------- /tests/protocols/airplay/test_airplay_verify.py: -------------------------------------------------------------------------------- 1 | """Functional credential verification tests using the API with a fake AirPlay Apple TV.""" 2 | 3 | from contextlib import nullcontext as does_not_raise 4 | 5 | import pytest 6 | 7 | from pyatv.auth.hap_pairing import TRANSIENT_CREDENTIALS, parse_credentials 8 | from pyatv.auth.server_auth import CLIENT_CREDENTIALS 9 | from pyatv.const import Protocol 10 | from pyatv.exceptions import AuthenticationError 11 | from pyatv.protocols.airplay.auth import pair_verify 12 | from pyatv.support import http 13 | 14 | from tests.fake_device.airplay import DEVICE_CREDENTIALS 15 | 16 | pytestmark = pytest.mark.asyncio 17 | 18 | 19 | @pytest.mark.parametrize( 20 | "credentials, expectation", 21 | [ 22 | (parse_credentials(DEVICE_CREDENTIALS), does_not_raise()), 23 | ( 24 | parse_credentials(f"{8 * '00'}:{32 * '11'}"), 25 | pytest.raises(AuthenticationError), 26 | ), 27 | (parse_credentials(CLIENT_CREDENTIALS), does_not_raise()), 28 | ( 29 | parse_credentials(f"{32 * '00'}:{32 * '11'}:{36 * '22'}:{36 * '33'}"), 30 | pytest.raises(AuthenticationError), 31 | ), 32 | (TRANSIENT_CREDENTIALS, does_not_raise()), 33 | ], 34 | ) 35 | async def test_verify(airplay_conf, credentials, expectation): 36 | connection = await http.http_connect( 37 | str(airplay_conf.address), airplay_conf.get_service(Protocol.AirPlay).port 38 | ) 39 | verifier = pair_verify(credentials, connection) 40 | with expectation: 41 | await verifier.verify_credentials() 42 | connection.close() 43 | -------------------------------------------------------------------------------- /tests/protocols/mrp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/postlund/pyatv/2fbdbd444b80048876e2ab6726ac945901e883b4/tests/protocols/mrp/__init__.py -------------------------------------------------------------------------------- /tests/protocols/mrp/conftest.py: -------------------------------------------------------------------------------- 1 | """Fixtures and code shared between MRP tests.""" 2 | 3 | import asyncio 4 | 5 | import pytest 6 | 7 | from pyatv.core.protocol import MessageDispatcher 8 | from pyatv.protocols.mrp import protobuf 9 | 10 | 11 | # This mock is _extremely_ basic, so needs to be adjusted heavily when adding 12 | # new tests 13 | class MrpProtocolMock(MessageDispatcher[int, protobuf.ProtocolMessage]): 14 | def __init__(self): 15 | super().__init__() 16 | self.sent_messages = [] 17 | self.device_info = None 18 | 19 | async def send(self, message): 20 | self.sent_messages.append(message) 21 | 22 | async def inject(self, message: protobuf.ProtocolMessage) -> None: 23 | await asyncio.gather(*self.dispatch(message.type, message)) 24 | 25 | 26 | @pytest.fixture(name="protocol_mock") 27 | def protocol_mock_fixture(event_loop): 28 | yield MrpProtocolMock() 29 | -------------------------------------------------------------------------------- /tests/protocols/mrp/test_mrp_scan.py: -------------------------------------------------------------------------------- 1 | """Functional tests for MRP scanning..""" 2 | 3 | from ipaddress import ip_address 4 | 5 | import pytest 6 | 7 | from pyatv.const import DeviceModel, Protocol 8 | 9 | from tests import fake_udns 10 | from tests.conftest import Scanner 11 | from tests.utils import assert_device 12 | 13 | IP_1 = "10.0.0.1" 14 | 15 | MRP_ID = "mrp_id_1" 16 | MRP_NAME = "MRP ATV" 17 | MRP_SERVICE_NAME = "MRP Service" 18 | 19 | MRP_PORT = 49152 20 | 21 | pytestmark = pytest.mark.asyncio 22 | 23 | 24 | async def test_multicast_scan_mrp_with_companion(udns_server, multicast_scan): 25 | udns_server.add_service( 26 | fake_udns.mrp_service( 27 | MRP_SERVICE_NAME, MRP_NAME, MRP_ID, addresses=[IP_1], port=MRP_PORT 28 | ) 29 | ) 30 | 31 | atvs = await multicast_scan(protocol=Protocol.MRP) 32 | assert len(atvs) == 1 33 | 34 | assert_device(atvs[0], MRP_NAME, ip_address(IP_1), MRP_ID, Protocol.MRP, MRP_PORT) 35 | 36 | 37 | async def test_unicast_scan_mrp(udns_server, unicast_scan): 38 | udns_server.add_service( 39 | fake_udns.mrp_service( 40 | MRP_SERVICE_NAME, MRP_NAME, MRP_ID, addresses=[IP_1], port=MRP_PORT 41 | ) 42 | ) 43 | 44 | atvs = await unicast_scan() 45 | assert len(atvs) == 1 46 | 47 | assert_device(atvs[0], MRP_NAME, ip_address(IP_1), MRP_ID, Protocol.MRP, MRP_PORT) 48 | -------------------------------------------------------------------------------- /tests/protocols/raop/conftest.py: -------------------------------------------------------------------------------- 1 | """Shared test code for RAOP test cases.""" 2 | 3 | import asyncio 4 | from typing import cast 5 | 6 | import pytest 7 | import pytest_asyncio 8 | 9 | from pyatv import connect 10 | from pyatv.conf import AppleTV, ManualService 11 | from pyatv.const import Protocol 12 | 13 | from tests.fake_device import FakeAppleTV, raop 14 | from tests.fake_device.raop import FakeRaopUseCases 15 | 16 | 17 | @pytest_asyncio.fixture(name="raop_device") 18 | async def raop_device_fixture(): 19 | fake_atv = FakeAppleTV(asyncio.get_running_loop(), test_mode=False) 20 | fake_atv.add_service(Protocol.RAOP) 21 | await fake_atv.start() 22 | yield fake_atv 23 | await fake_atv.stop() 24 | 25 | 26 | @pytest.fixture(name="raop_state") 27 | def raop_state_fixture(raop_device): 28 | yield raop_device.get_state(Protocol.RAOP) 29 | 30 | 31 | @pytest.fixture(name="raop_usecase") 32 | def raop_usecase_fixture(raop_device) -> FakeRaopUseCases: 33 | yield cast(FakeRaopUseCases, raop_device.get_usecase(Protocol.RAOP)) 34 | 35 | 36 | @pytest.fixture(name="raop_conf") 37 | def raop_conf_fixture(raop_device, raop_properties): 38 | service = ManualService( 39 | "raop_id", Protocol.RAOP, raop_device.get_port(Protocol.RAOP), raop_properties 40 | ) 41 | conf = AppleTV("127.0.0.1", "Apple TV") 42 | conf.add_service(service) 43 | yield conf 44 | 45 | 46 | @pytest_asyncio.fixture(name="raop_client") 47 | async def raop_client_fixture(raop_conf): 48 | client = await connect(raop_conf, loop=asyncio.get_running_loop()) 49 | yield client 50 | await asyncio.gather(*client.close()) 51 | -------------------------------------------------------------------------------- /tests/protocols/raop/test_raop_scan.py: -------------------------------------------------------------------------------- 1 | """Functional tests for RAOP scanning..""" 2 | 3 | from ipaddress import ip_address 4 | 5 | import pytest 6 | 7 | from pyatv.const import Protocol 8 | 9 | from tests import fake_udns 10 | from tests.conftest import Scanner 11 | from tests.utils import assert_device 12 | 13 | IP_1 = "10.0.0.1" 14 | 15 | RAOP_ID = "AABBCCDDEEFF" 16 | RAOP_NAME = "RAOP ATV" 17 | 18 | RAOP_PORT = 4567 19 | 20 | pytestmark = pytest.mark.asyncio 21 | 22 | 23 | async def test_multicast_scan_raop_device(udns_server, multicast_scan): 24 | udns_server.add_service( 25 | fake_udns.raop_service(RAOP_NAME, RAOP_ID, addresses=[IP_1], port=RAOP_PORT) 26 | ) 27 | 28 | atvs = await multicast_scan() 29 | assert len(atvs) == 1 30 | 31 | assert_device( 32 | atvs[0], RAOP_NAME, ip_address(IP_1), RAOP_ID, Protocol.RAOP, RAOP_PORT 33 | ) 34 | 35 | 36 | async def test_unicast_scan_raop(udns_server, unicast_scan): 37 | udns_server.add_service( 38 | fake_udns.raop_service(RAOP_NAME, RAOP_ID, addresses=[IP_1], port=RAOP_PORT) 39 | ) 40 | 41 | atvs = await unicast_scan() 42 | assert len(atvs) == 1 43 | 44 | assert_device( 45 | atvs[0], RAOP_NAME, ip_address(IP_1), RAOP_ID, Protocol.RAOP, RAOP_PORT 46 | ) 47 | -------------------------------------------------------------------------------- /tests/shared_helpers.py: -------------------------------------------------------------------------------- 1 | """Shared test helper code.""" 2 | 3 | from pyatv.const import PowerState 4 | from pyatv.interface import PowerListener 5 | 6 | 7 | class SavingPowerListener(PowerListener): 8 | def __init__(self): 9 | self.last_update = None 10 | self.all_updates = [] 11 | 12 | def powerstate_update(self, old_state: PowerState, new_state: PowerState): 13 | """Device power state was updated.""" 14 | self.last_update = new_state 15 | self.all_updates.append(new_state) 16 | -------------------------------------------------------------------------------- /tests/support/pyatv.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "../.." 5 | }, 6 | { 7 | "path": "../../../airplay2-receiver" 8 | }, 9 | { 10 | "path": "../../../pyminiaudio" 11 | } 12 | ], 13 | "settings": {} 14 | } -------------------------------------------------------------------------------- /tests/support/test_chacha20.py: -------------------------------------------------------------------------------- 1 | """Unit tests for pyatv.support.chacha20.""" 2 | 3 | import logging 4 | 5 | from pyatv.support import chacha20 6 | 7 | fake_key = b"k" * 32 8 | 9 | 10 | def test_12_bytes_nonce(): 11 | cipher = chacha20.Chacha20Cipher(fake_key, fake_key, 12) 12 | assert len(cipher.out_nonce) == chacha20.NONCE_LENGTH 13 | assert len(cipher.in_nonce) == chacha20.NONCE_LENGTH 14 | result = cipher.encrypt(b"test") 15 | assert cipher.decrypt(result) == b"test" 16 | 17 | 18 | def test_8_bytes_nonce(): 19 | cipher = chacha20.Chacha20Cipher8byteNonce(fake_key, fake_key) 20 | assert len(cipher.out_nonce) == chacha20.NONCE_LENGTH 21 | assert len(cipher.in_nonce) == chacha20.NONCE_LENGTH 22 | result = cipher.encrypt(b"test") 23 | assert cipher.decrypt(result) == b"test" 24 | -------------------------------------------------------------------------------- /tests/support/test_metadata.py: -------------------------------------------------------------------------------- 1 | """Unit tests for pyatv.support.metadata.""" 2 | 3 | import math 4 | from pathlib import Path 5 | 6 | import pytest 7 | 8 | from pyatv.interface import MediaMetadata 9 | from pyatv.support.metadata import get_metadata, merge_into 10 | 11 | from tests.utils import data_path 12 | 13 | 14 | def assert_metadata(metadata: MediaMetadata) -> None: 15 | assert metadata.artist == "postlund" 16 | assert metadata.album == "raop" 17 | assert metadata.title == "pyatv" 18 | assert metadata.artwork is None 19 | assert math.isclose(metadata.duration, 0.0) 20 | 21 | 22 | @pytest.mark.asyncio 23 | async def test_get_metadata_from_file(): 24 | with open(data_path("only_metadata.wav"), "rb") as fh: 25 | metadata = await get_metadata(fh) 26 | assert_metadata(metadata) 27 | 28 | 29 | @pytest.mark.asyncio 30 | async def test_get_metadata_from_buffer(): 31 | metadata = await get_metadata(data_path("only_metadata.wav")) 32 | assert_metadata(metadata) 33 | 34 | 35 | METADATA_FIELDS = list(MediaMetadata.__dataclass_fields__.keys()) 36 | 37 | 38 | def test_returns_base_instance(): 39 | base = MediaMetadata() 40 | new_metadata = MediaMetadata() 41 | merged = merge_into(base, new_metadata) 42 | assert merged is base 43 | 44 | 45 | def test_to_empty_metadata(): 46 | # This basically sets the title field to "title", artist to "artist" and so on 47 | # for all fields and assert that it is correct 48 | metadata = merge_into(MediaMetadata(), MediaMetadata(*METADATA_FIELDS)) 49 | for field in METADATA_FIELDS: 50 | assert getattr(metadata, field) == field 51 | 52 | 53 | def test_no_override_set_value(): 54 | metadata = merge_into( 55 | MediaMetadata(title="title"), MediaMetadata(title="new title") 56 | ) 57 | assert metadata.title == "title" 58 | -------------------------------------------------------------------------------- /tests/support/test_packet.py: -------------------------------------------------------------------------------- 1 | """Unit tests for pyatv.support.packet.""" 2 | 3 | import pytest 4 | 5 | from pyatv.support.packet import defpacket 6 | 7 | Foo = defpacket("Foo", a="c", b="h") 8 | Bar = Foo.extend("Bar", c="I") 9 | 10 | 11 | def test_encode_messages(): 12 | assert Foo.encode(b"\x16", 0x123) == b"\x16\x01\x23" 13 | 14 | 15 | def test_decode_message(): 16 | decoded = Foo.decode(b"\x16\x01\x23") 17 | assert decoded.a == b"\x16" 18 | assert decoded.b == 0x123 19 | 20 | 21 | def test_decode_with_excessive_data(): 22 | decoded = Foo.decode(b"\x17\x02\x34\x11\x22\x33", allow_excessive=True) 23 | assert decoded.a == b"\x17" 24 | assert decoded.b == 0x234 25 | 26 | 27 | def test_extend_encode(): 28 | assert Bar.encode(b"\x77", 0x67, 0xAABBCCDD) == b"\x77\x00\x67\xaa\xbb\xcc\xdd" 29 | 30 | 31 | def test_extend_decode(): 32 | decoded = Bar.decode(b"\x77\x00\x67\xaa\xbb\xcc\xdd") 33 | assert decoded.a == b"\x77" 34 | assert decoded.b == 0x0067 35 | assert decoded.c == 0xAABBCCDD 36 | 37 | 38 | def test_message_length(): 39 | assert Foo.length == 3 40 | assert Bar.length == 3 + 4 41 | -------------------------------------------------------------------------------- /tests/support/test_shield.py: -------------------------------------------------------------------------------- 1 | """Unit tests for pyatv.support.shield.""" 2 | 3 | import pytest 4 | 5 | from pyatv.exceptions import BlockedStateError, InvalidStateError 6 | from pyatv.support import shield 7 | 8 | 9 | class Dummy: 10 | pass 11 | 12 | 13 | def test_shield_object(): 14 | obj = Dummy() 15 | 16 | assert not shield.is_shielded(obj) 17 | obj2 = shield.shield(obj) 18 | assert obj == obj2 19 | assert shield.is_shielded(obj) 20 | 21 | 22 | def test_cannot_block_unshielded_object(): 23 | obj = Dummy() 24 | with pytest.raises(InvalidStateError): 25 | shield.block(obj) 26 | 27 | 28 | def test_is_blocking_does_not_raise_on_unshielded_object(): 29 | obj = Dummy() 30 | assert not shield.is_blocking(obj) 31 | 32 | 33 | def test_block_shielded_object(): 34 | obj = Dummy() 35 | shield.shield(obj) 36 | assert not shield.is_blocking(obj) 37 | shield.block(obj) 38 | assert shield.is_blocking(obj) 39 | 40 | 41 | class GuardedClass: 42 | @shield.guard 43 | def guarded_method(self, a): 44 | return a * a 45 | 46 | def unguarded_method(self, b): 47 | return b + b 48 | 49 | 50 | def test_guarded_methods(): 51 | obj = GuardedClass() 52 | 53 | shield.shield(obj) 54 | 55 | # Should work fine since object is not blocked 56 | assert obj.guarded_method(2) == 4 57 | assert obj.unguarded_method(4) == 8 58 | 59 | shield.block(obj) 60 | 61 | with pytest.raises(BlockedStateError): 62 | obj.guarded_method() 63 | 64 | obj.unguarded_method(5) == 10 65 | -------------------------------------------------------------------------------- /tests/support/test_url.py: -------------------------------------------------------------------------------- 1 | from pyatv.support.url import is_url, is_url_or_scheme 2 | 3 | 4 | def test_is_url_accepts_http_url(): 5 | assert is_url("http://example.com") is True 6 | 7 | 8 | def test_is_url_accepts_app_url(): 9 | assert ( 10 | is_url( 11 | "com.apple.tv://tv.apple.com/show/marvels-spidey-and-his-amazing-friends/umc.cmc.3ambs8tqwzphbn0u8e9g76x7m?profile=kids&action=play" 12 | ) 13 | is True 14 | ) 15 | 16 | 17 | def test_is_url_rejects_bundle_id(): 18 | assert is_url("com.apple.tv") is False 19 | 20 | 21 | def test_is_url_rejects_scheme(): 22 | assert is_url("com.apple.tv://") is False 23 | 24 | 25 | def test_is_url_or_scheme_accepts_http_url(): 26 | assert is_url_or_scheme("http://example.com") is True 27 | 28 | 29 | def test_is_url_or_scheme_accepts_app_url(): 30 | assert ( 31 | is_url_or_scheme( 32 | "com.apple.tv://tv.apple.com/show/marvels-spidey-and-his-amazing-friends/umc.cmc.3ambs8tqwzphbn0u8e9g76x7m?profile=kids&action=play" 33 | ) 34 | is True 35 | ) 36 | 37 | 38 | def test_is_url_or_scheme_rejects_bundle_id(): 39 | assert is_url_or_scheme("com.apple.tv") is False 40 | 41 | 42 | def test_is_url_or_scheme_rejects_scheme(): 43 | assert is_url_or_scheme("com.apple.tv://") is True 44 | -------------------------------------------------------------------------------- /tests/support/test_variant.py: -------------------------------------------------------------------------------- 1 | """Unit tests for pyatv.protocols.mrp.variant.""" 2 | 3 | import pytest 4 | 5 | from pyatv.support.variant import read_variant, write_variant 6 | 7 | 8 | def test_read_single_byte(): 9 | assert read_variant(b"\x00")[0] == 0x00 10 | assert read_variant(b"\x35")[0] == 0x35 11 | 12 | 13 | def test_read_multiple_bytes(): 14 | assert read_variant(b"\xb5\x44")[0] == 8757 15 | assert read_variant(b"\xc5\x92\x01")[0] == 18757 16 | 17 | 18 | def test_read_and_return_remaining_data(): 19 | value, remaining = read_variant(b"\xb5\x44\xca\xfe") 20 | assert value == 8757 21 | assert remaining == b"\xca\xfe" 22 | 23 | 24 | def test_read_invalid_variant(): 25 | with pytest.raises(Exception): 26 | read_variant(b"\x80") 27 | 28 | 29 | def test_write_single_byte(): 30 | assert write_variant(0x00) == b"\x00" 31 | assert write_variant(0x35) == b"\x35" 32 | 33 | 34 | def test_write_multiple_bytes(): 35 | assert write_variant(8757) == b"\xb5\x44" 36 | assert write_variant(18757) == b"\xc5\x92\x01" 37 | -------------------------------------------------------------------------------- /tests/zeroconf_stub.py: -------------------------------------------------------------------------------- 1 | """Stub for the zeroconf library. 2 | 3 | As zeroconf does not provide a stub or mock, this implementation will serve as 4 | stub here. It can fake immediate answers for any service. 5 | """ 6 | 7 | from zeroconf import ServiceInfo 8 | 9 | 10 | class ServiceBrowserStub: 11 | """Stub for ServiceBrowser.""" 12 | 13 | def __init__(self, zeroconf, service_type, listener): 14 | """Create a new instance of ServiceBrowser.""" 15 | for service in zeroconf.services: 16 | if service.type == service_type: 17 | listener.add_service(zeroconf, service_type, service.name) 18 | 19 | 20 | class ZeroconfStub: 21 | """Stub for Zeroconf.""" 22 | 23 | def __init__(self, services): 24 | """Create a new instance of Zeroconf.""" 25 | self.services = services 26 | self.registered_services = [] 27 | 28 | def get_service_info(self, service_type, service_name): 29 | """Look up service information.""" 30 | for service in self.services: 31 | if service.name == service_name: 32 | return service 33 | 34 | def register_service(self, service): 35 | """Save services registered services.""" 36 | self.registered_services.append(service) 37 | 38 | def unregister_service(self, service): 39 | """Stub for unregistering services (does nothing).""" 40 | pass 41 | 42 | def close(self): 43 | """Stub for closing zeroconf (does nothing).""" 44 | pass 45 | 46 | 47 | def stub(module, *services): 48 | """Stub a module using zeroconf.""" 49 | instance = ZeroconfStub(list(services)) 50 | module.Zeroconf = lambda: instance 51 | module.ServiceBrowser = ServiceBrowserStub 52 | return instance 53 | --------------------------------------------------------------------------------