Start | Getting Started | Tutorial | Supported Features | Concepts | Protocols | atvremote | atvproxy | atvscript | atvlog

Table of Contents

atvscript

This script has been created specifically for scripting purposes or for integration with existing software without having to write any additional code. It supports a subset of what atvremote supports and always produce output in a format that is easy to parse, e.g. JSON. It is simple to add support for additional formats if needed.

Extending and improving this script will mostly be left to the community. If you want a new feature, feel free to send a PR!

General Output Format

Output is always in the form of a dictionary (be it JSON, YAML or something else) with the following keys pre-defined keys:

Key Meaning
result success if command was successful, else failure.
datetime Date and time (ISO8601) when event occurred (was printed), e.g. 2020-04-06T18:51:04.758569+02:00.
error An error occurred and this is a well defined string representing the error. Values: device_not_found, unsupported_command.
exception If an unexpected exception occurred, this key contains the exception message.
stacktrace If a stacktrace is available with the exception, it is included as a string here.

error and exception will only be present if result is set to failure. Any additional keys not mentioned here are part of the command response.

Specifying Device and Credentials

Currently atvscript supports device discovery using regular scanning and unicast scanning, with identifier as filter. The arguments are identical to atvremote:

$ atvscript --id 12345 xyz
$ atvscript -s 10.0.0.2 xyz

Credentials are specified via --dmap-credentials, --mrp-credentials and --airplay-credentials. Protocol can also be specified with --protocol.

When using unicast scanning, i.e. specifying IP address via -s, it is not needed to specify --id as the script will pick the first available device it finds. This can only be the requested device.

Command Reference

This section documents the supported commands. JSON output has been prettified for some commands to make them easier to read. One command response is always printed per line.

Scanning

It is possible to scan for devices with the scan command:

$ atvscript scan
{
  "result": "success",
  "datetime": "2020-04-06T18:51:04.758569+02:00",
  "devices": [
    {
      "name": "Vardagsrum",
      "address": "10.0.10.81",
      "identifier": "xxx",
      "all_identifiers": [
        "xxx",
        "xxx",
        "xxx"
      ],
      "device_info": {
        "mac": "AA:BB:CC:DD:EE:FF",
        "model": "Gen4K",
        "model_str": "Apple TV 4K",
        "operating_system": "TvOS",
        "version": "15.5.1"
      },
      "services": [
        {
          "protocol": "mrp",
          "port": 49152
        },
        {
          "protocol": "airplay",
          "port": 7000
        }
      ]
    },
    {
      "name": "Apple TV",
      "address": "10.0.10.123",
      "identifier": "xxx",
      "all_identifiers": [
        "xxx",
        "xxx",
        "xxx"
      ],
      "device_info": {
        "mac": "AA:BB:CC:DD:EE:FF",
        "model": "Gen3",
        "model_str": "Apple TV 3",
        "operating_system": "Legacy",
        "version": "8.6.1"
      }
      "services": [
        {
          "protocol": "airplay",
          "port": 7000
        },
        {
          "protocol": "dmap",
          "port": 3689
        }
      ]
    },
    {
      "name": "Proxy",
      "address": "10.0.10.254",
      "identifier": "xxx",
      "all_identifiers": [
        "xxx",
        "xxx",
        "xxx"
      ],
      "device_info": {
        "mac": null,
        "model": "Unknown",
        "model_str": "Unknown",
        "operating_system": "Unknown",
        "version": null
      },
      "services": [
        {
          "protocol": "mrp",
          "port": 47531
        }
      ]
    }
  ]
}

Scanning also respects the --scan-hosts (-s) flag, which is useful sometimes if scanning is flaky:

$ atvscript -s 10.0.10.81 scan
{
  "result": "success",
  "datetime": "2020-04-06T18:51:04.758569+02:00",
  "devices": [
    {
      "name": "Vardagsrum",
      "address": "10.0.10.81",
      "identifier": "xxx",
      "all_identifiers": [
        "xxx",
        "xxx",
        "xxx"
      ],
      "device_info": {
        "mac": "AA:BB:CC:DD:EE:FF",
        "model": "Gen4K",
        "model_str": "Apple TV 4K",
        "operating_system": "TvOS",
        "version": "15.5.1"
      },
      "services": [
        {
          "protocol": "mrp",
          "port": 49152
        },
        {
          "protocol": "airplay",
          "port": 7000
        }
      ]
    }
  ]
}

What is Playing

To get what is playing:

$ atvscript -s 10.0.10.81 playing
{
  "result": "success",
  "datetime": "2020-04-06T18:51:04.758569+02:00",
  "hash": "azyFEzFpSNOSGq9ZvcaX4A∆DcpumkUoRty+R098MQeIKA",
  "media_type": "music",
  "device_state": "paused",
  "title": "Ordinary World (Live)",
  "artist": "Duran Duran",
  "album": "From Mediterranea With Love - EP",
  "genre": "Rock",
  "total_time": 395,
  "position": 1,
  "shuffle": "off",
  "repeat": "off",
  "app": "Musik",
  "app_id": "com.apple.TVMusic"
}

Some of the fields, like media_type and device_state uses the names from their corresponding enum, but in lower case. Check out const.DeviceState for instance to find valid values for device_state.

Remote Control

All buttons in interface.RemoteControl are supported by atvscript except for the ones beginning with set_:

$ atvscript -s 10.0.10.81 menu
{"result": "success", "datetime": "2020-04-06T18:51:04.758569+02:00", "command": "menu"}

Push, Power, Audio and Keyboard Updates

Push, power, audio and keyboard updates are printed to the terminal as they happen:

$ atvscript -s 10.0.10.81 push_updates
{"result": "success", "datetime": "2020-04-06T18:51:04.758569+02:00", "power_state": "off"}
{"result": "success", "datetime": "2020-04-06T18:51:04.758569+02:00", "output_devices": [{"name": "Living room", "identifier": "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE"}]}
{"result": "success", "datetime": "2020-04-06T18:51:04.758569+02:00", "focus_state": "unfocused"}
{"result": "success", "datetime": "2020-04-06T18:51:04.758569+02:00", "hash": "azyFEzFpSNOSGq9ZvcaX4A\u2206DcpumkUoRty+R098MQeIKA", "media_type": "music", "device_state": "paused", "title": "Ordinary World (Live)", "artist": "Duran Duran", "album": "From Mediterranea With Love - EP", "genre": "Rock", "total_time": 395, "position": 1, "shuffle": "off", "repeat": "off", "app": "Musik", "app_id": "com.apple.TVMusic"}
{"result": "success", "datetime": "2020-04-06T18:51:04.758569+02:00", "power_state": "on"}
{"result": "success", "datetime": "2020-04-06T18:51:04.758569+02:00", "volume": 20.0}
{"result": "success", "datetime": "2020-04-06T18:51:04.758569+02:00", "volume": 15.0}
{"result": "success", "datetime": "2020-04-06T18:51:04.758569+02:00", "focus_state": "focused"}
{"result": "success", "datetime": "2020-04-06T18:51:04.758569+02:00", "focus_state": "unfocused"}
{"result": "success", "datetime": "2020-04-06T18:51:04.758569+02:00", "power_state": "off"}

{"result": "success", "datetime": "2020-04-06T18:51:04.758569+02:00", "push_updates": "finished"}

Current power state is always printed as the first update.

When pressing ENTER, the script will exit (as seen on the last line).

Connection Status

Listening to push updates is a long-lived process and the connection might be closed at some point, e.g. due to device reboot or network issues. If this happens, connection will be set to closed:

$ atvscript --id 6D797FD3-3538-427E-A47B-A32FC6CF3A69 push_updates
{"result": "success", "datetime": "2020-04-06T18:51:04.758569+02:00", "power_state": "on"}
...
{"result": "failure", "datetime": "2020-04-06T18:51:04.758569+02:00", "connection": "closed"}

In case of abnormal disconnection, connection will be set to lost and an exception will be included:

{"result": "success", "datetime": "2020-04-06T18:51:04.758569+02:00", "power_state": "on"}
...
{"result": "failure", "datetime": "2020-04-06T18:51:04.758569+02:00", "exception": "something bad happened", "connection": "closed"}

The script will also exit (without requiring user interaction) with a non-zero exit code.

← atvproxy | atvlog →