diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..c2295e0 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4709d26 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 stickpin + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e50b1a8 --- /dev/null +++ b/README.md @@ -0,0 +1,146 @@ +# node-appletv-x + +> A node module for interacting with an Apple TV (4th-generation or later) over the Media Remote Protocol. + +[![License][license-image]][license-url] + +![](images/pairing.gif) + +## Disclaimer + +Original project by [@edc1591](https://twitter.com/edc1591). +Code includes some fixes made by: [GioCirque](https://github.com/GioCirque/node-appletv), [casey-chow](https://github.com/casey-chow/node-appletv) and [SeppSTA](https://github.com/SeppSTA/node-appletv) +The code delivered AS-IS, due to lack of time I am not planning to provide any support, feel free to do with it whatever you want. :) + +## Overview + +`node-appletv-x` is a `node.js` implementation of the Media Remote Protocol which shipped with the 4th-generation Apple TV. This is the protocol that the Apple TV remote app uses, so this should enable the creation of an Apple TV remote app for various platforms. It can also be used in a `homebridge` plugin to connect Apple TV events to HomeKit and vice versa. `node-appletv-x` can be used as a standalone command line application, or as a module in your own node app. Keep reading for installation and usage instructions. + +This version adding following funtionality: +* Works with latest NodeJS (v12.x and newer) & Modules. +* Fix duplicate AppleTV entries +* Additinal buttons are added: select, tv, longtv +* Support for tvOS 13.3 and newer +* Various fixes + +## Documentation + +Developer documentation for `node-appletv-x` can be found /docs/ folder. + +## Usage + +### As a standalone cli + +```bash +# Install +$ sudo apt-get install libtool autoconf automake libavahi-compat-libdnssd-dev +$ sudo npm install -g node-appletv-x --unsafe-perm + +# Display built-in help +$ appletv --help +``` + +The `appletv` cli supports several commands, such as: + +`pair`: Scans for Apple TVs on the local network and initiates the pairing process + +`command `: Execute a command on an Apple TV (play, pause, menu, etc.) +Example: `appletv --credentials '' command tv` + +`state`: Logs state changes from an Apple TV (now playing info) + +`queue`: Requests the current playback queue from an Apple TV + +`messages`: Logs all raw messages from an Apple TV + +`help `: Get help for a specific command + + +### As a node module + +```bash +$ npm install --save node-appletv-x +``` + +`node-appletv-x` makes heavy use of Promises. All functions, except for the observe functions, return Promises. + +### Examples + +#### Scan for Apple TVs and pair + +```typescript +import { scan } from 'node-appletv'; + +return scan() + .then(devices => { + // devices is an array of AppleTV objects + let device = devices[0]; + return device.openConnection() + .then(device => { + return device.pair(); + }) + .then(callback => { + // the pin is provided onscreen from the Apple TV + return callback(pin); + }); + }) + .then(device => { + // you're paired! + let credentials = device.credentials.toString(); + console.log(credentials); + }) + .catch(error => { + console.log(error); + }); +``` + +#### Connect to a paired Apple TV + +```typescript +import { scan, parseCredentials, NowPlayingInfo } from 'node-appletv'; + +// see example above for how to get the credentials string +let credentials = parseCredentials(credentialsString); + +return scan(uniqueIdentifier) + .then(devices => { + let device = devices[0]; + return device.openConnection(credentials); + }) + .then(device => { + // you're connected! + // press menu + return device.sendKeyCommand(AppleTV.Key.Menu); + }) + .then(device => { + console.log("Sent a menu command!"); + + // monitor now playing info + device.on('nowPlaying', (info: NowPlayingInfo) => { + console.log(info.toString()); + }); + }) + .catch(error => { + console.log(error); + }); +``` + +The `uniqueIdentifier` is advertised by each Apple TV via Bonjour. Use an app like [Bonjour Browser](http://www.tildesoft.com) to find it. The identifier is also the first value in the string value of the `Credentials` object. + +See [homebridge-theater-mode](https://github.com/edc1591/homebridge-theater-mode) for a more practical use of this module. + +## Acknowledgments + +`node-appletv-x` would not have been possible without the work of these people: + +* [Jean Regisser](https://github.com/jeanregisser) who reversed the protobuf [spec of the MediaRemoteTV protocol](https://github.com/jeanregisser/mediaremotetv-protocol) +* [Pierre Ståhl](https://github.com/postlund) who [implemented the protocol in Python](https://github.com/postlund/pyatv) +* [Khaos Tian](https://github.com/KhaosT) for [reversing the HomeKit protocol](https://github.com/KhaosT/HAP-NodeJS) which also uses SRP encryption +* [Zach Bean](https://github.com/forty2) for [implementing the HAP client spec](https://github.com/forty2/hap-client) + +## Meta + +Distributed under the MIT license. See ``LICENSE`` for more information. + +[license-image]: https://img.shields.io/badge/License-MIT-blue.svg +[license-url]: LICENSE \ No newline at end of file diff --git a/bin/.DS_Store b/bin/.DS_Store new file mode 100644 index 0000000..f577102 Binary files /dev/null and b/bin/.DS_Store differ diff --git a/bin/appletv b/bin/appletv new file mode 100755 index 0000000..24fba31 --- /dev/null +++ b/bin/appletv @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +require('../dist/bin/index.js'); \ No newline at end of file diff --git a/dist/.DS_Store b/dist/.DS_Store new file mode 100644 index 0000000..c95cbb1 Binary files /dev/null and b/dist/.DS_Store differ diff --git a/dist/bin/index.d.ts b/dist/bin/index.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/bin/index.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/bin/index.js b/dist/bin/index.js new file mode 100644 index 0000000..f0771ec --- /dev/null +++ b/dist/bin/index.js @@ -0,0 +1,180 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const caporal = require("caporal"); +let cli = caporal; +const appletv_1 = require("../lib/appletv"); +const credentials_1 = require("../lib/credentials"); +const scan_1 = require("./scan"); +const pair_1 = require("./pair"); +cli + .version('1.0.11') + .command('pair', 'Pair with an Apple TV') + .option('--timeout ', 'The amount of time (in seconds) to scan for Apple TVs', cli.INTEGER) + .action((args, options, logger) => { + scan_1.scan(logger, options.timeout) + .then(device => { + device.on('debug', (message) => { + logger.debug(message); + }); + device.on('error', (error) => { + logger.error(error.message); + logger.debug(error.stack); + }); + return pair_1.pair(device, logger) + .then(keys => { + logger.info("Credentials: " + device.credentials.toString()); + process.exit(); + }); + }) + .catch(error => { + logger.error(error.message); + logger.debug(error.stack); + process.exit(); + }); +}); +cli + .command('command', 'Send a command to an Apple TV') + .argument('', 'The command to send', /^up|down|left|right|menu|play|pause|next|previous|suspend|select|tv|longtv$/) + .option('--credentials ', 'The device credentials from pairing', cli.STRING) + .action((args, options, logger) => { + if (!options.credentials) { + logger.error("Credentials are required. Pair first."); + process.exit(); + } + let credentials = credentials_1.Credentials.parse(options.credentials); + scan_1.scan(logger, null, credentials.uniqueIdentifier) + .then(device => { + device.on('debug', (message) => { + logger.debug(message); + }); + device.on('error', (error) => { + logger.error(error.message); + logger.debug(error.stack); + }); + return device + .openConnection(credentials) + .then(() => { + return device + .sendKeyCommand(appletv_1.AppleTV.key(args["command"])) + .then(result => { + logger.info("Success!"); + process.exit(); + }); + }); + }) + .catch(error => { + logger.error(error.message); + logger.debug(error.stack); + process.exit(); + }); +}); +cli + .command('state', 'Logs the playback state from the Apple TV') + .option('--credentials ', 'The device credentials from pairing', cli.STRING) + .action((args, options, logger) => { + if (!options.credentials) { + logger.error("Credentials are required. Pair first."); + process.exit(); + } + let credentials = credentials_1.Credentials.parse(options.credentials); + scan_1.scan(logger, null, credentials.uniqueIdentifier) + .then(device => { + device.on('debug', (message) => { + logger.debug(message); + }); + device.on('error', (error) => { + logger.error(error.message); + logger.debug(error.stack); + }); + return device + .openConnection(credentials); + }) + .then(device => { + device.on('nowPlaying', (info) => { + logger.info(info.toString()); + }); + }) + .catch(error => { + logger.error(error.message); + logger.debug(error.stack); + process.exit(); + }); +}); +cli + .command('queue', 'Request the playback state from the Apple TV') + .option('--credentials ', 'The device credentials from pairing', cli.STRING) + .option('--location ', 'The location in the queue', cli.INTEGER) + .option('--length ', 'The length of the queue', cli.INTEGER) + .option('--metadata', 'Include metadata', cli.BOOLEAN) + .option('--lyrics', 'Include lyrics', cli.BOOLEAN) + .option('--languages', 'Include language options', cli.BOOLEAN) + .action((args, options, logger) => { + if (!options.credentials) { + logger.error("Credentials are required. Pair first."); + process.exit(); + } + let credentials = credentials_1.Credentials.parse(options.credentials); + scan_1.scan(logger, null, credentials.uniqueIdentifier) + .then(device => { + device.on('debug', (message) => { + logger.debug(message); + }); + device.on('error', (error) => { + logger.error(error.message); + logger.debug(error.stack); + }); + return device + .openConnection(credentials); + }) + .then(device => { + return device + .requestPlaybackQueue({ + location: options.location || 0, + length: options.length || 1, + includeMetadata: options.metadata, + includeLyrics: options.lyrics, + includeLanguageOptions: options.languages + }); + }) + .then(message => { + logger.info(message); + }) + .catch(error => { + logger.error(error.message); + logger.debug(error.stack); + process.exit(); + }); +}); +cli + .command('messages', 'Log all messages sent from the Apple TV') + .option('--credentials ', 'The device credentials from pairing', cli.STRING) + .action((args, options, logger) => { + if (!options.credentials) { + logger.error("Credentials are required. Pair first."); + process.exit(); + } + let credentials = credentials_1.Credentials.parse(options.credentials); + scan_1.scan(logger, null, credentials.uniqueIdentifier) + .then(device => { + device.on('debug', (message) => { + logger.debug(message); + }); + device.on('error', (error) => { + logger.error(error.message); + logger.debug(error.stack); + }); + return device + .openConnection(credentials); + }) + .then(device => { + device.on('message', (message) => { + logger.info(JSON.stringify(message.toObject(), null, 2)); + }); + }) + .catch(error => { + logger.error(error.message); + logger.debug(error.stack); + process.exit(); + }); +}); +cli.parse(process.argv); diff --git a/dist/bin/pair.d.ts b/dist/bin/pair.d.ts new file mode 100644 index 0000000..23ecea3 --- /dev/null +++ b/dist/bin/pair.d.ts @@ -0,0 +1,2 @@ +import { AppleTV } from '../lib/appletv'; +export declare function pair(device: AppleTV, logger: Logger): Promise; diff --git a/dist/bin/pair.js b/dist/bin/pair.js new file mode 100644 index 0000000..23ef879 --- /dev/null +++ b/dist/bin/pair.js @@ -0,0 +1,40 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const inquirer_1 = require("inquirer"); +const ora = require("ora"); +const pairing_1 = require("../lib/pairing"); +function pair(device, logger) { + let spinner = ora("Connecting to " + device.name).start(); + return device + .openConnection() + .then(() => { + spinner.succeed().start('Initiating Pairing'); + let pairing = new pairing_1.Pairing(device); + return pairing.initiatePair() + .then(callback => { + spinner.succeed(); + return inquirer_1.prompt([{ + type: 'input', + name: 'pin', + message: "Enter the 4-digit pin that's currently being displayed on " + device.name, + validate: (input) => { + let isValid = /^\d+$/.test(input); + return isValid ? true : 'Pin must be 4-digits and all numbers.'; + } + }]) + .then(answers => { + spinner.start('Completing Pairing'); + return callback(answers['pin']); + }); + }) + .then(device => { + spinner.succeed(); + return device; + }) + .catch(error => { + spinner.fail(); + throw error; + }); + }); +} +exports.pair = pair; diff --git a/dist/bin/scan.d.ts b/dist/bin/scan.d.ts new file mode 100644 index 0000000..39388aa --- /dev/null +++ b/dist/bin/scan.d.ts @@ -0,0 +1,2 @@ +import { AppleTV } from '../lib/appletv'; +export declare function scan(logger: Logger, timeout?: number, uniqueIdentifier?: string): Promise; diff --git a/dist/bin/scan.js b/dist/bin/scan.js new file mode 100644 index 0000000..ead3878 --- /dev/null +++ b/dist/bin/scan.js @@ -0,0 +1,38 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const inquirer_1 = require("inquirer"); +const ora = require("ora"); +const browser_1 = require("../lib/browser"); +function scan(logger, timeout, uniqueIdentifier) { + let browser = new browser_1.Browser(); + let spinner = ora('Scanning for Apple TVs...').start(); + return browser + .scan(uniqueIdentifier, timeout) + .then(devices => { + spinner.stop(); + if (devices.length == 1) { + return devices[0]; + } + if (devices.length == 0) { + throw new Error("No Apple TVs found on the network. Try again."); + } + else { + return inquirer_1.prompt([{ + type: 'list', + name: 'device', + message: 'Which Apple TV would you like to pair with?', + choices: devices.map(device => { + return { + name: device.name + " (" + device.address + ":" + device.port + ")", + value: device.uid + }; + }) + }]) + .then(answers => { + let uid = answers['device']; + return devices.filter(device => { return device.uid == uid; })[0]; + }); + } + }); +} +exports.scan = scan; diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..34cfbc2 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,20 @@ +import { Credentials } from './lib/credentials'; +import { AppleTV } from './lib/appletv'; +import { Browser } from './lib/browser'; +import { NowPlayingInfo } from './lib/now-playing-info'; +import { Message } from './lib/message'; +import { SupportedCommand } from './lib/supported-command'; +/** +* A convenience function to scan for AppleTVs on the local network. +* @param uniqueIdentifier An optional identifier for the AppleTV to scan for. The AppleTV advertises this via Bonjour. +* @param timeout An optional timeout value (in seconds) to give up the search after. +* @returns A promise that resolves to an array of AppleTV objects. If you provide a `uniqueIdentifier` the array is guaranteed to only contain one object. +*/ +export declare function scan(uniqueIdentifier?: string, timeout?: number): Promise; +/** +* A convenience function to parse a credentials string into a Credentials object. +* @param text The credentials string. +* @returns A credentials object. +*/ +export declare function parseCredentials(text: string): Credentials; +export { AppleTV, Browser, NowPlayingInfo, Credentials, Message, SupportedCommand }; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..75d6447 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const credentials_1 = require("./lib/credentials"); +exports.Credentials = credentials_1.Credentials; +const appletv_1 = require("./lib/appletv"); +exports.AppleTV = appletv_1.AppleTV; +const browser_1 = require("./lib/browser"); +exports.Browser = browser_1.Browser; +const now_playing_info_1 = require("./lib/now-playing-info"); +exports.NowPlayingInfo = now_playing_info_1.NowPlayingInfo; +const message_1 = require("./lib/message"); +exports.Message = message_1.Message; +const supported_command_1 = require("./lib/supported-command"); +exports.SupportedCommand = supported_command_1.SupportedCommand; +/** +* A convenience function to scan for AppleTVs on the local network. +* @param uniqueIdentifier An optional identifier for the AppleTV to scan for. The AppleTV advertises this via Bonjour. +* @param timeout An optional timeout value (in seconds) to give up the search after. +* @returns A promise that resolves to an array of AppleTV objects. If you provide a `uniqueIdentifier` the array is guaranteed to only contain one object. +*/ +function scan(uniqueIdentifier, timeout) { + let browser = new browser_1.Browser(); + return browser.scan(uniqueIdentifier, timeout); +} +exports.scan = scan; +/** +* A convenience function to parse a credentials string into a Credentials object. +* @param text The credentials string. +* @returns A credentials object. +*/ +function parseCredentials(text) { + return credentials_1.Credentials.parse(text); +} +exports.parseCredentials = parseCredentials; diff --git a/dist/lib/.DS_Store b/dist/lib/.DS_Store new file mode 100644 index 0000000..021aba1 Binary files /dev/null and b/dist/lib/.DS_Store differ diff --git a/dist/lib/appletv.d.ts b/dist/lib/appletv.d.ts new file mode 100644 index 0000000..23a423d --- /dev/null +++ b/dist/lib/appletv.d.ts @@ -0,0 +1,123 @@ +import { Service } from 'mdns'; +import { Credentials } from './credentials'; +import { NowPlayingInfo } from './now-playing-info'; +import { SupportedCommand } from './supported-command'; +import TypedEventEmitter from './typed-events'; +import { Message } from './message'; +export interface Size { + width: number; + height: number; +} +export interface PlaybackQueueRequestOptions { + location: number; + length: number; + includeMetadata?: boolean; + includeLanguageOptions?: boolean; + includeLyrics?: boolean; + artworkSize?: Size; +} +export interface ClientUpdatesConfig { + artworkUpdates: boolean; + nowPlayingUpdates: boolean; + volumeUpdates: boolean; + keyboardUpdates: boolean; +} +export declare class AppleTV extends TypedEventEmitter { + private service; + name: string; + address: string; + port: number; + uid: string; + pairingId: string; + credentials: Credentials; + private connection; + constructor(service: Service); + /** + * Pair with an already discovered AppleTV. + * @returns A promise that resolves to the AppleTV object. + */ + pair(): Promise<(pin: string) => Promise>; + /** + * Opens a connection to the AppleTV over the MRP protocol. + * @param credentials The credentials object for this AppleTV + * @returns A promise that resolves to the AppleTV object. + */ + openConnection(credentials?: Credentials): Promise; + /** + * Closes the connection to the Apple TV. + */ + closeConnection(): void; + /** + * Send a Protobuf message to the AppleTV. This is for advanced usage only. + * @param definitionFilename The Protobuf filename of the message type. + * @param messageType The name of the message. + * @param body The message body + * @param waitForResponse Whether or not to wait for a response before resolving the Promise. + * @returns A promise that resolves to the response from the AppleTV. + */ + sendMessage(definitionFilename: string, messageType: string, body: {}, waitForResponse: boolean, priority?: number): Promise; + /** + * Wait for a single message of a specified type. + * @param type The type of the message to wait for. + * @param timeout The timeout (in seconds). + * @returns A promise that resolves to the Message. + */ + messageOfType(type: Message.Type, timeout?: number): Promise; + /** + * Requests the current playback queue from the Apple TV. + * @param options Options to send + * @returns A Promise that resolves to a NewPlayingInfo object. + */ + requestPlaybackQueue(options: PlaybackQueueRequestOptions): Promise; + /** + * Send a key command to the AppleTV. + * @param key The key to press. + * @returns A promise that resolves to the AppleTV object after the message has been sent. + */ + sendKeyCommand(key: AppleTV.Key): Promise; + private promiseTimeout; + private sendKeyPressAndRelease; + private sendKeyHoldAndRelease; + private sendKeyPress; + private requestPlaybackQueueWithWait; + private sendIntroduction; + private sendConnectionState; + private sendClientUpdatesConfig; + private sendWakeDevice; +} +export declare module AppleTV { + interface Events { + connect: void; + nowPlaying: NowPlayingInfo; + supportedCommands: SupportedCommand[]; + playbackQueue: any; + message: Message; + close: void; + error: Error; + debug: string; + } +} +export declare module AppleTV { + /** An enumeration of key presses available. + */ + enum Key { + Up = 0, + Down = 1, + Left = 2, + Right = 3, + Menu = 4, + Play = 5, + Pause = 6, + Next = 7, + Previous = 8, + Suspend = 9, + Select = 10, + LongTv = 11, + Tv = 12 + } + /** Convert a string representation of a key to the correct enum type. + * @param string The string. + * @returns The key enum value. + */ + function key(string: string): AppleTV.Key; +} diff --git a/dist/lib/appletv.js b/dist/lib/appletv.js new file mode 100644 index 0000000..b28a551 --- /dev/null +++ b/dist/lib/appletv.js @@ -0,0 +1,395 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const path = require("path"); +const protobufjs_1 = require("protobufjs"); +const uuid_1 = require("uuid"); +const connection_1 = require("./connection"); +const pairing_1 = require("./pairing"); +const verifier_1 = require("./verifier"); +const now_playing_info_1 = require("./now-playing-info"); +const supported_command_1 = require("./supported-command"); +const typed_events_1 = require("./typed-events"); +const message_1 = require("./message"); +const number_1 = require("./util/number"); +class AppleTV extends typed_events_1.default { + constructor(service) { + super(); + this.service = service; + this.pairingId = uuid_1.v4(); + this.service = service; + this.name = service.txtRecord.Name; + if (service.addresses.length > 1) { + this.address = service.addresses[1]; + } + else { + this.address = service.addresses[0]; + } + this.port = service.port; + this.uid = service.txtRecord.UniqueIdentifier; + this.connection = new connection_1.Connection(this); + let that = this; + this.connection.on('message', (message) => { + that.emit('message', message); + if (message.type == message_1.Message.Type.SetStateMessage) { + if (message.payload == null) { + that.emit('nowPlaying', null); + return; + } + if (message.payload.nowPlayingInfo) { + let info = new now_playing_info_1.NowPlayingInfo(message.payload); + that.emit('nowPlaying', info); + } + if (message.payload.supportedCommands) { + let commands = (message.payload.supportedCommands.supportedCommands || []) + .map(sc => { + return new supported_command_1.SupportedCommand(sc.command, sc.enabled || false, sc.canScrub || false); + }); + that.emit('supportedCommands', commands); + } + if (message.payload.playbackQueue) { + that.emit('playbackQueue', message.payload.playbackQueue); + } + } + }) + .on('connect', () => { + that.emit('connect'); + }) + .on('close', () => { + that.emit('close'); + }) + .on('error', (error) => { + that.emit('error', error); + }) + .on('debug', (message) => { + that.emit('debug', message); + }); + var queuePollTimer = null; + this._on('newListener', (event, listener) => { + if (queuePollTimer == null && (event == 'nowPlaying' || event == 'supportedCommands')) { + queuePollTimer = setInterval(() => { + if (that.connection.isOpen) { + that.requestPlaybackQueueWithWait({ + length: 100, + location: 0, + artworkSize: { + width: -1, + height: 368 + } + }, false).then(() => { }).catch(error => { }); + } + }, 5000); + } + }); + this._on('removeListener', (event, listener) => { + if (queuePollTimer != null && (event == 'nowPlaying' || event == 'supportedCommands')) { + let listenerCount = that.listenerCount('nowPlaying') + that.listenerCount('supportedCommands'); + if (listenerCount == 0) { + clearInterval(queuePollTimer); + queuePollTimer = null; + } + } + }); + } + /** + * Pair with an already discovered AppleTV. + * @returns A promise that resolves to the AppleTV object. + */ + pair() { + let pairing = new pairing_1.Pairing(this); + return pairing.initiatePair(); + } + /** + * Opens a connection to the AppleTV over the MRP protocol. + * @param credentials The credentials object for this AppleTV + * @returns A promise that resolves to the AppleTV object. + */ + openConnection(credentials) { + let that = this; + if (credentials) { + this.pairingId = credentials.pairingId; + } + return this.connection + .open() + .then(() => { + return that.sendIntroduction(); + }) + .then(() => { + that.credentials = credentials; + if (credentials) { + let verifier = new verifier_1.Verifier(that); + return verifier.verify() + .then(keys => { + that.credentials.readKey = keys['readKey']; + that.credentials.writeKey = keys['writeKey']; + that.emit('debug', "DEBUG: Keys Read=" + that.credentials.readKey.toString('hex') + ", Write=" + that.credentials.writeKey.toString('hex')); + return that.sendConnectionState(); + }); + } + else { + return null; + } + }) + .then(() => { + if (credentials) { + return that.sendClientUpdatesConfig({ + nowPlayingUpdates: true, + artworkUpdates: true, + keyboardUpdates: false, + volumeUpdates: false + }); + } + else { + return null; + } + }) + .then(() => { + return Promise.resolve(that); + }); + } + /** + * Closes the connection to the Apple TV. + */ + closeConnection() { + this.connection.close(); + } + /** + * Send a Protobuf message to the AppleTV. This is for advanced usage only. + * @param definitionFilename The Protobuf filename of the message type. + * @param messageType The name of the message. + * @param body The message body + * @param waitForResponse Whether or not to wait for a response before resolving the Promise. + * @returns A promise that resolves to the response from the AppleTV. + */ + sendMessage(definitionFilename, messageType, body, waitForResponse, priority = 0) { + return protobufjs_1.load(path.resolve(__dirname + "/protos/" + definitionFilename + ".proto")) + .then(root => { + let type = root.lookupType(messageType); + return type.create(body); + }) + .then(message => { + return this.connection + .send(message, waitForResponse, priority, this.credentials); + }); + } + /** + * Wait for a single message of a specified type. + * @param type The type of the message to wait for. + * @param timeout The timeout (in seconds). + * @returns A promise that resolves to the Message. + */ + messageOfType(type, timeout = 5) { + let that = this; + return new Promise((resolve, reject) => { + let listener; + let timer = setTimeout(() => { + reject(new Error("Timed out waiting for message type " + type)); + that.removeListener('message', listener); + }, timeout * 1000); + listener = (message) => { + if (message.type == type) { + resolve(message); + that.removeListener('message', listener); + } + }; + that.on('message', listener); + }); + } + /** + * Requests the current playback queue from the Apple TV. + * @param options Options to send + * @returns A Promise that resolves to a NewPlayingInfo object. + */ + requestPlaybackQueue(options) { + return this.requestPlaybackQueueWithWait(options, true); + } + /** + * Send a key command to the AppleTV. + * @param key The key to press. + * @returns A promise that resolves to the AppleTV object after the message has been sent. + */ + sendKeyCommand(key) { + switch (key) { + case AppleTV.Key.Up: + return this.sendKeyPressAndRelease(1, 0x8C); + case AppleTV.Key.Down: + return this.sendKeyPressAndRelease(1, 0x8D); + case AppleTV.Key.Left: + return this.sendKeyPressAndRelease(1, 0x8B); + case AppleTV.Key.Right: + return this.sendKeyPressAndRelease(1, 0x8A); + case AppleTV.Key.Menu: + return this.sendKeyPressAndRelease(1, 0x86); + case AppleTV.Key.Play: + return this.sendKeyPressAndRelease(12, 0xB0); + case AppleTV.Key.Pause: + return this.sendKeyPressAndRelease(12, 0xB1); + case AppleTV.Key.Next: + return this.sendKeyPressAndRelease(12, 0xB5); + case AppleTV.Key.Previous: + return this.sendKeyPressAndRelease(12, 0xB6); + case AppleTV.Key.Suspend: + return this.sendKeyPressAndRelease(1, 0x82); + case AppleTV.Key.Select: + return this.sendKeyPressAndRelease(1, 0x89); + case AppleTV.Key.LongTv: + return this.sendKeyHoldAndRelease(12, 0x60); + case AppleTV.Key.Tv: + return this.sendKeyPressAndRelease(12, 0x60); + } + } + promiseTimeout(time) { + return new Promise(function (resolve) { + setTimeout(function () { resolve(time); }, time); + }); + } + sendKeyPressAndRelease(usePage, usage) { + let that = this; + return this.sendKeyPress(usePage, usage, true) + .then(() => { + return that.sendKeyPress(usePage, usage, false); + }); + } + sendKeyHoldAndRelease(usePage, usage) { + let that = this; + return this.sendKeyPress(usePage, usage, true) + .then(() => { + return this.promiseTimeout(2000); + }) + .then(() => { + return that.sendKeyPress(usePage, usage, false); + }); + } + sendKeyPress(usePage, usage, down) { + let time = Buffer.from('438922cf08020000', 'hex'); + let data = Buffer.concat([ + number_1.default.UInt16toBufferBE(usePage), + number_1.default.UInt16toBufferBE(usage), + down ? number_1.default.UInt16toBufferBE(1) : number_1.default.UInt16toBufferBE(0) + ]); + let body = { + hidEventData: Buffer.concat([ + time, + Buffer.from('00000000000000000100000000000000020' + '00000200000000300000001000000000000', 'hex'), + data, + Buffer.from('0000000000000001000000', 'hex') + ]) + }; + let that = this; + return this.sendMessage("SendHIDEventMessage", "SendHIDEventMessage", body, false) + .then(() => { + return that; + }); + } + requestPlaybackQueueWithWait(options, waitForResponse) { + var params = options; + params.requestID = uuid_1.v4(); + if (options.artworkSize) { + params.artworkWidth = options.artworkSize.width; + params.artworkHeight = options.artworkSize.height; + delete params.artworkSize; + } + return this.sendMessage("PlaybackQueueRequestMessage", "PlaybackQueueRequestMessage", params, waitForResponse); + } + sendIntroduction() { + let body = { + uniqueIdentifier: this.pairingId, + name: 'node-appletv-x', + localizedModelName: 'iPhone', + systemBuildVersion: '17C54', + applicationBundleIdentifier: 'com.apple.TVRemote', + applicationBundleVersion: '344.28', + protocolVersion: 1, + allowsPairing: true, + lastSupportedMessageType: 45, + supportsSystemPairing: true, + }; + return this.sendMessage('DeviceInfoMessage', 'DeviceInfoMessage', body, true); + } + sendConnectionState() { + let that = this; + return protobufjs_1.load(path.resolve(__dirname + "/protos/SetConnectionStateMessage.proto")) + .then(root => { + let type = root.lookupType('SetConnectionStateMessage'); + let stateEnum = type.lookupEnum('ConnectionState'); + let message = type.create({ + state: stateEnum.values['Connected'] + }); + return that + .connection + .send(message, false, 0, that.credentials); + }); + } + sendClientUpdatesConfig(config) { + return this.sendMessage('ClientUpdatesConfigMessage', 'ClientUpdatesConfigMessage', config, false); + } + sendWakeDevice() { + return this.sendMessage('WakeDeviceMessage', 'WakeDeviceMessage', {}, false); + } +} +exports.AppleTV = AppleTV; +(function (AppleTV) { + /** An enumeration of key presses available. + */ + let Key; + (function (Key) { + Key[Key["Up"] = 0] = "Up"; + Key[Key["Down"] = 1] = "Down"; + Key[Key["Left"] = 2] = "Left"; + Key[Key["Right"] = 3] = "Right"; + Key[Key["Menu"] = 4] = "Menu"; + Key[Key["Play"] = 5] = "Play"; + Key[Key["Pause"] = 6] = "Pause"; + Key[Key["Next"] = 7] = "Next"; + Key[Key["Previous"] = 8] = "Previous"; + Key[Key["Suspend"] = 9] = "Suspend"; + Key[Key["Select"] = 10] = "Select"; + Key[Key["LongTv"] = 11] = "LongTv"; + Key[Key["Tv"] = 12] = "Tv"; + })(Key = AppleTV.Key || (AppleTV.Key = {})); + /** Convert a string representation of a key to the correct enum type. + * @param string The string. + * @returns The key enum value. + */ + function key(string) { + if (string == "up") { + return AppleTV.Key.Up; + } + else if (string == "down") { + return AppleTV.Key.Down; + } + else if (string == "left") { + return AppleTV.Key.Left; + } + else if (string == "right") { + return AppleTV.Key.Right; + } + else if (string == "menu") { + return AppleTV.Key.Menu; + } + else if (string == "play") { + return AppleTV.Key.Play; + } + else if (string == "pause") { + return AppleTV.Key.Pause; + } + else if (string == "next") { + return AppleTV.Key.Next; + } + else if (string == "previous") { + return AppleTV.Key.Previous; + } + else if (string == "suspend") { + return AppleTV.Key.Suspend; + } + else if (string == "select") { + return AppleTV.Key.Select; + } + else if (string == "longtv") { + return AppleTV.Key.LongTv; + } + else if (string == "tv") { + return AppleTV.Key.Tv; + } + } + AppleTV.key = key; +})(AppleTV = exports.AppleTV || (exports.AppleTV = {})); diff --git a/dist/lib/browser.d.ts b/dist/lib/browser.d.ts new file mode 100644 index 0000000..c7892b8 --- /dev/null +++ b/dist/lib/browser.d.ts @@ -0,0 +1,20 @@ +import { AppleTV } from './appletv'; +export declare class Browser { + private browser; + private services; + private uniqueIdentifier; + private onComplete; + private onFailure; + /** + * Creates a new Browser + * @param log An optional function that takes a string to provide verbose logging. + */ + constructor(); + /** + * Scans for AppleTVs on the local network. + * @param uniqueIdentifier An optional identifier for the AppleTV to scan for. The AppleTV advertises this via Bonjour. + * @param timeout An optional timeout value (in seconds) to give up the search after. + * @returns A promise that resolves to an array of AppleTV objects. If you provide a `uniqueIdentifier` the array is guaranteed to only contain one object. + */ + scan(uniqueIdentifier?: string, timeout?: number): Promise; +} diff --git a/dist/lib/browser.js b/dist/lib/browser.js new file mode 100644 index 0000000..0c918c9 --- /dev/null +++ b/dist/lib/browser.js @@ -0,0 +1,66 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const mdns = require("mdns"); +const appletv_1 = require("./appletv"); +class Browser { + /** + * Creates a new Browser + * @param log An optional function that takes a string to provide verbose logging. + */ + constructor() { + let sequence = [ + mdns.rst.DNSServiceResolve(), + 'DNSServiceGetAddrInfo' in mdns.dns_sd ? mdns.rst.DNSServiceGetAddrInfo() : mdns.rst.getaddrinfo({ families: [4] }), + mdns.rst.makeAddressesUnique() + ]; + this.browser = mdns.createBrowser(mdns.tcp('mediaremotetv'), { resolverSequence: sequence }); + this.services = []; + this.devices = []; + let that = this; + this.browser.on('serviceUp', function (service) { + let device = new appletv_1.AppleTV(service); + if (that.uniqueIdentifier && device.uid == that.uniqueIdentifier) { + that.browser.stop(); + that.onComplete([device]); + } + else { + if(that.devices.find(uuid => uuid == device.uid)) { + return; + } else { + that.devices.push(device.uid); + that.services.push(device); + } + } + }); + } + /** + * Scans for AppleTVs on the local network. + * @param uniqueIdentifier An optional identifier for the AppleTV to scan for. The AppleTV advertises this via Bonjour. + * @param timeout An optional timeout value (in seconds) to give up the search after. + * @returns A promise that resolves to an array of AppleTV objects. If you provide a `uniqueIdentifier` the array is guaranteed to only contain one object. + */ + scan(uniqueIdentifier, timeout) { + this.services = []; + this.uniqueIdentifier = uniqueIdentifier; + this.browser.start(); + let that = this; + let to = timeout == null ? 5 : timeout; + return new Promise((resolve, reject) => { + that.onComplete = resolve; + that.onFailure = reject; + setTimeout(() => { + that.browser.stop(); + if (that.uniqueIdentifier) { + reject(new Error("Failed to locate specified AppleTV on the network")); + } + else { + resolve(that.services + .sort((a, b) => { + return a > b ? 1 : -1; + })); + } + }, to * 1000); + }); + } +} +exports.Browser = Browser; diff --git a/dist/lib/connection.d.ts b/dist/lib/connection.d.ts new file mode 100644 index 0000000..e35ba14 --- /dev/null +++ b/dist/lib/connection.d.ts @@ -0,0 +1,31 @@ +import { Message as ProtoMessage } from 'protobufjs'; +import { Credentials } from './credentials'; +import { AppleTV } from './appletv'; +import TypedEventEmitter from './typed-events'; +import { Message } from './message'; +export declare class Connection extends TypedEventEmitter { + device: AppleTV; + isOpen: boolean; + private socket; + private callbacks; + private ProtocolMessage; + private buffer; + constructor(device: AppleTV); + private addCallback; + private executeCallbacks; + open(): Promise; + close(): void; + sendBlank(typeName: string, waitForResponse: boolean, credentials?: Credentials): Promise; + send(message: ProtoMessage<{}>, waitForResponse: boolean, priority: number, credentials?: Credentials): Promise; + private sendProtocolMessage; + private decodeMessage; +} +export declare module Connection { + interface Events { + connect: void; + message: Message; + close: void; + error: Error; + debug: string; + } +} diff --git a/dist/lib/connection.js b/dist/lib/connection.js new file mode 100644 index 0000000..fd43fc0 --- /dev/null +++ b/dist/lib/connection.js @@ -0,0 +1,187 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const net_1 = require("net"); +const protobufjs_1 = require("protobufjs"); +const uuid_1 = require("uuid"); +const path = require("path"); +const varint = require("varint"); +const snake = require("snake-case"); +const camelcase = require("camelcase"); +const typed_events_1 = require("./typed-events"); +const message_1 = require("./message"); +class Connection extends typed_events_1.default { + constructor(device) { + super(); + this.device = device; + this.callbacks = new Map(); + this.buffer = Buffer.alloc(0); + this.socket = new net_1.Socket(); + let that = this; + this.socket.on('data', (data) => { + try { + that.buffer = Buffer.concat([that.buffer, data]); + let length = varint.decode(that.buffer); + let messageBytes = that.buffer.slice(varint.decode.bytes, length + varint.decode.bytes); + if (messageBytes.length < length) { + that.emit('debug', "Message length mismatch"); + return; + } + that.buffer = that.buffer.slice(length + varint.decode.bytes); + that.emit('debug', "DEBUG: <<<< Received Data=" + messageBytes.toString('hex')); + if (device.credentials && device.credentials.readKey) { + messageBytes = device.credentials.decrypt(messageBytes); + that.emit('debug', "DEBUG: Decrypted Data=" + messageBytes.toString('hex')); + } + that.decodeMessage(messageBytes) + .then(protoMessage => { + let message = new message_1.Message(protoMessage); + that.emit('message', message); + that.executeCallbacks(message.identifier, message); + }) + .catch(error => { + that.emit('error', error); + }); + } + catch (error) { + that.emit('error', error); + } + }); + this.socket.on('connect', () => { + that.emit('connect'); + that.isOpen = true; + }); + this.socket.on('close', () => { + that.emit('close'); + that.isOpen = false; + }); + this.socket.on('error', (error) => { + that.emit('error', error); + }); + } + addCallback(identifier, callback) { + if (this.callbacks.has(identifier)) { + this.callbacks.get(identifier).push({ + callback: callback + }); + } + else { + this.callbacks.set(identifier, [{ + callback: callback + }]); + } + } + executeCallbacks(identifier, message) { + let callbacks = this.callbacks.get(identifier); + if (callbacks) { + for (var i = 0; i < callbacks.length; i++) { + let callback = callbacks[i]; + callback.callback(message); + this.callbacks.get(identifier).splice(i, 1); + } + return true; + } + else { + return false; + } + } + open() { + let that = this; + return protobufjs_1.load(path.resolve(__dirname + "/protos/ProtocolMessage.proto")) + .then(root => { + that.ProtocolMessage = root.lookupType("ProtocolMessage"); + return new Promise((resolve, reject) => { + that.socket.connect(this.device.port, this.device.address, function () { + resolve(); + }); + }); + }); + } + close() { + this.socket.end(); + } + sendBlank(typeName, waitForResponse, credentials) { + let that = this; + return protobufjs_1.load(path.resolve(__dirname + "/protos/ProtocolMessage.proto")) + .then(root => { + let ProtocolMessage = root.lookupType("ProtocolMessage"); + let types = ProtocolMessage.lookupEnum("Type"); + let type = types.values[typeName]; + let name = camelcase(typeName); + let message = ProtocolMessage.create({ + type: type, + priority: 0 + }); + return that.sendProtocolMessage(message, name, type, waitForResponse, credentials); + }); + } + send(message, waitForResponse, priority, credentials) { + let ProtocolMessage = message.$type.parent['ProtocolMessage']; + let types = ProtocolMessage.lookupEnum("Type"); + let name = message.$type.name; + let typeName = snake.snakeCase(name).toUpperCase(); + let type = types.values[typeName]; + var outerMessage = ProtocolMessage.create({ + priority: priority, + type: type + }); + if (Object.keys(message.toJSON()).length > 0) { + let field = outerMessage.$type.fieldsArray.filter((f) => { return f.type == message.$type.name; })[0]; + outerMessage[field.name] = message; + } + return this.sendProtocolMessage(outerMessage, name, type, waitForResponse, credentials); + } + sendProtocolMessage(message, name, type, waitForResponse, credentials) { + let that = this; + return new Promise((resolve, reject) => { + let ProtocolMessage = message.$type; + if (waitForResponse) { + let identifier = uuid_1.v4(); + message["identifier"] = identifier; + let callback = (message) => { + resolve(message); + }; + that.addCallback(identifier, callback); + } + let data = ProtocolMessage.encode(message).finish(); + that.emit('debug', "DEBUG: >>>> Send Data=" + data.toString('hex')); + if (credentials && credentials.writeKey) { + let encrypted = credentials.encrypt(data); + that.emit('debug', "DEBUG: >>>> Send Encrypted Data=" + encrypted.toString('hex')); + that.emit('debug', "DEBUG: >>>> Send Protobuf=" + JSON.stringify(message.toJSON(), null, 2)); + let messageLength = Buffer.from(varint.encode(encrypted.length)); + let bytes = Buffer.concat([messageLength, encrypted]); + that.socket.write(bytes); + } + else { + that.emit('debug', "DEBUG: >>>> Send Protobuf=" + JSON.stringify(message.toJSON(), null, 2)); + let messageLength = Buffer.from(varint.encode(data.length)); + let bytes = Buffer.concat([messageLength, data]); + that.socket.write(bytes); + } + if (!waitForResponse) { + resolve(new message_1.Message(message)); + } + }); + } + decodeMessage(data) { + let that = this; + return protobufjs_1.load(path.resolve(__dirname + "/protos/ProtocolMessage.proto")) + .then(root => { + let ProtocolMessage = root.lookupType("ProtocolMessage"); + let preMessage = ProtocolMessage.decode(data); + let type = preMessage.toJSON().type; + if (type == null) { + return Promise.resolve(preMessage); + } + let name = type[0].toUpperCase() + camelcase(type).substring(1); + return protobufjs_1.load(path.resolve(__dirname + "/protos/" + name + ".proto")) + .then(root => { + let ProtocolMessage = root.lookupType("ProtocolMessage"); + let message = ProtocolMessage.decode(data); + that.emit('debug', "DEBUG: <<<< Received Protobuf=" + JSON.stringify(message.toJSON(), null, 2)); + return message; + }); + }); + } +} +exports.Connection = Connection; diff --git a/dist/lib/credentials.d.ts b/dist/lib/credentials.d.ts new file mode 100644 index 0000000..7229b47 --- /dev/null +++ b/dist/lib/credentials.d.ts @@ -0,0 +1,26 @@ +/// +export declare class Credentials { + uniqueIdentifier: string; + identifier: Buffer; + pairingId: string; + publicKey: Buffer; + encryptionKey: Buffer; + readKey: Buffer; + writeKey: Buffer; + private encryptCount; + private decryptCount; + constructor(uniqueIdentifier: string, identifier: Buffer, pairingId: string, publicKey: Buffer, encryptionKey: Buffer); + /** + * Parse a credentials string into a Credentials object. + * @param text The credentials string. + * @returns A credentials object. + */ + static parse(text: string): Credentials; + /** + * Returns a string representation of a Credentials object. + * @returns A string representation of a Credentials object. + */ + toString(): string; + encrypt(message: Buffer): Buffer; + decrypt(message: Buffer): Buffer; +} diff --git a/dist/lib/credentials.js b/dist/lib/credentials.js new file mode 100644 index 0000000..6e39607 --- /dev/null +++ b/dist/lib/credentials.js @@ -0,0 +1,50 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const encryption_1 = require("./util/encryption"); +const number_1 = require("./util/number"); +class Credentials { + constructor(uniqueIdentifier, identifier, pairingId, publicKey, encryptionKey) { + this.uniqueIdentifier = uniqueIdentifier; + this.identifier = identifier; + this.pairingId = pairingId; + this.publicKey = publicKey; + this.encryptionKey = encryptionKey; + this.encryptCount = 0; + this.decryptCount = 0; + } + /** + * Parse a credentials string into a Credentials object. + * @param text The credentials string. + * @returns A credentials object. + */ + static parse(text) { + let parts = text.split(':'); + return new Credentials(parts[0], Buffer.from(parts[1], 'hex'), Buffer.from(parts[2], 'hex').toString(), Buffer.from(parts[3], 'hex'), Buffer.from(parts[4], 'hex')); + } + /** + * Returns a string representation of a Credentials object. + * @returns A string representation of a Credentials object. + */ + toString() { + return this.uniqueIdentifier + + ":" + + this.identifier.toString('hex') + + ":" + + Buffer.from(this.pairingId).toString('hex') + + ":" + + this.publicKey.toString('hex') + + ":" + + this.encryptionKey.toString('hex'); + } + encrypt(message) { + let nonce = number_1.default.UInt53toBufferLE(this.encryptCount++); + return Buffer.concat(encryption_1.default.encryptAndSeal(message, null, nonce, this.writeKey)); + } + decrypt(message) { + let nonce = number_1.default.UInt53toBufferLE(this.decryptCount++); + let cipherText = message.slice(0, -16); + let hmac = message.slice(-16); + return encryption_1.default.verifyAndDecrypt(cipherText, hmac, null, nonce, this.readKey); + } +} +exports.Credentials = Credentials; diff --git a/dist/lib/message.d.ts b/dist/lib/message.d.ts new file mode 100644 index 0000000..f7b6ae0 --- /dev/null +++ b/dist/lib/message.d.ts @@ -0,0 +1,57 @@ +import { Message as ProtoMessage } from 'protobufjs'; +export declare class Message { + private message; + type: Message.Type; + identifier: string; + payload: any; + constructor(message: ProtoMessage<{}>); + toObject(): any; +} +export declare module Message { + enum Type { + SendCommandMessage = 1, + CommandResultMessage = 2, + GetStateMessage = 3, + SetStateMessage = 4, + SetArtworkMessage = 5, + RegisterHidDeviceMessage = 6, + RegisterHidDeviceResultMessage = 7, + SendHidEventMessage = 8, + SendHidReportMessage = 9, + SendVirtualTouchEventMessage = 10, + NotificationMessage = 11, + ContentItemsChangedNotificationMessage = 12, + DeviceInfoMessage = 15, + ClientUpdatesConfigMessage = 16, + VolumeControlAvailabilityMessage = 17, + GameControllerMessage = 18, + RegisterGameControllerMessage = 19, + RegisterGameControllerResponseMessage = 20, + UnregisterGameControllerMessage = 21, + RegisterForGameControllerEventsMessage = 22, + KeyboardMessage = 23, + GetKeyboardSessionMessage = 24, + TextInputMessage = 25, + GetVoiceInputDevicesMessage = 26, + GetVoiceInputDevicesResponseMessage = 27, + RegisterVoiceInputDeviceMessage = 28, + RegisterVoiceInputDeviceResponseMessage = 29, + SetRecordingStateMessage = 30, + SendVoiceInputMessage = 31, + PlaybackQueueRequestMessage = 32, + TransactionMessage = 33, + CryptoPairingMessage = 34, + GameControllerPropertiesMessage = 35, + SetReadyStateMessage = 36, + DeviceInfoUpdate = 37, + SetDisconnectingStateMessage = 38, + SendButtonEvent = 39, + SetHiliteModeMessage = 40, + WakeDeviceMessage = 41, + GenericMessage = 42, + SendPackedVirtualTouchEvent = 43, + SendLyricsEvent = 44, + PlaybackQueueCapabilitiesRequest = 45, + ModifyOutputContextRequest = 46 + } +} diff --git a/dist/lib/message.js b/dist/lib/message.js new file mode 100644 index 0000000..05992d0 --- /dev/null +++ b/dist/lib/message.js @@ -0,0 +1,66 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class Message { + constructor(message) { + this.message = message; + this.type = message['type']; + this.identifier = message['identifier']; + let keys = Object.keys(message.toJSON()).filter(key => { return key[0] == "."; }); + if (keys.length > 0) { + this.payload = message[keys[0]]; + } + } + toObject() { + return this.message; + } +} +exports.Message = Message; +(function (Message) { + let Type; + (function (Type) { + Type[Type["SendCommandMessage"] = 1] = "SendCommandMessage"; + Type[Type["CommandResultMessage"] = 2] = "CommandResultMessage"; + Type[Type["GetStateMessage"] = 3] = "GetStateMessage"; + Type[Type["SetStateMessage"] = 4] = "SetStateMessage"; + Type[Type["SetArtworkMessage"] = 5] = "SetArtworkMessage"; + Type[Type["RegisterHidDeviceMessage"] = 6] = "RegisterHidDeviceMessage"; + Type[Type["RegisterHidDeviceResultMessage"] = 7] = "RegisterHidDeviceResultMessage"; + Type[Type["SendHidEventMessage"] = 8] = "SendHidEventMessage"; + Type[Type["SendHidReportMessage"] = 9] = "SendHidReportMessage"; + Type[Type["SendVirtualTouchEventMessage"] = 10] = "SendVirtualTouchEventMessage"; + Type[Type["NotificationMessage"] = 11] = "NotificationMessage"; + Type[Type["ContentItemsChangedNotificationMessage"] = 12] = "ContentItemsChangedNotificationMessage"; + Type[Type["DeviceInfoMessage"] = 15] = "DeviceInfoMessage"; + Type[Type["ClientUpdatesConfigMessage"] = 16] = "ClientUpdatesConfigMessage"; + Type[Type["VolumeControlAvailabilityMessage"] = 17] = "VolumeControlAvailabilityMessage"; + Type[Type["GameControllerMessage"] = 18] = "GameControllerMessage"; + Type[Type["RegisterGameControllerMessage"] = 19] = "RegisterGameControllerMessage"; + Type[Type["RegisterGameControllerResponseMessage"] = 20] = "RegisterGameControllerResponseMessage"; + Type[Type["UnregisterGameControllerMessage"] = 21] = "UnregisterGameControllerMessage"; + Type[Type["RegisterForGameControllerEventsMessage"] = 22] = "RegisterForGameControllerEventsMessage"; + Type[Type["KeyboardMessage"] = 23] = "KeyboardMessage"; + Type[Type["GetKeyboardSessionMessage"] = 24] = "GetKeyboardSessionMessage"; + Type[Type["TextInputMessage"] = 25] = "TextInputMessage"; + Type[Type["GetVoiceInputDevicesMessage"] = 26] = "GetVoiceInputDevicesMessage"; + Type[Type["GetVoiceInputDevicesResponseMessage"] = 27] = "GetVoiceInputDevicesResponseMessage"; + Type[Type["RegisterVoiceInputDeviceMessage"] = 28] = "RegisterVoiceInputDeviceMessage"; + Type[Type["RegisterVoiceInputDeviceResponseMessage"] = 29] = "RegisterVoiceInputDeviceResponseMessage"; + Type[Type["SetRecordingStateMessage"] = 30] = "SetRecordingStateMessage"; + Type[Type["SendVoiceInputMessage"] = 31] = "SendVoiceInputMessage"; + Type[Type["PlaybackQueueRequestMessage"] = 32] = "PlaybackQueueRequestMessage"; + Type[Type["TransactionMessage"] = 33] = "TransactionMessage"; + Type[Type["CryptoPairingMessage"] = 34] = "CryptoPairingMessage"; + Type[Type["GameControllerPropertiesMessage"] = 35] = "GameControllerPropertiesMessage"; + Type[Type["SetReadyStateMessage"] = 36] = "SetReadyStateMessage"; + Type[Type["DeviceInfoUpdate"] = 37] = "DeviceInfoUpdate"; + Type[Type["SetDisconnectingStateMessage"] = 38] = "SetDisconnectingStateMessage"; + Type[Type["SendButtonEvent"] = 39] = "SendButtonEvent"; + Type[Type["SetHiliteModeMessage"] = 40] = "SetHiliteModeMessage"; + Type[Type["WakeDeviceMessage"] = 41] = "WakeDeviceMessage"; + Type[Type["GenericMessage"] = 42] = "GenericMessage"; + Type[Type["SendPackedVirtualTouchEvent"] = 43] = "SendPackedVirtualTouchEvent"; + Type[Type["SendLyricsEvent"] = 44] = "SendLyricsEvent"; + Type[Type["PlaybackQueueCapabilitiesRequest"] = 45] = "PlaybackQueueCapabilitiesRequest"; + Type[Type["ModifyOutputContextRequest"] = 46] = "ModifyOutputContextRequest"; + })(Type = Message.Type || (Message.Type = {})); +})(Message = exports.Message || (exports.Message = {})); diff --git a/dist/lib/now-playing-info.d.ts b/dist/lib/now-playing-info.d.ts new file mode 100644 index 0000000..57bf362 --- /dev/null +++ b/dist/lib/now-playing-info.d.ts @@ -0,0 +1,21 @@ +export declare class NowPlayingInfo { + message: any; + duration: number; + elapsedTime: number; + title: string; + artist: string; + album: string; + appDisplayName: string; + appBundleIdentifier: string; + playbackState: NowPlayingInfo.State; + timestamp: number; + constructor(message: any); + percentCompleted(): string; + toString(): string; +} +export declare module NowPlayingInfo { + enum State { + Playing = "playing", + Paused = "paused" + } +} diff --git a/dist/lib/now-playing-info.js b/dist/lib/now-playing-info.js new file mode 100644 index 0000000..1ce09de --- /dev/null +++ b/dist/lib/now-playing-info.js @@ -0,0 +1,55 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class NowPlayingInfo { + constructor(message) { + this.message = message; + let nowPlayingInfo = message.nowPlayingInfo; + if (nowPlayingInfo) { + this.duration = nowPlayingInfo.duration; + this.elapsedTime = nowPlayingInfo.elapsedTime; + this.title = nowPlayingInfo.title; + this.artist = nowPlayingInfo.artist; + this.album = nowPlayingInfo.album; + this.timestamp = nowPlayingInfo.timestamp; + } + this.appDisplayName = message.displayName; + this.appBundleIdentifier = message.displayID; + if (message.playbackState == 2) { + this.playbackState = NowPlayingInfo.State.Paused; + } + else if (message.playbackState == 1) { + this.playbackState = NowPlayingInfo.State.Playing; + } + } + percentCompleted() { + if (!this.elapsedTime || !this.duration) { + return "0.00"; + } + return ((this.elapsedTime / this.duration) * 100).toPrecision(3); + } + toString() { + if (this.artist) { + let album = this.album == null ? '' : " -- " + this.album + " "; + return this.title + " by " + this.artist + album + " (" + this.percentCompleted() + "%) | " + + this.appDisplayName + " (" + this.appBundleIdentifier + ") | " + + this.playbackState; + } + else if (this.title) { + return this.title + " (" + this.percentCompleted() + "%) | " + + this.appDisplayName + " (" + this.appBundleIdentifier + ") | " + + this.playbackState; + } + else { + return this.appDisplayName + " (" + this.appBundleIdentifier + ") | " + + this.playbackState; + } + } +} +exports.NowPlayingInfo = NowPlayingInfo; +(function (NowPlayingInfo) { + let State; + (function (State) { + State["Playing"] = "playing"; + State["Paused"] = "paused"; + })(State = NowPlayingInfo.State || (NowPlayingInfo.State = {})); +})(NowPlayingInfo = exports.NowPlayingInfo || (exports.NowPlayingInfo = {})); diff --git a/dist/lib/pairing.d.ts b/dist/lib/pairing.d.ts new file mode 100644 index 0000000..a742319 --- /dev/null +++ b/dist/lib/pairing.d.ts @@ -0,0 +1,19 @@ +import { AppleTV } from './appletv'; +export declare class Pairing { + device: AppleTV; + private srp; + private key; + private publicKey; + private proof; + private deviceSalt; + private devicePublicKey; + private deviceProof; + constructor(device: AppleTV); + /** + * Initiates the pairing process + * @returns A promise that resolves to a callback which takes in the pairing pin from the Apple TV. + */ + initiatePair(): Promise<(pin: string) => Promise>; + private completePairing; + private waitForSequence; +} diff --git a/dist/lib/pairing.js b/dist/lib/pairing.js new file mode 100644 index 0000000..2d19721 --- /dev/null +++ b/dist/lib/pairing.js @@ -0,0 +1,139 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const srp = require("fast-srp-hap"); +const crypto = require("crypto"); +const ed25519 = require("ed25519-wasm-pro"); +const credentials_1 = require("./credentials"); +const message_1 = require("./message"); +const tlv_1 = require("./util/tlv"); +const encryption_1 = require("./util/encryption"); +class Pairing { + constructor(device) { + this.device = device; + this.key = crypto.randomBytes(32); + } + /** + * Initiates the pairing process + * @returns A promise that resolves to a callback which takes in the pairing pin from the Apple TV. + */ + initiatePair() { + let that = this; + let tlvData = tlv_1.default.encode(tlv_1.default.Tag.PairingMethod, 0x00, tlv_1.default.Tag.Sequence, 0x01); + let message = { + status: 0, + isUsingSystemPairing: false, + isRetrying: false, + state: 2, + pairingData: tlvData + }; + return this.device + .sendMessage('CryptoPairingMessage', 'CryptoPairingMessage', message, false) + .then(() => { + return that.waitForSequence(0x02); + }) + .then(message => { + let pairingData = message.payload.pairingData; + let tlvData = tlv_1.default.decode(pairingData); + if (tlvData[tlv_1.default.Tag.BackOff]) { + let backOff = tlvData[tlv_1.default.Tag.BackOff]; + let seconds = backOff.readIntBE(0, backOff.byteLength); + if (seconds > 0) { + throw new Error("You've attempt to pair too recently. Try again in " + seconds + " seconds."); + } + } + if (tlvData[tlv_1.default.Tag.ErrorCode]) { + let buffer = tlvData[tlv_1.default.Tag.ErrorCode]; + throw new Error(that.device.name + " responded with error code " + buffer.readIntBE(0, buffer.byteLength) + ". Try rebooting your Apple TV."); + } + that.deviceSalt = tlvData[tlv_1.default.Tag.Salt]; + that.devicePublicKey = tlvData[tlv_1.default.Tag.PublicKey]; + if (that.deviceSalt.byteLength != 16) { + throw new Error(`salt must be 16 bytes (but was ${that.deviceSalt.byteLength})`); + } + if (that.devicePublicKey.byteLength !== 384) { + throw new Error(`serverPublicKey must be 384 bytes (but was ${that.devicePublicKey.byteLength})`); + } + return Promise.resolve((pin) => { + return that.completePairing(pin); + }); + }); + } + completePairing(pin) { + this.srp = srp.Client(srp.params['3072'], this.deviceSalt, Buffer.from('Pair-Setup'), Buffer.from(pin), this.key); + this.srp.setB(this.devicePublicKey); + this.publicKey = this.srp.computeA(); + this.proof = this.srp.computeM1(); + // console.log("DEBUG: Client Public Key=" + this.publicKey.toString('hex') + "\nProof=" + this.proof.toString('hex')); + let that = this; + let tlvData = tlv_1.default.encode(tlv_1.default.Tag.Sequence, 0x03, tlv_1.default.Tag.PublicKey, that.publicKey, tlv_1.default.Tag.Proof, that.proof); + let message = { + status: 0, + pairingData: tlvData + }; + return this.device + .sendMessage('CryptoPairingMessage', 'CryptoPairingMessage', message, false) + .then(() => { + return that.waitForSequence(0x04); + }) + .then(message => { + let pairingData = message.payload.pairingData; + that.deviceProof = tlv_1.default.decode(pairingData)[tlv_1.default.Tag.Proof]; + //console.log("DEBUG: Device Proof=" + that.deviceProof.toString('hex')); + that.srp.checkM2(that.deviceProof); + let seed = crypto.randomBytes(32); + let keyPair = ed25519.createKeyPair(seed); + let privateKey = keyPair.secretKey; + let publicKey = keyPair.publicKey; + let sharedSecret = that.srp.computeK(); + let deviceHash = encryption_1.default.HKDF("sha512", Buffer.from("Pair-Setup-Controller-Sign-Salt"), sharedSecret, Buffer.from("Pair-Setup-Controller-Sign-Info"), 32); + let deviceInfo = Buffer.concat([deviceHash, Buffer.from(that.device.pairingId), publicKey]); + let deviceSignature = ed25519.sign(deviceInfo, publicKey, privateKey); + let encryptionKey = encryption_1.default.HKDF("sha512", Buffer.from("Pair-Setup-Encrypt-Salt"), sharedSecret, Buffer.from("Pair-Setup-Encrypt-Info"), 32); + let tlvData = tlv_1.default.encode(tlv_1.default.Tag.Username, Buffer.from(that.device.pairingId), tlv_1.default.Tag.PublicKey, publicKey, tlv_1.default.Tag.Signature, deviceSignature); + let encryptedTLV = Buffer.concat(encryption_1.default.encryptAndSeal(tlvData, null, Buffer.from('PS-Msg05'), encryptionKey)); + let outerTLV = tlv_1.default.encode(tlv_1.default.Tag.Sequence, 0x05, tlv_1.default.Tag.EncryptedData, encryptedTLV); + let nextMessage = { + status: 0, + pairingData: outerTLV + }; + return that.device + .sendMessage('CryptoPairingMessage', 'CryptoPairingMessage', nextMessage, false) + .then(() => { + return that.waitForSequence(0x06); + }) + .then(message => { + let encryptedData = tlv_1.default.decode(message.payload.pairingData)[tlv_1.default.Tag.EncryptedData]; + let cipherText = encryptedData.slice(0, -16); + let hmac = encryptedData.slice(-16); + let decrpytedData = encryption_1.default.verifyAndDecrypt(cipherText, hmac, null, Buffer.from('PS-Msg06'), encryptionKey); + let tlvData = tlv_1.default.decode(decrpytedData); + that.device.credentials = new credentials_1.Credentials(that.device.uid, tlvData[tlv_1.default.Tag.Username], that.device.pairingId, tlvData[tlv_1.default.Tag.PublicKey], seed); + return that.device; + }); + }); + } + waitForSequence(sequence, timeout = 3) { + let that = this; + let handler = (message, resolve) => { + let tlvData = tlv_1.default.decode(message.payload.pairingData); + if (Buffer.from([sequence]).equals(tlvData[tlv_1.default.Tag.Sequence])) { + resolve(message); + } + }; + return new Promise((resolve, reject) => { + that.device.on('message', (message) => { + if (message.type == message_1.Message.Type.CryptoPairingMessage) { + handler(message, resolve); + } + }); + setTimeout(() => { + reject(new Error("Timed out waiting for crypto sequence " + sequence)); + }, timeout * 1000); + }) + .then(value => { + that.device.removeListener('message', handler); + return value; + }); + } +} +exports.Pairing = Pairing; diff --git a/dist/lib/protos/AudioBuffer.proto b/dist/lib/protos/AudioBuffer.proto new file mode 100644 index 0000000..d908e1a --- /dev/null +++ b/dist/lib/protos/AudioBuffer.proto @@ -0,0 +1,16 @@ +syntax = "proto2"; + +message AudioPacket { + required int32 startOffset = 1; + required int32 variableFramesInPacket = 2; + required int32 dataByteSize = 3; +} + +message AudioBuffer { + required AudioFormatSettings formatSettings = 1; + optional int32 packetCount = 2; + optional int32 maximumPacketSize = 3; + optional int32 packetCapacity = 4; + required bytes contents = 5; + repeated AudioPacket packetDescriptions = 6; +} diff --git a/dist/lib/protos/AudioFormatSettings.proto b/dist/lib/protos/AudioFormatSettings.proto new file mode 100755 index 0000000..b3e5ea8 --- /dev/null +++ b/dist/lib/protos/AudioFormatSettings.proto @@ -0,0 +1,5 @@ +syntax = "proto2"; + +message AudioFormatSettings { + optional bytes formatSettingsPlistData = 1; +} \ No newline at end of file diff --git a/dist/lib/protos/ClientUpdatesConfigMessage.proto b/dist/lib/protos/ClientUpdatesConfigMessage.proto new file mode 100755 index 0000000..ea42b4f --- /dev/null +++ b/dist/lib/protos/ClientUpdatesConfigMessage.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional ClientUpdatesConfigMessage clientUpdatesConfigMessage = 21; +} + +message ClientUpdatesConfigMessage { + optional bool artworkUpdates = 1; + optional bool nowPlayingUpdates = 2; + optional bool volumeUpdates = 3; + optional bool keyboardUpdates = 4; +} + diff --git a/dist/lib/protos/CommandInfo.proto b/dist/lib/protos/CommandInfo.proto new file mode 100755 index 0000000..0c91cb4 --- /dev/null +++ b/dist/lib/protos/CommandInfo.proto @@ -0,0 +1,82 @@ +syntax = "proto2"; + +enum Command { +Unknown=0; +Play=1; +Pause=2; +TogglePlayPause=3; +Stop=4; +NextTrack=5; +PreviousTrack=6; +AdvanceShuffleMode=7; +AdvanceRepeatMode=8; +BeginFastForward=9; +EndFastForward=10; +BeginRewind=11; +EndRewind=12; +Rewind15Seconds=13; +FastForward15Seconds=14; +Rewind30Seconds=15; +FastForward30Seconds=16; + +SkipForward=18; +SkipBackward=19; +ChangePlaybackRate=20; +RateTrack=21; +LikeTrack=22; +DislikeTrack=23; +BookmarkTrack=24; + +SeekToPlaybackPosition=45; +ChangeRepeatMode=46; +ChangeShuffleMode=47; + +EnableLanguageOption=53; +DisableLanguageOption=54; + +NextChapter=25; +PreviousChapter=26; +NextAlbum=27; +PreviousAlbum=28; +NextPlaylist=29; +PreviousPlaylist=30; +BanTrack=31; +AddTrackToWishList=32; +RemoveTrackFromWishList=33; +NextInContext=34; +PreviousInContext=35; + +ResetPlaybackTimeout=41; +SetPlaybackQueue=48; +AddNowPlayingItemToLibrary=49; +CreateRadioStation=50; +AddItemToLibrary=51; +InsertIntoPlaybackQueue=52; + +ReorderPlaybackQueue=55; +RemoveFromPlaybackQueue=56; +PlayItemInPlaybackQueue=57; +} + +message CommandInfo { + optional Command command = 1; + optional bool enabled = 2; + optional bool active = 3; + repeated double preferredIntervals = 4; + optional string localizedTitle = 5; + optional float minimumRating = 6; + optional float maximumRating = 7; + repeated float supportedRates = 8; + optional string localizedShortTitle = 9; + optional int32 repeatMode = 10; + optional int32 shuffleMode = 11; + optional int32 presentationStyle = 12; + optional int32 skipInterval = 13; + optional int32 numAvailableSkips = 14; + optional int32 skipFrequency = 15; + optional int32 canScrub = 16; + repeated int32 supportedPlaybackQueueTypes = 17; + repeated string supportedCustomQueueIdentifiers = 18; + repeated int32 supportedInsertionPositions = 19; + optional bool supportsSharedQueue = 20; +} \ No newline at end of file diff --git a/dist/lib/protos/CommandOptions.proto b/dist/lib/protos/CommandOptions.proto new file mode 100755 index 0000000..21ac017 --- /dev/null +++ b/dist/lib/protos/CommandOptions.proto @@ -0,0 +1,34 @@ +syntax = "proto2"; + +message CommandOptions { + optional string sourceId = 2; + optional string mediaType = 3; + optional bool externalPlayerCommand = 4; + optional float skipInterval = 5; + optional float playbackRate = 6; + optional float rating = 7; + optional bool negative = 8; + optional double playbackPosition = 9; + optional int32 repeatMode = 10; + optional int32 shuffleMode = 11; + optional uint64 trackID = 12; + optional int64 radioStationID = 13; + optional string radioStationHash = 14; + optional bytes systemAppPlaybackQueueData = 15; + optional string destinationAppDisplayID = 16; + optional uint32 sendOptions = 17; + optional bool requestDefermentToPlaybackQueuePosition = 18; + optional string contextID = 19; + optional bool shouldOverrideManuallyCuratedQueue = 20; + optional string stationURL = 21; + optional bool shouldBeginRadioPlayback = 22; + optional int32 playbackQueueInsertionPosition = 23; + optional string contentItemID = 24; + optional int32 playbackQueueOffset = 25; + optional int32 playbackQueueDestinationOffset = 26; + optional bytes languageOption = 27; + optional bytes playbackQueueContext = 28; + optional string insertAfterContentItemID = 29; + optional string nowPlayingContentItemID = 30; + optional int32 replaceIntent = 31; +} \ No newline at end of file diff --git a/dist/lib/protos/CommandResultMessage.proto b/dist/lib/protos/CommandResultMessage.proto new file mode 100644 index 0000000..b9f59a3 --- /dev/null +++ b/dist/lib/protos/CommandResultMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional CommandResultMessage commandResultMessage = 7; +} + +message CommandResultMessage { + optional uint64 value = 1; +} \ No newline at end of file diff --git a/dist/lib/protos/ContentItem.proto b/dist/lib/protos/ContentItem.proto new file mode 100755 index 0000000..0c81263 --- /dev/null +++ b/dist/lib/protos/ContentItem.proto @@ -0,0 +1,18 @@ +syntax = "proto2"; + +import "ContentItemMetadata.proto"; +import "LanguageOption.proto"; + +message ContentItem { + optional string identifier = 1; + optional ContentItemMetadata metadata = 2; + optional bytes info = 3; + repeated LanguageOption availableLanguageOptions = 4; + repeated LanguageOption currentLanguageOptions = 5; +// optional Lyrics lyrics = 6; +// repeated Sections sections = 7; + optional string parentIdentifier = 8; + optional string ancestorIdentifier = 9; + optional string queueIdentifier = 10; + optional string requestIdentifier = 11; +} \ No newline at end of file diff --git a/dist/lib/protos/ContentItemMetadata.proto b/dist/lib/protos/ContentItemMetadata.proto new file mode 100755 index 0000000..ce2378f --- /dev/null +++ b/dist/lib/protos/ContentItemMetadata.proto @@ -0,0 +1,84 @@ +syntax = "proto2"; + +message ContentItemMetadata { + optional string title = 1; + optional string subtitle = 2; + optional bool isContainer = 3; + optional bool isPlayable = 4; + optional float playbackProgress = 5; + optional string albumName = 6; + optional string trackArtistName = 7; + optional string albumArtistName = 8; + optional string directorName = 9; + optional int32 seasonNumber = 10; + optional int32 episodeNumber = 11; + optional double releaseDate = 12; + optional int32 playCount = 13; + optional double duration = 14; + optional string localizedContentRating = 15; + optional bool isExplicitItem = 16; + optional int32 playlistType = 17; + optional int32 radioStationType = 18; + optional bool artworkAvailable = 19; + + optional bool infoAvailable = 21; + optional bool languageOptionsAvailable = 22; + optional int32 numberOfSections = 23; + optional bool lyricsAvailable = 24; + optional int32 editingStyleFlags = 25; + optional bool isStreamingContent = 26; + optional bool isCurrentlyPlaying = 27; + optional string collectionIdentifier = 28; + optional string profileIdentifier = 29; + optional double startTime = 30; + optional string artworkMIMEType = 31; + optional string assetURLString = 32; + optional string composer = 33; + optional int32 discNumber = 34; + optional double elapsedTime = 35; + optional string genre = 36; + optional bool isAlwaysLive = 37; + + optional float playbackRate = 39; + optional int32 chapterCount = 40; + optional int32 totalDiscCount = 41; + optional int32 totalTrackCount = 42; + optional int32 trackNumber = 43; + optional string contentIdentifier = 44; + + optional bool isSharable = 46; + + optional bool isLiked = 48; + optional bool isInWishList = 49; + optional int64 radioStationIdentifier = 50; + + optional string radioStationName = 52; + optional string radioStationString = 53; + optional int64 iTunesStoreIdentifier = 54; + optional int64 iTunesStoreSubscriptionIdentifier = 55; + optional int64 iTunesStoreArtistIdentifier = 56; + optional int64 iTunesStoreAlbumIdentifier = 57; + optional bytes purchaseInfoData = 58; + optional float defaultPlaybackRate = 59; + optional int32 downloadState = 60; + optional float downloadProgress = 61; + optional bytes appMetricsData = 62; + optional string seriesName = 63; + optional int32 mediaType = 64; + optional int32 mediaSubType = 65; + + optional bytes nowPlayingInfoData = 67; + optional bytes userInfoData = 68; + optional bool isSteerable = 69; + optional string artworkURL = 70; + optional string lyricsURL = 71; + optional bytes deviceSpecificUserInfoData = 72; + optional bytes collectionInfoData = 73; + optional double elapsedTimeTimestamp = 74; + optional double inferredTimestamp = 75; + optional string serviceIdentifier = 76; + optional int32 artworkDataWidth = 77; + optional int32 artworkDataHeight = 78; + optional bytes currentPlaybackDateData = 79; + optional string artworkIdentifier = 80; +} \ No newline at end of file diff --git a/dist/lib/protos/CryptoPairingMessage.proto b/dist/lib/protos/CryptoPairingMessage.proto new file mode 100755 index 0000000..1f8a777 --- /dev/null +++ b/dist/lib/protos/CryptoPairingMessage.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional CryptoPairingMessage cryptoPairingMessage = 39; +} + +message CryptoPairingMessage { + optional bytes pairingData = 1; // Example: <00010006 0101> + required int32 status = 2; // Example: 0 + optional bool isRetrying = 3; + optional bool isUsingSystemPairing = 4; + optional int32 state = 5; +} diff --git a/dist/lib/protos/DeviceInfoMessage.proto b/dist/lib/protos/DeviceInfoMessage.proto new file mode 100755 index 0000000..0b9fef2 --- /dev/null +++ b/dist/lib/protos/DeviceInfoMessage.proto @@ -0,0 +1,20 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional DeviceInfoMessage deviceInfoMessage = 20; +} + +message DeviceInfoMessage { + required string uniqueIdentifier = 1; // Example: B8D8678C-9DA9-4D29-9338-5D6B827B8063 + required string name = 2; // Example: Jean's iPhone + optional string localizedModelName = 3; // Example: iPhone + required string systemBuildVersion = 4; // Example: 13F69 + required string applicationBundleIdentifier = 5; // Example: com.example.myremote + optional string applicationBundleVersion = 6; // Example: 107 + required int32 protocolVersion = 7; // Example: 1 + optional int32 lastSupportedMessageType = 8; + optional bool allowsPairing = 9; + optional bool supportsSystemPairing = 10; +} diff --git a/dist/lib/protos/DeviceInfoUpdate.proto b/dist/lib/protos/DeviceInfoUpdate.proto new file mode 100644 index 0000000..c7bd509 --- /dev/null +++ b/dist/lib/protos/DeviceInfoUpdate.proto @@ -0,0 +1,20 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional DeviceInfoUpdate deviceInfoUpdate = 20; +} + +message DeviceInfoUpdate { + required string uniqueIdentifier = 1; // Example: B8D8678C-9DA9-4D29-9338-5D6B827B8063 + required string name = 2; // Example: Jean's iPhone + optional string localizedModelName = 3; // Example: iPhone + required string systemBuildVersion = 4; // Example: 13F69 + required string applicationBundleIdentifier = 5; // Example: com.example.myremote + optional string applicationBundleVersion = 6; // Example: 107 + required int32 protocolVersion = 7; // Example: 1 + optional int32 lastSupportedMessageType = 8; + optional bool allowsPairing = 9; + optional bool supportsSystemPairing = 10; +} diff --git a/dist/lib/protos/GetKeyboardSessionMessage.proto b/dist/lib/protos/GetKeyboardSessionMessage.proto new file mode 100755 index 0000000..b3d7b3e --- /dev/null +++ b/dist/lib/protos/GetKeyboardSessionMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional string getKeyboardSessionMessage = 29; +} + +message GetKeyboardSessionMessage { + +} diff --git a/dist/lib/protos/GetStateMessage.proto b/dist/lib/protos/GetStateMessage.proto new file mode 100644 index 0000000..536ab72 --- /dev/null +++ b/dist/lib/protos/GetStateMessage.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional string getStateMessage = 8; +} diff --git a/dist/lib/protos/KeyboardMessage.proto b/dist/lib/protos/KeyboardMessage.proto new file mode 100755 index 0000000..bd5501b --- /dev/null +++ b/dist/lib/protos/KeyboardMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; +import "TextEditingAttributes.proto"; + +extend ProtocolMessage { + optional KeyboardMessage keyboardMessage = 28; +} + +message KeyboardMessage { + optional int32 state = 1; + optional TextEditingAttributes attributes = 3; +} diff --git a/dist/lib/protos/LanguageOption.proto b/dist/lib/protos/LanguageOption.proto new file mode 100755 index 0000000..d72720b --- /dev/null +++ b/dist/lib/protos/LanguageOption.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; + +message LanguageOption { + optional int32 type = 1; + optional string languageTag = 2; + repeated string characteristics = 3; + optional string displayName = 4; + optional string identifier = 5; +} \ No newline at end of file diff --git a/dist/lib/protos/NotificationMessage.proto b/dist/lib/protos/NotificationMessage.proto new file mode 100755 index 0000000..5df69cd --- /dev/null +++ b/dist/lib/protos/NotificationMessage.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; +import "PlayerPath.proto"; + +extend ProtocolMessage { + optional NotificationMessage notificationMessage = 16; +} + +message NotificationMessage { + repeated string notification = 1; + repeated bytes userInfos = 2; + repeated PlayerPath playerPaths = 3; +} \ No newline at end of file diff --git a/dist/lib/protos/NowPlayingClient.proto b/dist/lib/protos/NowPlayingClient.proto new file mode 100755 index 0000000..eb738a7 --- /dev/null +++ b/dist/lib/protos/NowPlayingClient.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +message NowPlayingClient { + optional int32 processIdentifier = 1; + optional string bundleIdentifier = 2; + optional string parentApplicationBundleIdentifier = 3; + optional int32 processUserIdentifier = 4; + optional int32 nowPlayingVisibility = 5; +// optional TintColor tintColor = 6; + optional string displayName = 7; +} \ No newline at end of file diff --git a/dist/lib/protos/NowPlayingInfo.proto b/dist/lib/protos/NowPlayingInfo.proto new file mode 100755 index 0000000..567ff4c --- /dev/null +++ b/dist/lib/protos/NowPlayingInfo.proto @@ -0,0 +1,22 @@ +syntax = "proto2"; + +message NowPlayingInfo { + optional string album = 1; + optional string artist = 2; + optional double duration = 3; + optional double elapsedTime = 4; + optional float playbackRate = 5; + optional int32 repeatMode = 6; + optional int32 shuffleMode = 7; + optional double timestamp = 8; + optional string title = 9; + optional uint64 uniqueIdentifier = 10; + optional bool isExplicitTrack = 11; + optional bool isMusicApp = 12; + optional int64 radioStationIdentifier = 13; + optional string radioStationHash = 14; + optional string radioStationName = 15; + optional bytes artworkDataDigest = 16; + optional bool isAlwaysLive = 17; + optional bool isAdvertisement = 18; +} \ No newline at end of file diff --git a/dist/lib/protos/NowPlayingPlayer.proto b/dist/lib/protos/NowPlayingPlayer.proto new file mode 100755 index 0000000..6d6dc07 --- /dev/null +++ b/dist/lib/protos/NowPlayingPlayer.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; + +message NowPlayingPlayer { + optional string identifier = 1; + optional string displayName = 2; + optional bool isDefaultPlayer = 3; +} \ No newline at end of file diff --git a/dist/lib/protos/Origin.proto b/dist/lib/protos/Origin.proto new file mode 100755 index 0000000..9d51236 --- /dev/null +++ b/dist/lib/protos/Origin.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +import "DeviceInfoMessage.proto"; + +message Origin { + optional int32 type = 1; + optional string displayName = 2; + optional int32 identifier = 3; + optional DeviceInfoMessage deviceInfo = 4; +} \ No newline at end of file diff --git a/dist/lib/protos/PlaybackQueue.proto b/dist/lib/protos/PlaybackQueue.proto new file mode 100644 index 0000000..5d67163 --- /dev/null +++ b/dist/lib/protos/PlaybackQueue.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +import "ContentItem.proto"; +import "PlayerPath.proto"; + +message PlaybackQueue { + optional ContentItem contentItem = 2; + optional string requestID = 4; + optional PlayerPath playerPath = 5; +} \ No newline at end of file diff --git a/dist/lib/protos/PlaybackQueueRequestMessage.proto b/dist/lib/protos/PlaybackQueueRequestMessage.proto new file mode 100644 index 0000000..c3622df --- /dev/null +++ b/dist/lib/protos/PlaybackQueueRequestMessage.proto @@ -0,0 +1,17 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional PlaybackQueueRequestMessage playbackQueueRequestMessage = 37; +} + +message PlaybackQueueRequestMessage { + required int32 location = 1; + required int32 length = 2; + optional bool includeMetadata = 3; + optional double artworkWidth = 4; + optional double artworkHeight = 5; + optional bool includeLanguageOptions = 9; + required string requestID = 11; +} diff --git a/dist/lib/protos/PlayerPath.proto b/dist/lib/protos/PlayerPath.proto new file mode 100755 index 0000000..630bff1 --- /dev/null +++ b/dist/lib/protos/PlayerPath.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "Origin.proto"; +import "NowPlayingClient.proto"; +import "NowPlayingPlayer.proto"; + +message PlayerPath { + optional Origin origin = 1; + optional NowPlayingClient client = 2; + optional NowPlayingPlayer player = 3; +} \ No newline at end of file diff --git a/dist/lib/protos/ProtocolMessage.proto b/dist/lib/protos/ProtocolMessage.proto new file mode 100755 index 0000000..d9e6951 --- /dev/null +++ b/dist/lib/protos/ProtocolMessage.proto @@ -0,0 +1,92 @@ +syntax = "proto2"; + +message ProtocolMessage { + extensions 6 to max; + + enum Type { + SEND_COMMAND_MESSAGE = 1; + COMMAND_RESULT_MESSAGE = 2; + GET_STATE_MESSAGE = 3; + SET_STATE_MESSAGE = 4; + SET_ARTWORK_MESSAGE = 5; + REGISTER_HID_DEVICE_MESSAGE = 6; + REGISTER_HID_DEVICE_RESULT_MESSAGE = 7; + SEND_HID_EVENT_MESSAGE = 8; + SEND_HID_REPORT_MESSAGE = 9; + SEND_VIRTUAL_TOUCH_EVENT_MESSAGE = 10; + NOTIFICATION_MESSAGE = 11; + CONTENT_ITEMS_CHANGED_NOTIFICATION_MESSAGE = 12; + DEVICE_INFO_MESSAGE = 15; + CLIENT_UPDATES_CONFIG_MESSAGE = 16; + VOLUME_CONTROL_AVAILABILITY_MESSAGE = 17; + GAME_CONTROLLER_MESSAGE = 18; + REGISTER_GAME_CONTROLLER_MESSAGE = 19; + REGISTER_GAME_CONTROLLER_RESPONSE_MESSAGE = 20; + UNREGISTER_GAME_CONTROLLER_MESSAGE = 21; + REGISTER_FOR_GAME_CONTROLLER_EVENTS_MESSAGE = 22; + KEYBOARD_MESSAGE = 23; + GET_KEYBOARD_SESSION_MESSAGE = 24; + TEXT_INPUT_MESSAGE = 25; + GET_VOICE_INPUT_DEVICES_MESSAGE = 26; + GET_VOICE_INPUT_DEVICES_RESPONSE_MESSAGE = 27; + REGISTER_VOICE_INPUT_DEVICE_MESSAGE = 28; + REGISTER_VOICE_INPUT_DEVICE_RESPONSE_MESSAGE = 29; + SET_RECORDING_STATE_MESSAGE = 30; + SEND_VOICE_INPUT_MESSAGE = 31; + PLAYBACK_QUEUE_REQUEST_MESSAGE = 32; + TRANSACTION_MESSAGE = 33; + CRYPTO_PAIRING_MESSAGE = 34; + GAME_CONTROLLER_PROPERTIES_MESSAGE = 35; + SET_READY_STATE_MESSAGE = 36; + DEVICE_INFO_UPDATE = 37; + SET_DISCONNECTING_STATE_MESSAGE = 38; + SEND_BUTTON_EVENT = 39; + SET_HILITE_MODE_MESSAGE = 40; + WAKE_DEVICE_MESSAGE = 41; + GENERIC_MESSAGE = 42; + SEND_PACKED_VIRTUAL_TOUCH_EVENT = 43; + SEND_LYRICS_EVENT = 44; + PLAYBACK_QUEUE_CAPABILITIES_REQUEST = 45; + MODIFY_OUTPUT_CONTEXT_REQUEST = 46; + } + + required Type type = 1; // Identifies which underlying message is filled in. + optional string identifier = 2; + optional int32 priority = 4; + + // One of the following will be filled in. +// optional SendCommandMessage sendCommandMessage = 6; +// optional SendCommandResultMessage sendCommandResultMessage = 7; +// optional SetStateMessage setStateMessage = 9; +// optional SetArtworkMessage setArtworkMessage = 10; +// optional RegisterHIDDeviceMessage registerHIDDeviceMessage = 11; +// optional RegisterHIDDeviceResultMessage registerHIDDeviceResultMessage = 12; +// optional SendHIDEventMessage sendHIDEventMessage = 13; +// optional SendVirtualTouchEventMessage sendVirtualTouchEventMessage = 15; +// optional NotificationMessage notificationMessage = 16; +// optional ContentItemsChangedNotificationMessage contentItemsChangedNotificationMessage = 17; +// optional DeviceInfoMessage deviceInfoMessage = 20; +// optional ClientUpdatesConfigMessage clientUpdatesConfigMessage = 21; +// optional VolumeControlAvailabilityMessage volumeControlAvailabilityMessage = 22; +// optional GameControllerMessage gameControllerMessage = 23; +// optional RegisterGameControllerMessage registerGameControllerMessage = 24; +// optional RegisterGameControllerResponseMessage registerGameControllerResponseMessage = 25; +// optional UnregisterGameControllerMessage unregisterGameControllerMessage = 26; +// optional RegisterForGameControllerEventsMessage registerForGameControllerEventsMessage = 27; +// optional KeyboardMessage keyboardMessage = 28; +// optional GetKeyboardSessionMessage getKeyboardSessionMessage = 29; +// optional TextInputMessage textInputMessage = 30; +// optional GetVoiceInputDevicesMessage getVoiceInputDevicesMessage = 31; +// optional GetVoiceInputDevicesResponseMessage getVoiceInputDevicesResponseMessage = 32; +// optional RegisterVoiceInputDeviceMessage registerVoiceInputDeviceMessage = 33; +// optional RegisterVoiceInputDeviceResponseMessage registerVoiceInputDeviceResponseMessage = 34; +// optional SetRecordingStateMessage setRecordingStateMessage = 35; +// optional SendVoiceInputMessage sendVoiceInputMessage = 36; +// optional GetPlaybackQueueMessage getPlaybackQueueMessage = 37; +// optional TransactionMessage transactionMessage = 38; +// optional CryptoPairingMessage cryptoPairingMessage = 39; +// optional GameControllerPropertiesMessage gameControllerPropertiesMessage = 40; +// optional SetReadyStateMessage setReadyStateMessage = 41; +// optional SendButtonEventMessage sendButtonEventMessage = 43; +// optional SetHiliteModeMessage setHiliteModeMessage = 44; +} diff --git a/dist/lib/protos/RegisterForGameControllerEventsMessage.proto b/dist/lib/protos/RegisterForGameControllerEventsMessage.proto new file mode 100755 index 0000000..9a1544d --- /dev/null +++ b/dist/lib/protos/RegisterForGameControllerEventsMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional RegisterForGameControllerEventsMessage registerForGameControllerEventsMessage = 27; +} + +message RegisterForGameControllerEventsMessage { + optional int32 inputModeFlags = 1; +} diff --git a/dist/lib/protos/RegisterHIDDeviceMessage.proto b/dist/lib/protos/RegisterHIDDeviceMessage.proto new file mode 100755 index 0000000..9b3fb43 --- /dev/null +++ b/dist/lib/protos/RegisterHIDDeviceMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; +import "VirtualTouchDeviceDescriptor.proto"; + +extend ProtocolMessage { + optional RegisterHIDDeviceMessage registerHIDDeviceMessage = 11; +} + +message RegisterHIDDeviceMessage { + optional VirtualTouchDeviceDescriptor deviceDescriptor = 1; +} \ No newline at end of file diff --git a/dist/lib/protos/RegisterHIDDeviceResultMessage.proto b/dist/lib/protos/RegisterHIDDeviceResultMessage.proto new file mode 100755 index 0000000..3a1e283 --- /dev/null +++ b/dist/lib/protos/RegisterHIDDeviceResultMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional RegisterHIDDeviceResultMessage registerHIDDeviceResultMessage = 12; +} + +message RegisterHIDDeviceResultMessage { + optional int32 errorCode = 1; + optional int32 deviceIdentifier = 2; +} \ No newline at end of file diff --git a/dist/lib/protos/RegisterVoiceInputDeviceMessage.proto b/dist/lib/protos/RegisterVoiceInputDeviceMessage.proto new file mode 100755 index 0000000..672de4a --- /dev/null +++ b/dist/lib/protos/RegisterVoiceInputDeviceMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; +import "VoiceInputDeviceDescriptor.proto"; + +extend ProtocolMessage { + optional RegisterVoiceInputDeviceMessage registerVoiceInputDeviceMessage = 33; +} + +message RegisterVoiceInputDeviceMessage { + optional VoiceInputDeviceDescriptor deviceDescriptor = 1; +} \ No newline at end of file diff --git a/dist/lib/protos/RegisterVoiceInputDeviceResponseMessage.proto b/dist/lib/protos/RegisterVoiceInputDeviceResponseMessage.proto new file mode 100755 index 0000000..db37599 --- /dev/null +++ b/dist/lib/protos/RegisterVoiceInputDeviceResponseMessage.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional RegisterVoiceInputDeviceResponseMessage registerVoiceInputDeviceResponseMessage = 34; +} + +message RegisterVoiceInputDeviceResponseMessage { + optional int32 deviceID = 1; + optional int32 errorCode = 2; +} \ No newline at end of file diff --git a/dist/lib/protos/SendButtonEventMessage.proto b/dist/lib/protos/SendButtonEventMessage.proto new file mode 100755 index 0000000..091e401 --- /dev/null +++ b/dist/lib/protos/SendButtonEventMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SendButtonEventMessage sendButtonEventMessage = 43; +} + +message SendButtonEventMessage { + optional uint32 usagePage = 1; + optional uint32 usage = 2; + optional bool buttonDown = 3; +} \ No newline at end of file diff --git a/dist/lib/protos/SendCommandMessage.proto b/dist/lib/protos/SendCommandMessage.proto new file mode 100755 index 0000000..3bd5f8e --- /dev/null +++ b/dist/lib/protos/SendCommandMessage.proto @@ -0,0 +1,16 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; +import "CommandInfo.proto"; +import "CommandOptions.proto"; +import "PlayerPath.proto"; + +extend ProtocolMessage { + optional SendCommandMessage sendCommandMessage = 6; +} + +message SendCommandMessage { + optional Command command = 1; + optional CommandOptions options = 2; + optional PlayerPath playerPath = 3; +} \ No newline at end of file diff --git a/dist/lib/protos/SendHIDEventMessage.proto b/dist/lib/protos/SendHIDEventMessage.proto new file mode 100644 index 0000000..00e09e4 --- /dev/null +++ b/dist/lib/protos/SendHIDEventMessage.proto @@ -0,0 +1,41 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SendHIDEventMessage sendHIDEventMessage = 13; +} + +message SendHIDEventMessage { + // This data corresponds to a "keyboardEvent" in IOHIDEvent.h encoded as raw + // data. Here is one source: + // + // https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-308/IOHIDFamily/IOHIDEvent.h.auto.html + // + // The interesting parts are: + // - usagePage (UInt32) + // - usage (Uint32) + // - down (bool) + // + // The parameters usagePage and usage corresponds to the key being pressed. + // It is mapped to the USB HID values, which can be found here: + // + // https://github.com/Daij-Djan/DDHidLib/blob/master/usb_hid_usages.txt + // + // Pressing left key would for instance map to usagePage=0x01, usage=0x8B. In + // the hid data, these values are stored as big endian uint16 values in the + // mentioned order. So the same example would be: 0x0001008B0001, assuming + // down = true (key being pressed). For each key press, the same usagePage + // and usage are sent with down=true and down=false (key down + key up). + // + // There is a bit of magic in the raw data that's just not decoded yet, but + // that doesn't matter. Just use this and it will work: + // + // 438922cf0802000000000000000000000100000000000000020000002000000003000000010000000000000000000000000001000000 + // + // corresponds to the values above, e.g. 0001008B0001. The first 8 + // bytes is a timestamp (mach AbsoluteTime). It's a bit tricky to derive but + // tvOS seems to accept old timestamps here. So it's probably fine to send + // anything. + optional bytes hidEventData = 1; +} \ No newline at end of file diff --git a/dist/lib/protos/SendPackedVirtualTouchEventMessage.proto b/dist/lib/protos/SendPackedVirtualTouchEventMessage.proto new file mode 100644 index 0000000..cad4e98 --- /dev/null +++ b/dist/lib/protos/SendPackedVirtualTouchEventMessage.proto @@ -0,0 +1,24 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SendPackedVirtualTouchEventMessage sendPackedVirtualTouchEventMessage = 47; +} + +message SendPackedVirtualTouchEventMessage { + + // Corresponds to "phase" in data + enum Phase { + Began = 1; + Moved = 2; + Stationary = 3; + Ended = 4; + Cancelled = 5; + } + + // The packed version of VirtualTouchEvent contains X, Y, phase, deviceID + // and finger stored as a byte array. Each value is written as 16bit little + // endian integers. + optional bytes data = 1; +} \ No newline at end of file diff --git a/dist/lib/protos/SendVirtualTouchEventMessage.proto b/dist/lib/protos/SendVirtualTouchEventMessage.proto new file mode 100755 index 0000000..afb870b --- /dev/null +++ b/dist/lib/protos/SendVirtualTouchEventMessage.proto @@ -0,0 +1,13 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; +import "VirtualTouchEvent.proto"; + +extend ProtocolMessage { + optional SendVirtualTouchEventMessage sendVirtualTouchEventMessage = 15; +} + +message SendVirtualTouchEventMessage { + optional int32 deviceIdentifier = 1; + optional VirtualTouchEvent event = 2; +} \ No newline at end of file diff --git a/dist/lib/protos/SendVoiceInputMessage.proto b/dist/lib/protos/SendVoiceInputMessage.proto new file mode 100644 index 0000000..80d4d3d --- /dev/null +++ b/dist/lib/protos/SendVoiceInputMessage.proto @@ -0,0 +1,24 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; +import "AudioFormatSettings.proto"; +import "AudioBuffer.proto"; + +extend ProtocolMessage { + optional SendVoiceInputMessage sendVoiceInputMessage = 36; +} + +message VoiceInputTime { + required float timestamp = 1; + required float sampleRate = 2; +} + +message VoiceInputDataBlock { + required AudioBuffer buffer = 1; + optional VoiceInputTime time = 2; + optional float gain = 3; +} + +message SendVoiceInputMessage { + required VoiceInputDataBlock dataBlock = 1; +} diff --git a/dist/lib/protos/SetArtworkMessage.proto b/dist/lib/protos/SetArtworkMessage.proto new file mode 100644 index 0000000..fc44283 --- /dev/null +++ b/dist/lib/protos/SetArtworkMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SetArtworkMessage setArtworkMessage = 10; +} + +message SetArtworkMessage { + optional bytes jpegData = 1; +} diff --git a/dist/lib/protos/SetConnectionStateMessage.proto b/dist/lib/protos/SetConnectionStateMessage.proto new file mode 100644 index 0000000..f84e1c0 --- /dev/null +++ b/dist/lib/protos/SetConnectionStateMessage.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SetConnectionStateMessage setConnectionStateMessage = 42; +} + +message SetConnectionStateMessage { + enum ConnectionState { + Connected = 2; + } + + optional ConnectionState state = 1; +} diff --git a/dist/lib/protos/SetDisconnectingStateMessage.proto b/dist/lib/protos/SetDisconnectingStateMessage.proto new file mode 100644 index 0000000..b8d0e32 --- /dev/null +++ b/dist/lib/protos/SetDisconnectingStateMessage.proto @@ -0,0 +1,5 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + + diff --git a/dist/lib/protos/SetHiliteModeMessage.proto b/dist/lib/protos/SetHiliteModeMessage.proto new file mode 100755 index 0000000..355b59d --- /dev/null +++ b/dist/lib/protos/SetHiliteModeMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SetHiliteModeMessage setHiliteModeMessage = 44; +} + +message SetHiliteModeMessage { + optional bool hiliteMode = 1; +} \ No newline at end of file diff --git a/dist/lib/protos/SetRecordingStateMessage.proto b/dist/lib/protos/SetRecordingStateMessage.proto new file mode 100644 index 0000000..1d60ab5 --- /dev/null +++ b/dist/lib/protos/SetRecordingStateMessage.proto @@ -0,0 +1,16 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional SetRecordingStateMessage setRecordingStateMessage = 35; +} + +message SetRecordingStateMessage { + enum RecordingState { + Recording = 1; + NotRecording = 2; + } + + required RecordingState state = 1; +} \ No newline at end of file diff --git a/dist/lib/protos/SetStateMessage.proto b/dist/lib/protos/SetStateMessage.proto new file mode 100755 index 0000000..38a9f4c --- /dev/null +++ b/dist/lib/protos/SetStateMessage.proto @@ -0,0 +1,23 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; +import "NowPlayingInfo.proto"; +import "SupportedCommands.proto"; +import "PlayerPath.proto"; +import "PlaybackQueue.proto"; + +extend ProtocolMessage { + optional SetStateMessage setStateMessage = 9; +} + +message SetStateMessage { + optional NowPlayingInfo nowPlayingInfo = 1; + optional SupportedCommands supportedCommands = 2; + optional PlaybackQueue playbackQueue = 3; + optional string displayID = 4; + optional string displayName = 5; + optional uint32 playbackState = 6; +// optional PlaybackQueueCapabilities playbackQueueCapabilities = 8; + optional PlayerPath playerPath = 9; +// optional Request request = 10; +} \ No newline at end of file diff --git a/dist/lib/protos/SupportedCommands.proto b/dist/lib/protos/SupportedCommands.proto new file mode 100755 index 0000000..ea4f117 --- /dev/null +++ b/dist/lib/protos/SupportedCommands.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; + +import "CommandInfo.proto"; + +message SupportedCommands { + repeated CommandInfo supportedCommands = 1; +} \ No newline at end of file diff --git a/dist/lib/protos/TextEditingAttributes.proto b/dist/lib/protos/TextEditingAttributes.proto new file mode 100755 index 0000000..07ec343 --- /dev/null +++ b/dist/lib/protos/TextEditingAttributes.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; + +import "TextInputTraits.proto"; + +message TextEditingAttributes { + optional string title = 1; + optional string prompt = 2; + optional TextInputTraits inputTraits = 3; +} \ No newline at end of file diff --git a/dist/lib/protos/TextInputTraits.proto b/dist/lib/protos/TextInputTraits.proto new file mode 100755 index 0000000..08eeabe --- /dev/null +++ b/dist/lib/protos/TextInputTraits.proto @@ -0,0 +1,51 @@ +syntax = "proto2"; + +message TextInputTraits { + enum AutocapitalizationType { + NONE = 0; + WORDS = 1; + SENTENCES = 2; + CHARACTERS = 3; + } + + enum KeyboardType { + KEYBOARD_TYPE_DEFAULT = 0; + ASCII_CAPABLE = 1; + NUMBERS_AND_PUNCTUATION = 2; + URL = 3; + NUMBER_PAD = 4; + PHONE_PAD = 5; + NAME_PHONE_PAD = 6; + EMAIL_ADDRESS = 7; + DECIMAL_PAD = 8; + TWITTER = 9; + WEB_SEARCH = 10; + // ALPHABET = 1; + } + + enum ReturnKeyType { + RETURN_KEY_DEFAULT = 0; + GO = 1; + GOOGLE = 2; + JOIN = 3; + NEXT = 4; + ROUTE = 5; + SEARCH = 6; + SEND = 7; + YAHOO = 8; + DONE = 9; + EMERGENCY_CALL = 10; + CONTINUE = 11; + } + +// optional AutocapitalizationType autocapitalizationType = ?; +// optional bool autocorrection = ?; +// repeated int64 PINEntrySeparatorIndexes = ?; +// optional bool enablesReturnKeyAutomatically = ?; +// optional KeyboardType keyboardType = ?; +// optional ReturnKeyType returnKeyType = ?; +// optional bool secureTextEntry = ?; +// optional bool spellchecking = ?; +// optional int32 validTextRangeLength = ?; +// optional int32 validTextRangeLocation = ?; +} \ No newline at end of file diff --git a/dist/lib/protos/TransactionKey.proto b/dist/lib/protos/TransactionKey.proto new file mode 100755 index 0000000..d9a0f6d --- /dev/null +++ b/dist/lib/protos/TransactionKey.proto @@ -0,0 +1,6 @@ +syntax = "proto2"; + +message TransactionKey { + optional string identifier = 1; + optional bytes userData = 2; +} \ No newline at end of file diff --git a/dist/lib/protos/TransactionMessage.proto b/dist/lib/protos/TransactionMessage.proto new file mode 100755 index 0000000..87df06c --- /dev/null +++ b/dist/lib/protos/TransactionMessage.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +import "TransactionPackets.proto"; +import "ProtocolMessage.proto"; +import "PlayerPath.proto"; + +extend ProtocolMessage { + optional TransactionMessage transactionMessage = 38; +} + +message TransactionMessage { + optional uint64 name = 1; + optional TransactionPackets packets = 2; + optional PlayerPath playerPath = 3; +} \ No newline at end of file diff --git a/dist/lib/protos/TransactionPacket.proto b/dist/lib/protos/TransactionPacket.proto new file mode 100755 index 0000000..1630e84 --- /dev/null +++ b/dist/lib/protos/TransactionPacket.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "TransactionKey.proto"; + +message TransactionPacket { + optional TransactionKey key = 1; + optional bytes packetData = 2; + optional string identifier = 3; + optional uint64 totalLength = 4; + optional uint64 totalWritePosition = 5; +} \ No newline at end of file diff --git a/dist/lib/protos/TransactionPackets.proto b/dist/lib/protos/TransactionPackets.proto new file mode 100755 index 0000000..105eef2 --- /dev/null +++ b/dist/lib/protos/TransactionPackets.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; + +import "TransactionPacket.proto"; + +message TransactionPackets { + repeated TransactionPacket packets = 1; +} \ No newline at end of file diff --git a/dist/lib/protos/VirtualTouchDeviceDescriptor.proto b/dist/lib/protos/VirtualTouchDeviceDescriptor.proto new file mode 100755 index 0000000..b7b0555 --- /dev/null +++ b/dist/lib/protos/VirtualTouchDeviceDescriptor.proto @@ -0,0 +1,8 @@ +syntax = "proto2"; + +message VirtualTouchDeviceDescriptor { + optional bool absolute = 1; + optional bool integratedDisplay = 2; + optional float screenSizeWidth = 3; + optional float screenSizeHeight = 4; +} \ No newline at end of file diff --git a/dist/lib/protos/VirtualTouchEvent.proto b/dist/lib/protos/VirtualTouchEvent.proto new file mode 100755 index 0000000..208df9d --- /dev/null +++ b/dist/lib/protos/VirtualTouchEvent.proto @@ -0,0 +1,8 @@ +syntax = "proto2"; + +message VirtualTouchEvent { + optional double x = 1; + optional double y = 2; + optional int32 phase = 3; + optional int32 finger = 4; +} \ No newline at end of file diff --git a/dist/lib/protos/VoiceInputDeviceDescriptor.proto b/dist/lib/protos/VoiceInputDeviceDescriptor.proto new file mode 100755 index 0000000..4d8b56d --- /dev/null +++ b/dist/lib/protos/VoiceInputDeviceDescriptor.proto @@ -0,0 +1,8 @@ +syntax = "proto2"; + +import "AudioFormatSettings.proto"; + +message VoiceInputDeviceDescriptor { + optional AudioFormatSettings defaultFormat = 1; + repeated AudioFormatSettings supportedFormats = 2; +} \ No newline at end of file diff --git a/dist/lib/protos/VolumeControlAvailabilityMessage.proto b/dist/lib/protos/VolumeControlAvailabilityMessage.proto new file mode 100644 index 0000000..7072c26 --- /dev/null +++ b/dist/lib/protos/VolumeControlAvailabilityMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional VolumeControlAvailabilityMessage volumeControlAvailabilityMessage = 22; +} + +message VolumeControlAvailabilityMessage { + optional bool volumeControlAvailable = 1; +} \ No newline at end of file diff --git a/dist/lib/protos/WakeDeviceMessage.proto b/dist/lib/protos/WakeDeviceMessage.proto new file mode 100644 index 0000000..15badf9 --- /dev/null +++ b/dist/lib/protos/WakeDeviceMessage.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +import "ProtocolMessage.proto"; + +extend ProtocolMessage { + optional WakeDeviceMessage wakeDeviceMessage = 45; +} + +message WakeDeviceMessage { + +} diff --git a/dist/lib/supported-command.d.ts b/dist/lib/supported-command.d.ts new file mode 100644 index 0000000..ca03fb7 --- /dev/null +++ b/dist/lib/supported-command.d.ts @@ -0,0 +1,24 @@ +export declare class SupportedCommand { + command: keyof typeof SupportedCommand.Command; + enabled: boolean; + canScrub: boolean; + constructor(command: keyof typeof SupportedCommand.Command, enabled: boolean, canScrub: boolean); +} +export declare module SupportedCommand { + enum Command { + Play = "Play", + Pause = "Pause", + TogglePlayPause = "TogglePlayPause", + EnableLanguageOption = "EnableLanguageOption", + DisableLanguageOption = "DisableLanguageOption", + Stop = "Stop", + SkipForward = "SkipForward", + SkipBackward = "SkipBackward", + BeginFastForward = "BeginFastForward", + BeginRewind = "BeginRewind", + ChangePlaybackRate = "ChangePlaybackRate", + SeekToPlaybackPosition = "SeekToPlaybackPosition", + NextInContext = "NextInContext", + PreviousInContext = "PreviousInContext" + } +} diff --git a/dist/lib/supported-command.js b/dist/lib/supported-command.js new file mode 100644 index 0000000..1086ca3 --- /dev/null +++ b/dist/lib/supported-command.js @@ -0,0 +1,29 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class SupportedCommand { + constructor(command, enabled, canScrub) { + this.command = command; + this.enabled = enabled; + this.canScrub = canScrub; + } +} +exports.SupportedCommand = SupportedCommand; +(function (SupportedCommand) { + let Command; + (function (Command) { + Command["Play"] = "Play"; + Command["Pause"] = "Pause"; + Command["TogglePlayPause"] = "TogglePlayPause"; + Command["EnableLanguageOption"] = "EnableLanguageOption"; + Command["DisableLanguageOption"] = "DisableLanguageOption"; + Command["Stop"] = "Stop"; + Command["SkipForward"] = "SkipForward"; + Command["SkipBackward"] = "SkipBackward"; + Command["BeginFastForward"] = "BeginFastForward"; + Command["BeginRewind"] = "BeginRewind"; + Command["ChangePlaybackRate"] = "ChangePlaybackRate"; + Command["SeekToPlaybackPosition"] = "SeekToPlaybackPosition"; + Command["NextInContext"] = "NextInContext"; + Command["PreviousInContext"] = "PreviousInContext"; + })(Command = SupportedCommand.Command || (SupportedCommand.Command = {})); +})(SupportedCommand = exports.SupportedCommand || (exports.SupportedCommand = {})); diff --git a/dist/lib/typed-events.d.ts b/dist/lib/typed-events.d.ts new file mode 100644 index 0000000..cc90ccb --- /dev/null +++ b/dist/lib/typed-events.d.ts @@ -0,0 +1,8 @@ +/// +import { EventEmitter } from 'events'; +export default class TypedEventEmitter extends EventEmitter { + constructor(...args: any[]); + _on(event: string, callback: (...args: any[]) => void): this; + on(event: string & keyof T, callback: (arg: T[typeof event]) => void): this; + emit(event: string & keyof T, payload?: T[typeof event]): boolean; +} diff --git a/dist/lib/typed-events.js b/dist/lib/typed-events.js new file mode 100644 index 0000000..f83b2a6 --- /dev/null +++ b/dist/lib/typed-events.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const events_1 = require("events"); +class TypedEventEmitter extends events_1.EventEmitter { + constructor(...args) { + super(); + } + _on(event, callback) { + return super.on(event, callback); + } + on(event, callback) { + return super.on(event, callback); + } + emit(event, payload) { + return super.emit(event, payload); + } +} +exports.default = TypedEventEmitter; diff --git a/dist/lib/util/encryption.d.ts b/dist/lib/util/encryption.d.ts new file mode 100644 index 0000000..ac23455 --- /dev/null +++ b/dist/lib/util/encryption.d.ts @@ -0,0 +1,10 @@ +/// +declare function verifyAndDecrypt(cipherText: Buffer, mac: Buffer, AAD: Buffer, nonce: Buffer, key: Buffer): Buffer; +declare function encryptAndSeal(plainText: Buffer, AAD: Buffer, nonce: Buffer, key: Buffer): Buffer[]; +declare function HKDF(hashAlg: string, salt: Buffer, ikm: Buffer, info: Buffer, size: number): Buffer; +declare const _default: { + encryptAndSeal: typeof encryptAndSeal; + verifyAndDecrypt: typeof verifyAndDecrypt; + HKDF: typeof HKDF; +}; +export default _default; diff --git a/dist/lib/util/encryption.js b/dist/lib/util/encryption.js new file mode 100644 index 0000000..b3612e2 --- /dev/null +++ b/dist/lib/util/encryption.js @@ -0,0 +1,80 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const sodium_1 = require("sodium"); +const crypto = require("crypto"); +const number_1 = require("./number"); +function computePoly1305(cipherText, AAD, nonce, key) { + if (AAD == null) { + AAD = Buffer.alloc(0); + } + const msg = Buffer.concat([ + AAD, + getPadding(AAD, 16), + cipherText, + getPadding(cipherText, 16), + number_1.default.UInt53toBufferLE(AAD.length), + number_1.default.UInt53toBufferLE(cipherText.length) + ]); + const polyKey = sodium_1.api.crypto_stream_chacha20(32, nonce, key); + const computed_hmac = sodium_1.api.crypto_onetimeauth(msg, polyKey); + polyKey.fill(0); + return computed_hmac; +} +// i'd really prefer for this to be a direct call to +// Sodium.crypto_aead_chacha20poly1305_decrypt() +// but unfortunately the way it constructs the message to +// calculate the HMAC is not compatible with homekit +// (long story short, it uses [ AAD, AAD.length, CipherText, CipherText.length ] +// whereas homekit expects [ AAD, CipherText, AAD.length, CipherText.length ] +function verifyAndDecrypt(cipherText, mac, AAD, nonce, key) { + const matches = sodium_1.api.crypto_verify_16(mac, computePoly1305(cipherText, AAD, nonce, key)); + if (matches === 0) { + return sodium_1.api + .crypto_stream_chacha20_xor_ic(cipherText, nonce, 1, key); + } + return null; +} +// See above about calling directly into libsodium. +function encryptAndSeal(plainText, AAD, nonce, key) { + const cipherText = sodium_1.api + .crypto_stream_chacha20_xor_ic(plainText, nonce, 1, key); + const hmac = computePoly1305(cipherText, AAD, nonce, key); + return [cipherText, hmac]; +} +function getPadding(buffer, blockSize) { + return buffer.length % blockSize === 0 + ? Buffer.alloc(0) + : Buffer.alloc(blockSize - (buffer.length % blockSize)); +} +function HKDF(hashAlg, salt, ikm, info, size) { + // create the hash alg to see if it exists and get its length + var hash = crypto.createHash(hashAlg); + var hashLength = hash.digest().length; + // now we compute the PRK + var hmac = crypto.createHmac(hashAlg, salt); + hmac.update(ikm); + var prk = hmac.digest(); + var prev = Buffer.alloc(0); + var output; + var buffers = []; + var num_blocks = Math.ceil(size / hashLength); + info = Buffer.from(info); + for (var i = 0; i < num_blocks; i++) { + var hmac = crypto.createHmac(hashAlg, prk); + var input = Buffer.concat([ + prev, + info, + Buffer.from(String.fromCharCode(i + 1)) + ]); + hmac.update(input); + prev = hmac.digest(); + buffers.push(prev); + } + output = Buffer.concat(buffers, size); + return output.slice(0, size); +} +exports.default = { + encryptAndSeal, + verifyAndDecrypt, + HKDF +}; diff --git a/dist/lib/util/number.d.ts b/dist/lib/util/number.d.ts new file mode 100644 index 0000000..9617f29 --- /dev/null +++ b/dist/lib/util/number.d.ts @@ -0,0 +1,8 @@ +/// +declare function UInt53toBufferLE(number: number): Buffer; +declare function UInt16toBufferBE(number: number): Buffer; +declare const _default: { + UInt53toBufferLE: typeof UInt53toBufferLE; + UInt16toBufferBE: typeof UInt16toBufferBE; +}; +export default _default; diff --git a/dist/lib/util/number.js b/dist/lib/util/number.js new file mode 100644 index 0000000..1fc6317 --- /dev/null +++ b/dist/lib/util/number.js @@ -0,0 +1,44 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const assert = require("assert"); +/* + * Originally based on code from github:KhaosT/HAP-NodeJS@0c8fd88 used + * used per the terms of the Apache Software License v2. + * + * Original code copyright Khaos Tian + * + * Modifications copyright Zach Bean + * * Reformatted for ES6-style module + * * renamed *UInt64* to *UInt53* to be more clear about range + * * renamed uintHighLow to be more clear about what it does + * * Refactored to return a buffer rather write into a passed-in buffer + */ +function splitUInt53(number) { + const MAX_UINT32 = 0x00000000FFFFFFFF; + const MAX_INT53 = 0x001FFFFFFFFFFFFF; + assert(number > -1 && number <= MAX_INT53, "number out of range"); + assert(Math.floor(number) === number, "number must be an integer"); + var high = 0; + var signbit = number & 0xFFFFFFFF; + var low = signbit < 0 ? (number & 0x7FFFFFFF) + 0x80000000 : signbit; + if (number > MAX_UINT32) { + high = (number - low) / (MAX_UINT32 + 1); + } + return [high, low]; +} +function UInt53toBufferLE(number) { + const [high, low] = splitUInt53(number); + const buf = Buffer.alloc(8); + buf.writeUInt32LE(low, 0); + buf.writeUInt32LE(high, 4); + return buf; +} +function UInt16toBufferBE(number) { + const buf = Buffer.alloc(2); + buf.writeUInt16BE(number, 0); + return buf; +} +exports.default = { + UInt53toBufferLE, + UInt16toBufferBE +}; diff --git a/dist/lib/util/tlv.d.ts b/dist/lib/util/tlv.d.ts new file mode 100644 index 0000000..96d96ba --- /dev/null +++ b/dist/lib/util/tlv.d.ts @@ -0,0 +1,36 @@ +/** + * Type Length Value encoding/decoding, used by HAP as a wire format. + * https://en.wikipedia.org/wiki/Type-length-value + * + * Originally based on code from github:KhaosT/HAP-NodeJS@0c8fd88 used + * used per the terms of the Apache Software License v2. + * + * Original code copyright Khaos Tian + * + * Modifications copyright Zach Bean + * * Reformatted for ES6-style module + * * Rewrote encode() to be non-recursive; also simplified the logic + * * Rewrote decode() + */ +/// +declare function encode(type: any, data: any, ...args: any[]): Buffer; +declare function decode(data: any): {}; +declare const _default: { + Tag: { + PairingMethod: number; + Username: number; + Salt: number; + PublicKey: number; + Proof: number; + EncryptedData: number; + Sequence: number; + ErrorCode: number; + BackOff: number; + Signature: number; + MFiCertificate: number; + MFiSignature: number; + }; + encode: typeof encode; + decode: typeof decode; +}; +export default _default; diff --git a/dist/lib/util/tlv.js b/dist/lib/util/tlv.js new file mode 100644 index 0000000..7908eba --- /dev/null +++ b/dist/lib/util/tlv.js @@ -0,0 +1,94 @@ +"use strict"; +/** + * Type Length Value encoding/decoding, used by HAP as a wire format. + * https://en.wikipedia.org/wiki/Type-length-value + * + * Originally based on code from github:KhaosT/HAP-NodeJS@0c8fd88 used + * used per the terms of the Apache Software License v2. + * + * Original code copyright Khaos Tian + * + * Modifications copyright Zach Bean + * * Reformatted for ES6-style module + * * Rewrote encode() to be non-recursive; also simplified the logic + * * Rewrote decode() + */ +Object.defineProperty(exports, "__esModule", { value: true }); +const Tag = { + PairingMethod: 0x00, + Username: 0x01, + Salt: 0x02, + // could be either the SRP client public key (384 bytes) or the ED25519 public key (32 bytes), depending on context + PublicKey: 0x03, + Proof: 0x04, + EncryptedData: 0x05, + Sequence: 0x06, + ErrorCode: 0x07, + BackOff: 0x08, + Signature: 0x0A, + MFiCertificate: 0x09, + MFiSignature: 0x0A +}; +function encode(type, data, ...args) { + var encodedTLVBuffer = Buffer.alloc(0); + // coerce data to Buffer if needed + if (typeof data === 'number') + data = Buffer.from([data]); + else if (typeof data === 'string') + data = Buffer.from(data); + if (data.length <= 255) { + encodedTLVBuffer = Buffer.concat([Buffer.from([type, data.length]), data]); + } + else { + var leftLength = data.length; + var tempBuffer = Buffer.alloc(0); + var currentStart = 0; + for (; leftLength > 0;) { + if (leftLength >= 255) { + tempBuffer = Buffer.concat([tempBuffer, Buffer.from([type, 0xFF]), data.slice(currentStart, currentStart + 255)]); + leftLength -= 255; + currentStart = currentStart + 255; + } + else { + tempBuffer = Buffer.concat([tempBuffer, Buffer.from([type, leftLength]), data.slice(currentStart, currentStart + leftLength)]); + leftLength -= leftLength; + } + } + encodedTLVBuffer = tempBuffer; + } + // do we have more to encode? + if (arguments.length > 2) { + // chop off the first two arguments which we already processed, and process the rest recursively + var remainingArguments = Array.prototype.slice.call(arguments, 2); + var remainingTLVBuffer = encode.apply(this, remainingArguments); + // append the remaining encoded arguments directly to the buffer + encodedTLVBuffer = Buffer.concat([encodedTLVBuffer, remainingTLVBuffer]); + } + return encodedTLVBuffer; +} +function decode(data) { + var objects = {}; + var leftLength = data.length; + var currentIndex = 0; + for (; leftLength > 0;) { + var type = data[currentIndex]; + var length = data[currentIndex + 1]; + currentIndex += 2; + leftLength -= 2; + var newData = data.slice(currentIndex, currentIndex + length); + if (objects[type]) { + objects[type] = Buffer.concat([objects[type], newData]); + } + else { + objects[type] = newData; + } + currentIndex += length; + leftLength -= length; + } + return objects; +} +exports.default = { + Tag, + encode, + decode +}; diff --git a/dist/lib/verifier.d.ts b/dist/lib/verifier.d.ts new file mode 100644 index 0000000..a25e165 --- /dev/null +++ b/dist/lib/verifier.d.ts @@ -0,0 +1,7 @@ +import { AppleTV } from './appletv'; +export declare class Verifier { + device: AppleTV; + constructor(device: AppleTV); + verify(): Promise<{}>; + private waitForSequence; +} diff --git a/dist/lib/verifier.js b/dist/lib/verifier.js new file mode 100644 index 0000000..4b95670 --- /dev/null +++ b/dist/lib/verifier.js @@ -0,0 +1,105 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const ed25519 = require("ed25519-wasm-pro"); +const curve25519 = require("curve25519-n"); +const message_1 = require("./message"); +const tlv_1 = require("./util/tlv"); +const encryption_1 = require("./util/encryption"); +class Verifier { + constructor(device) { + this.device = device; + } + verify() { + var verifyPrivate = Buffer.alloc(32); + curve25519.makeSecretKey(verifyPrivate); + let verifyPublic = curve25519.derivePublicKey(verifyPrivate); + let that = this; + let tlvData = tlv_1.default.encode(tlv_1.default.Tag.Sequence, 0x01, tlv_1.default.Tag.PublicKey, verifyPublic); + let message = { + status: 0, + state: 3, + isRetrying: false, + isUsingSystemPairing: false, + pairingData: tlvData + }; + return that.device + .sendMessage('CryptoPairingMessage', 'CryptoPairingMessage', message, false) + .then(() => { + return that.waitForSequence(0x02); + }) + .then(message => { + let pairingData = message.payload.pairingData; + let tlvData = tlv_1.default.decode(pairingData); + let sessionPublicKey = tlvData[tlv_1.default.Tag.PublicKey]; + let encryptedData = tlvData[tlv_1.default.Tag.EncryptedData]; + if (sessionPublicKey.length != 32) { + throw new Error(`sessionPublicKey must be 32 bytes (but was ${sessionPublicKey.length})`); + } + let sharedSecret = curve25519.deriveSharedSecret(verifyPrivate, sessionPublicKey); + let encryptionKey = encryption_1.default.HKDF("sha512", Buffer.from("Pair-Verify-Encrypt-Salt"), sharedSecret, Buffer.from("Pair-Verify-Encrypt-Info"), 32); + let cipherText = encryptedData.slice(0, -16); + let hmac = encryptedData.slice(-16); + let decryptedData = encryption_1.default.verifyAndDecrypt(cipherText, hmac, null, Buffer.from('PV-Msg02'), encryptionKey); + let innerTLV = tlv_1.default.decode(decryptedData); + let identifier = innerTLV[tlv_1.default.Tag.Username]; + let signature = innerTLV[tlv_1.default.Tag.Signature]; + if (!identifier.equals(that.device.credentials.identifier)) { + throw new Error("Identifier mismatch"); + } + let deviceInfo = Buffer.concat([sessionPublicKey, Buffer.from(identifier), verifyPublic]); + if (!ed25519.verify(signature, deviceInfo, that.device.credentials.publicKey)) { + throw new Error("Signature verification failed"); + } + let material = Buffer.concat([verifyPublic, Buffer.from(that.device.credentials.pairingId), sessionPublicKey]); + let keyPair = ed25519.createKeyPair(that.device.credentials.encryptionKey); + let signed = ed25519.sign(material, keyPair.publicKey, keyPair.secretKey); + let plainTLV = tlv_1.default.encode(tlv_1.default.Tag.Username, Buffer.from(that.device.credentials.pairingId), tlv_1.default.Tag.Signature, signed); + let encryptedTLV = Buffer.concat(encryption_1.default.encryptAndSeal(plainTLV, null, Buffer.from('PV-Msg03'), encryptionKey)); + let outerTLV = tlv_1.default.encode(tlv_1.default.Tag.Sequence, 0x03, tlv_1.default.Tag.EncryptedData, encryptedTLV); + let newMessage = { + status: 0, + state: 3, + isRetrying: false, + isUsingSystemPairing: true, + pairingData: outerTLV + }; + return that.device + .sendMessage('CryptoPairingMessage', 'CryptoPairingMessage', newMessage, false) + .then(() => { + return that.waitForSequence(0x04); + }) + .then(() => { + let readKey = encryption_1.default.HKDF("sha512", Buffer.from("MediaRemote-Salt"), sharedSecret, Buffer.from("MediaRemote-Read-Encryption-Key"), 32); + let writeKey = encryption_1.default.HKDF("sha512", Buffer.from("MediaRemote-Salt"), sharedSecret, Buffer.from("MediaRemote-Write-Encryption-Key"), 32); + return { + readKey: readKey, + writeKey: writeKey + }; + }); + }); + } + waitForSequence(sequence, timeout = 3) { + let that = this; + let handler = (message, resolve) => { + let tlvData = tlv_1.default.decode(message.payload.pairingData); + if (Buffer.from([sequence]).equals(tlvData[tlv_1.default.Tag.Sequence])) { + resolve(message); + } + }; + return new Promise((resolve, reject) => { + that.device.on('message', (message) => { + if (message.type == message_1.Message.Type.CryptoPairingMessage) { + handler(message, resolve); + } + }); + setTimeout(() => { + reject(new Error("Timed out waiting for crypto sequence " + sequence)); + }, timeout * 1000); + }) + .then(value => { + that.device.removeListener('message', handler); + return value; + }); + } +} +exports.Verifier = Verifier; diff --git a/docs/assets/css/main.css b/docs/assets/css/main.css new file mode 100644 index 0000000..48b3645 --- /dev/null +++ b/docs/assets/css/main.css @@ -0,0 +1,865 @@ +/*! normalize.css v1.1.3 | MIT License | git.io/normalize */ +/* ========================================================================== HTML5 display definitions ========================================================================== */ +/** Correct `block` display not defined in IE 6/7/8/9 and Firefox 3. */ +article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; } + +/** Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. */ +audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; } + +/** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */ +audio:not([controls]) { display: none; height: 0; } + +/** Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. Known issue: no IE 6 support. */ +[hidden] { display: none; } + +/* ========================================================================== Base ========================================================================== */ +/** 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using `em` units. 2. Prevent iOS text size adjust after orientation change, without disabling user zoom. */ +html { font-size: 100%; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ font-family: sans-serif; } + +/** Address `font-family` inconsistency between `textarea` and other form elements. */ +button, input, select, textarea { font-family: sans-serif; } + +/** Address margins handled incorrectly in IE 6/7. */ +body { margin: 0; } + +/* ========================================================================== Links ========================================================================== */ +/** Address `outline` inconsistency between Chrome and other browsers. */ +a:focus { outline: thin dotted; } +a:active, a:hover { outline: 0; } + +/** Improve readability when focused and also mouse hovered in all browsers. */ +/* ========================================================================== Typography ========================================================================== */ +/** Address font sizes and margins set differently in IE 6/7. Address font sizes within `section` and `article` in Firefox 4+, Safari 5, and Chrome. */ +h1 { font-size: 2em; margin: 0.67em 0; } + +h2 { font-size: 1.5em; margin: 0.83em 0; } + +h3 { font-size: 1.17em; margin: 1em 0; } + +h4, .tsd-index-panel h3 { font-size: 1em; margin: 1.33em 0; } + +h5 { font-size: 0.83em; margin: 1.67em 0; } + +h6 { font-size: 0.67em; margin: 2.33em 0; } + +/** Address styling not present in IE 7/8/9, Safari 5, and Chrome. */ +abbr[title] { border-bottom: 1px dotted; } + +/** Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. */ +b, strong { font-weight: bold; } + +blockquote { margin: 1em 40px; } + +/** Address styling not present in Safari 5 and Chrome. */ +dfn { font-style: italic; } + +/** Address differences between Firefox and other browsers. Known issue: no IE 6/7 normalization. */ +hr { box-sizing: content-box; height: 0; } + +/** Address styling not present in IE 6/7/8/9. */ +mark { background: #ff0; color: #000; } + +/** Address margins set differently in IE 6/7. */ +p, pre { margin: 1em 0; } + +/** Correct font family set oddly in IE 6, Safari 4/5, and Chrome. */ +code, kbd, pre, samp { font-family: monospace, serif; _font-family: "courier new", monospace; font-size: 1em; } + +/** Improve readability of pre-formatted text in all browsers. */ +pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; } + +/** Address CSS quotes not supported in IE 6/7. */ +q { quotes: none; } +q:before, q:after { content: ""; content: none; } + +/** Address `quotes` property not supported in Safari 4. */ +/** Address inconsistent and variable font size in all browsers. */ +small { font-size: 80%; } + +/** Prevent `sub` and `sup` affecting `line-height` in all browsers. */ +sub { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } + +sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; top: -0.5em; } + +sub { bottom: -0.25em; } + +/* ========================================================================== Lists ========================================================================== */ +/** Address margins set differently in IE 6/7. */ +dl, menu, ol, ul { margin: 1em 0; } + +dd { margin: 0 0 0 40px; } + +/** Address paddings set differently in IE 6/7. */ +menu, ol, ul { padding: 0 0 0 40px; } + +/** Correct list images handled incorrectly in IE 7. */ +nav ul, nav ol { list-style: none; list-style-image: none; } + +/* ========================================================================== Embedded content ========================================================================== */ +/** 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. 2. Improve image quality when scaled in IE 7. */ +img { border: 0; /* 1 */ -ms-interpolation-mode: bicubic; } + +/* 2 */ +/** Correct overflow displayed oddly in IE 9. */ +svg:not(:root) { overflow: hidden; } + +/* ========================================================================== Figures ========================================================================== */ +/** Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. */ +figure, form { margin: 0; } + +/* ========================================================================== Forms ========================================================================== */ +/** Correct margin displayed oddly in IE 6/7. */ +/** Define consistent border, margin, and padding. */ +fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } + +/** 1. Correct color not being inherited in IE 6/7/8/9. 2. Correct text not wrapping in Firefox 3. 3. Correct alignment displayed oddly in IE 6/7. */ +legend { border: 0; /* 1 */ padding: 0; white-space: normal; /* 2 */ *margin-left: -7px; } + +/* 3 */ +/** 1. Correct font size not being inherited in all browsers. 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, and Chrome. 3. Improve appearance and consistency in all browsers. */ +button, input, select, textarea { font-size: 100%; /* 1 */ margin: 0; /* 2 */ vertical-align: baseline; /* 3 */ *vertical-align: middle; } + +/* 3 */ +/** Address Firefox 3+ setting `line-height` on `input` using `!important` in the UA stylesheet. */ +button, input { line-height: normal; } + +/** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. Correct `select` style inheritance in Firefox 4+ and Opera. */ +button, select { text-transform: none; } + +/** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. 4. Remove inner spacing in IE 7 without affecting normal text inputs. Known issue: inner spacing remains in IE 6. */ +button, html input[type="button"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ *overflow: visible; } + +/* 4 */ +input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ *overflow: visible; } + +/* 4 */ +/** Re-set default cursor for disabled elements. */ +button[disabled], html input[disabled] { cursor: default; } + +/** 1. Address box sizing set to content-box in IE 8/9. 2. Remove excess padding in IE 8/9. 3. Remove excess padding in IE 7. Known issue: excess padding remains in IE 6. */ +input { /* 3 */ } +input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ *height: 13px; /* 3 */ *width: 13px; } +input[type="search"] { -webkit-appearance: textfield; /* 1 */ /* 2 */ box-sizing: content-box; } +input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } + +/** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */ +/** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */ +/** Remove inner padding and border in Firefox 3+. */ +button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } + +/** 1. Remove default vertical scrollbar in IE 6/7/8/9. 2. Improve readability and alignment in all browsers. */ +textarea { overflow: auto; /* 1 */ vertical-align: top; } + +/* 2 */ +/* ========================================================================== Tables ========================================================================== */ +/** Remove most spacing between table cells. */ +table { border-collapse: collapse; border-spacing: 0; } + +/* Visual Studio-like style based on original C# coloring by Jason Diamond */ +.hljs { display: inline-block; padding: 0.5em; background: white; color: black; } + +.hljs-comment, .hljs-annotation, .hljs-template_comment, .diff .hljs-header, .hljs-chunk, .apache .hljs-cbracket { color: #008000; } + +.hljs-keyword, .hljs-id, .hljs-built_in, .css .smalltalk .hljs-class, .hljs-winutils, .bash .hljs-variable, .tex .hljs-command, .hljs-request, .hljs-status, .nginx .hljs-title { color: #00f; } + +.xml .hljs-tag { color: #00f; } +.xml .hljs-tag .hljs-value { color: #00f; } + +.hljs-string, .hljs-title, .hljs-parent, .hljs-tag .hljs-value, .hljs-rules .hljs-value { color: #a31515; } + +.ruby .hljs-symbol { color: #a31515; } +.ruby .hljs-symbol .hljs-string { color: #a31515; } + +.hljs-template_tag, .django .hljs-variable, .hljs-addition, .hljs-flow, .hljs-stream, .apache .hljs-tag, .hljs-date, .tex .hljs-formula, .coffeescript .hljs-attribute { color: #a31515; } + +.ruby .hljs-string, .hljs-decorator, .hljs-filter .hljs-argument, .hljs-localvars, .hljs-array, .hljs-attr_selector, .hljs-pseudo, .hljs-pi, .hljs-doctype, .hljs-deletion, .hljs-envvar, .hljs-shebang, .hljs-preprocessor, .hljs-pragma, .userType, .apache .hljs-sqbracket, .nginx .hljs-built_in, .tex .hljs-special, .hljs-prompt { color: #2b91af; } + +.hljs-phpdoc, .hljs-javadoc, .hljs-xmlDocTag { color: #808080; } + +.vhdl .hljs-typename { font-weight: bold; } +.vhdl .hljs-string { color: #666666; } +.vhdl .hljs-literal { color: #a31515; } +.vhdl .hljs-attribute { color: #00b0e8; } + +.xml .hljs-attribute { color: #f00; } + +.col > :first-child, .col-1 > :first-child, .col-2 > :first-child, .col-3 > :first-child, .col-4 > :first-child, .col-5 > :first-child, .col-6 > :first-child, .col-7 > :first-child, .col-8 > :first-child, .col-9 > :first-child, .col-10 > :first-child, .col-11 > :first-child, .tsd-panel > :first-child, ul.tsd-descriptions > li > :first-child, .col > :first-child > :first-child, .col-1 > :first-child > :first-child, .col-2 > :first-child > :first-child, .col-3 > :first-child > :first-child, .col-4 > :first-child > :first-child, .col-5 > :first-child > :first-child, .col-6 > :first-child > :first-child, .col-7 > :first-child > :first-child, .col-8 > :first-child > :first-child, .col-9 > :first-child > :first-child, .col-10 > :first-child > :first-child, .col-11 > :first-child > :first-child, .tsd-panel > :first-child > :first-child, ul.tsd-descriptions > li > :first-child > :first-child, .col > :first-child > :first-child > :first-child, .col-1 > :first-child > :first-child > :first-child, .col-2 > :first-child > :first-child > :first-child, .col-3 > :first-child > :first-child > :first-child, .col-4 > :first-child > :first-child > :first-child, .col-5 > :first-child > :first-child > :first-child, .col-6 > :first-child > :first-child > :first-child, .col-7 > :first-child > :first-child > :first-child, .col-8 > :first-child > :first-child > :first-child, .col-9 > :first-child > :first-child > :first-child, .col-10 > :first-child > :first-child > :first-child, .col-11 > :first-child > :first-child > :first-child, .tsd-panel > :first-child > :first-child > :first-child, ul.tsd-descriptions > li > :first-child > :first-child > :first-child { margin-top: 0; } +.col > :last-child, .col-1 > :last-child, .col-2 > :last-child, .col-3 > :last-child, .col-4 > :last-child, .col-5 > :last-child, .col-6 > :last-child, .col-7 > :last-child, .col-8 > :last-child, .col-9 > :last-child, .col-10 > :last-child, .col-11 > :last-child, .tsd-panel > :last-child, ul.tsd-descriptions > li > :last-child, .col > :last-child > :last-child, .col-1 > :last-child > :last-child, .col-2 > :last-child > :last-child, .col-3 > :last-child > :last-child, .col-4 > :last-child > :last-child, .col-5 > :last-child > :last-child, .col-6 > :last-child > :last-child, .col-7 > :last-child > :last-child, .col-8 > :last-child > :last-child, .col-9 > :last-child > :last-child, .col-10 > :last-child > :last-child, .col-11 > :last-child > :last-child, .tsd-panel > :last-child > :last-child, ul.tsd-descriptions > li > :last-child > :last-child, .col > :last-child > :last-child > :last-child, .col-1 > :last-child > :last-child > :last-child, .col-2 > :last-child > :last-child > :last-child, .col-3 > :last-child > :last-child > :last-child, .col-4 > :last-child > :last-child > :last-child, .col-5 > :last-child > :last-child > :last-child, .col-6 > :last-child > :last-child > :last-child, .col-7 > :last-child > :last-child > :last-child, .col-8 > :last-child > :last-child > :last-child, .col-9 > :last-child > :last-child > :last-child, .col-10 > :last-child > :last-child > :last-child, .col-11 > :last-child > :last-child > :last-child, .tsd-panel > :last-child > :last-child > :last-child, ul.tsd-descriptions > li > :last-child > :last-child > :last-child { margin-bottom: 0; } + +.container { max-width: 1200px; margin: 0 auto; padding: 0 40px; } +@media (max-width: 640px) { .container { padding: 0 20px; } } + +.container-main { padding-bottom: 200px; } + +.row { position: relative; margin: 0 -10px; } +.row:after { visibility: hidden; display: block; content: ""; clear: both; height: 0; } + +.col, .col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11 { box-sizing: border-box; float: left; padding: 0 10px; } + +.col-1 { width: 8.33333%; } + +.offset-1 { margin-left: 8.33333%; } + +.col-2 { width: 16.66667%; } + +.offset-2 { margin-left: 16.66667%; } + +.col-3 { width: 25%; } + +.offset-3 { margin-left: 25%; } + +.col-4 { width: 33.33333%; } + +.offset-4 { margin-left: 33.33333%; } + +.col-5 { width: 41.66667%; } + +.offset-5 { margin-left: 41.66667%; } + +.col-6 { width: 50%; } + +.offset-6 { margin-left: 50%; } + +.col-7 { width: 58.33333%; } + +.offset-7 { margin-left: 58.33333%; } + +.col-8 { width: 66.66667%; } + +.offset-8 { margin-left: 66.66667%; } + +.col-9 { width: 75%; } + +.offset-9 { margin-left: 75%; } + +.col-10 { width: 83.33333%; } + +.offset-10 { margin-left: 83.33333%; } + +.col-11 { width: 91.66667%; } + +.offset-11 { margin-left: 91.66667%; } + +.tsd-kind-icon { display: block; position: relative; padding-left: 20px; text-indent: -20px; } +.tsd-kind-icon:before { content: ''; display: inline-block; vertical-align: middle; width: 17px; height: 17px; margin: 0 3px 2px 0; background-image: url(../images/icons.png); } +@media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { .tsd-kind-icon:before { background-image: url(../images/icons@2x.png); background-size: 238px 204px; } } + +.tsd-signature.tsd-kind-icon:before { background-position: 0 -153px; } + +.tsd-kind-object-literal > .tsd-kind-icon:before { background-position: 0px -17px; } +.tsd-kind-object-literal.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -17px; } +.tsd-kind-object-literal.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -17px; } + +.tsd-kind-class > .tsd-kind-icon:before { background-position: 0px -34px; } +.tsd-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -34px; } +.tsd-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -34px; } + +.tsd-kind-class.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: 0px -51px; } +.tsd-kind-class.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -51px; } +.tsd-kind-class.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -51px; } + +.tsd-kind-interface > .tsd-kind-icon:before { background-position: 0px -68px; } +.tsd-kind-interface.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -68px; } +.tsd-kind-interface.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -68px; } + +.tsd-kind-interface.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: 0px -85px; } +.tsd-kind-interface.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -85px; } +.tsd-kind-interface.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -85px; } + +.tsd-kind-module > .tsd-kind-icon:before { background-position: 0px -102px; } +.tsd-kind-module.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -102px; } +.tsd-kind-module.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -102px; } + +.tsd-kind-external-module > .tsd-kind-icon:before { background-position: 0px -102px; } +.tsd-kind-external-module.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -102px; } +.tsd-kind-external-module.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -102px; } + +.tsd-kind-enum > .tsd-kind-icon:before { background-position: 0px -119px; } +.tsd-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -119px; } +.tsd-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -119px; } + +.tsd-kind-enum-member > .tsd-kind-icon:before { background-position: 0px -136px; } +.tsd-kind-enum-member.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -136px; } +.tsd-kind-enum-member.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -136px; } + +.tsd-kind-signature > .tsd-kind-icon:before { background-position: 0px -153px; } +.tsd-kind-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -153px; } +.tsd-kind-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -153px; } + +.tsd-kind-type-alias > .tsd-kind-icon:before { background-position: 0px -170px; } +.tsd-kind-type-alias.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -170px; } +.tsd-kind-type-alias.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -170px; } + +.tsd-kind-variable > .tsd-kind-icon:before { background-position: -136px -0px; } +.tsd-kind-variable.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -0px; } +.tsd-kind-variable.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-variable.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-variable.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -0px; } +.tsd-kind-variable.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -0px; } +.tsd-kind-variable.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-variable.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -0px; } +.tsd-kind-variable.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -0px; } + +.tsd-kind-property > .tsd-kind-icon:before { background-position: -136px -0px; } +.tsd-kind-property.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -0px; } +.tsd-kind-property.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-property.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-property.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -0px; } +.tsd-kind-property.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -0px; } +.tsd-kind-property.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-property.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -0px; } +.tsd-kind-property.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -0px; } + +.tsd-kind-get-signature > .tsd-kind-icon:before { background-position: -136px -17px; } +.tsd-kind-get-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -17px; } +.tsd-kind-get-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -17px; } + +.tsd-kind-set-signature > .tsd-kind-icon:before { background-position: -136px -34px; } +.tsd-kind-set-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -34px; } +.tsd-kind-set-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -34px; } + +.tsd-kind-accessor > .tsd-kind-icon:before { background-position: -136px -51px; } +.tsd-kind-accessor.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -51px; } +.tsd-kind-accessor.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; } +.tsd-kind-accessor.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -51px; } +.tsd-kind-accessor.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -51px; } +.tsd-kind-accessor.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; } +.tsd-kind-accessor.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -51px; } +.tsd-kind-accessor.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -51px; } + +.tsd-kind-function > .tsd-kind-icon:before { background-position: -136px -68px; } +.tsd-kind-function.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; } +.tsd-kind-function.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-function.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-function.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; } +.tsd-kind-function.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; } +.tsd-kind-function.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-function.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; } +.tsd-kind-function.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; } + +.tsd-kind-method > .tsd-kind-icon:before { background-position: -136px -68px; } +.tsd-kind-method.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; } +.tsd-kind-method.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-method.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-method.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; } +.tsd-kind-method.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; } +.tsd-kind-method.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-method.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; } +.tsd-kind-method.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; } + +.tsd-kind-call-signature > .tsd-kind-icon:before { background-position: -136px -68px; } +.tsd-kind-call-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; } +.tsd-kind-call-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; } + +.tsd-kind-function.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: -136px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -85px; } + +.tsd-kind-method.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: -136px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -85px; } + +.tsd-kind-constructor > .tsd-kind-icon:before { background-position: -136px -102px; } +.tsd-kind-constructor.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -102px; } +.tsd-kind-constructor.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -102px; } +.tsd-kind-constructor.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -102px; } +.tsd-kind-constructor.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -102px; } +.tsd-kind-constructor.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -102px; } + +.tsd-kind-constructor-signature > .tsd-kind-icon:before { background-position: -136px -102px; } +.tsd-kind-constructor-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -102px; } +.tsd-kind-constructor-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -102px; } + +.tsd-kind-index-signature > .tsd-kind-icon:before { background-position: -136px -119px; } +.tsd-kind-index-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -119px; } +.tsd-kind-index-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -119px; } + +.tsd-kind-event > .tsd-kind-icon:before { background-position: -136px -136px; } +.tsd-kind-event.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -136px; } +.tsd-kind-event.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; } +.tsd-kind-event.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; } +.tsd-kind-event.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -136px; } +.tsd-kind-event.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -136px; } +.tsd-kind-event.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; } +.tsd-kind-event.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -136px; } +.tsd-kind-event.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -136px; } + +.tsd-is-static > .tsd-kind-icon:before { background-position: -136px -153px; } +.tsd-is-static.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -153px; } +.tsd-is-static.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; } +.tsd-is-static.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; } +.tsd-is-static.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -153px; } +.tsd-is-static.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -153px; } +.tsd-is-static.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; } +.tsd-is-static.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -153px; } +.tsd-is-static.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -153px; } + +.tsd-is-static.tsd-kind-function > .tsd-kind-icon:before { background-position: -136px -170px; } +.tsd-is-static.tsd-kind-function.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; } +.tsd-is-static.tsd-kind-function.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-method > .tsd-kind-icon:before { background-position: -136px -170px; } +.tsd-is-static.tsd-kind-method.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; } +.tsd-is-static.tsd-kind-method.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-call-signature > .tsd-kind-icon:before { background-position: -136px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-event > .tsd-kind-icon:before { background-position: -136px -187px; } +.tsd-is-static.tsd-kind-event.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -187px; } +.tsd-is-static.tsd-kind-event.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -187px; } + +.no-transition { transition: none !important; } + +@-webkit-keyframes fade-in { from { opacity: 0; } + to { opacity: 1; } } + +@keyframes fade-in { from { opacity: 0; } + to { opacity: 1; } } +@-webkit-keyframes fade-out { from { opacity: 1; visibility: visible; } + to { opacity: 0; } } +@keyframes fade-out { from { opacity: 1; visibility: visible; } + to { opacity: 0; } } +@-webkit-keyframes fade-in-delayed { 0% { opacity: 0; } + 33% { opacity: 0; } + 100% { opacity: 1; } } +@keyframes fade-in-delayed { 0% { opacity: 0; } + 33% { opacity: 0; } + 100% { opacity: 1; } } +@-webkit-keyframes fade-out-delayed { 0% { opacity: 1; visibility: visible; } + 66% { opacity: 0; } + 100% { opacity: 0; } } +@keyframes fade-out-delayed { 0% { opacity: 1; visibility: visible; } + 66% { opacity: 0; } + 100% { opacity: 0; } } +@-webkit-keyframes shift-to-left { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); } + to { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } } +@keyframes shift-to-left { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); } + to { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } } +@-webkit-keyframes unshift-to-left { from { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } + to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } } +@keyframes unshift-to-left { from { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } + to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } } +@-webkit-keyframes pop-in-from-right { from { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } + to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } } +@keyframes pop-in-from-right { from { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } + to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } } +@-webkit-keyframes pop-out-to-right { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); visibility: visible; } + to { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } } +@keyframes pop-out-to-right { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); visibility: visible; } + to { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } } +body { background: #fdfdfd; font-family: "Segoe UI", sans-serif; font-size: 16px; color: #222; } + +a { color: #4da6ff; text-decoration: none; } +a:hover { text-decoration: underline; } + +code, pre { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; padding: 0.2em; margin: 0; font-size: 14px; background-color: rgba(0, 0, 0, 0.04); } + +pre { padding: 10px; } +pre code { padding: 0; font-size: 100%; background-color: transparent; } + +.tsd-typography { line-height: 1.333em; } +.tsd-typography ul { list-style: square; padding: 0 0 0 20px; margin: 0; } +.tsd-typography h4, .tsd-typography .tsd-index-panel h3, .tsd-index-panel .tsd-typography h3, .tsd-typography h5, .tsd-typography h6 { font-size: 1em; margin: 0; } +.tsd-typography h5, .tsd-typography h6 { font-weight: normal; } +.tsd-typography p, .tsd-typography ul, .tsd-typography ol { margin: 1em 0; } + +@media (min-width: 901px) and (max-width: 1024px) { html.default .col-content { width: 72%; } + html.default .col-menu { width: 28%; } + html.default .tsd-navigation { padding-left: 10px; } } +@media (max-width: 900px) { html.default .col-content { float: none; width: 100%; } + html.default .col-menu { position: fixed !important; overflow: auto; -webkit-overflow-scrolling: touch; overflow-scrolling: touch; z-index: 1024; top: 0 !important; bottom: 0 !important; left: auto !important; right: 0 !important; width: 100%; padding: 20px 20px 0 0; max-width: 450px; visibility: hidden; background-color: #fff; -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } + html.default .col-menu > *:last-child { padding-bottom: 20px; } + html.default .overlay { content: ""; display: block; position: fixed; z-index: 1023; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.75); visibility: hidden; } + html.default.to-has-menu .overlay { -webkit-animation: fade-in 0.4s; animation: fade-in 0.4s; } + html.default.to-has-menu header, html.default.to-has-menu footer, html.default.to-has-menu .col-content { -webkit-animation: shift-to-left 0.4s; animation: shift-to-left 0.4s; } + html.default.to-has-menu .col-menu { -webkit-animation: pop-in-from-right 0.4s; animation: pop-in-from-right 0.4s; } + html.default.from-has-menu .overlay { -webkit-animation: fade-out 0.4s; animation: fade-out 0.4s; } + html.default.from-has-menu header, html.default.from-has-menu footer, html.default.from-has-menu .col-content { -webkit-animation: unshift-to-left 0.4s; animation: unshift-to-left 0.4s; } + html.default.from-has-menu .col-menu { -webkit-animation: pop-out-to-right 0.4s; animation: pop-out-to-right 0.4s; } + html.default.has-menu body { overflow: hidden; } + html.default.has-menu .overlay { visibility: visible; } + html.default.has-menu header, html.default.has-menu footer, html.default.has-menu .col-content { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } + html.default.has-menu .col-menu { visibility: visible; -webkit-transform: translate(0, 0); transform: translate(0, 0); } } + +.tsd-page-title { padding: 70px 0 20px 0; margin: 0 0 40px 0; background: #fff; box-shadow: 0 0 5px rgba(0, 0, 0, 0.35); } +.tsd-page-title h1 { margin: 0; } + +.tsd-breadcrumb { margin: 0; padding: 0; color: #808080; } +.tsd-breadcrumb a { color: #808080; text-decoration: none; } +.tsd-breadcrumb a:hover { text-decoration: underline; } +.tsd-breadcrumb li { display: inline; } +.tsd-breadcrumb li:after { content: " / "; } + +html.minimal .container { margin: 0; } +html.minimal .container-main { padding-top: 50px; padding-bottom: 0; } +html.minimal .content-wrap { padding-left: 300px; } +html.minimal .tsd-navigation { position: fixed !important; overflow: auto; -webkit-overflow-scrolling: touch; overflow-scrolling: touch; box-sizing: border-box; z-index: 1; left: 0; top: 40px; bottom: 0; width: 300px; padding: 20px; margin: 0; } +html.minimal .tsd-member .tsd-member { margin-left: 0; } +html.minimal .tsd-page-toolbar { position: fixed; z-index: 2; } +html.minimal #tsd-filter .tsd-filter-group { right: 0; -webkit-transform: none; transform: none; } +html.minimal footer { background-color: transparent; } +html.minimal footer .container { padding: 0; } +html.minimal .tsd-generator { padding: 0; } +@media (max-width: 900px) { html.minimal .tsd-navigation { display: none; } + html.minimal .content-wrap { padding-left: 0; } } + +dl.tsd-comment-tags { overflow: hidden; } +dl.tsd-comment-tags dt { clear: both; float: left; padding: 1px 5px; margin: 0 10px 0 0; border-radius: 4px; border: 1px solid #808080; color: #808080; font-size: 0.8em; font-weight: normal; } +dl.tsd-comment-tags dd { margin: 0 0 10px 0; } +dl.tsd-comment-tags p { margin: 0; } + +.tsd-panel.tsd-comment .lead { font-size: 1.1em; line-height: 1.333em; margin-bottom: 2em; } +.tsd-panel.tsd-comment .lead:last-child { margin-bottom: 0; } + +.toggle-protected .tsd-is-private { display: none; } + +.toggle-public .tsd-is-private, .toggle-public .tsd-is-protected, .toggle-public .tsd-is-private-protected { display: none; } + +.toggle-inherited .tsd-is-inherited { display: none; } + +.toggle-only-exported .tsd-is-not-exported { display: none; } + +.toggle-externals .tsd-is-external { display: none; } + +#tsd-filter { position: relative; display: inline-block; height: 40px; vertical-align: bottom; } +.no-filter #tsd-filter { display: none; } +#tsd-filter .tsd-filter-group { display: inline-block; height: 40px; vertical-align: bottom; white-space: nowrap; } +#tsd-filter input { display: none; } +@media (max-width: 900px) { #tsd-filter .tsd-filter-group { display: block; position: absolute; top: 40px; right: 20px; height: auto; background-color: #fff; visibility: hidden; -webkit-transform: translate(50%, 0); transform: translate(50%, 0); box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } + .has-options #tsd-filter .tsd-filter-group { visibility: visible; } + .to-has-options #tsd-filter .tsd-filter-group { -webkit-animation: fade-in 0.2s; animation: fade-in 0.2s; } + .from-has-options #tsd-filter .tsd-filter-group { -webkit-animation: fade-out 0.2s; animation: fade-out 0.2s; } + #tsd-filter label, #tsd-filter .tsd-select { display: block; padding-right: 20px; } } + +footer { border-top: 1px solid #eee; background-color: #fff; } +footer.with-border-bottom { border-bottom: 1px solid #eee; } +footer .tsd-legend-group { font-size: 0; } +footer .tsd-legend { display: inline-block; width: 25%; padding: 0; font-size: 16px; list-style: none; line-height: 1.333em; vertical-align: top; } +@media (max-width: 900px) { footer .tsd-legend { width: 50%; } } + +.tsd-hierarchy { list-style: square; padding: 0 0 0 20px; margin: 0; } +.tsd-hierarchy .target { font-weight: bold; } + +.tsd-index-panel .tsd-index-content { margin-bottom: -30px !important; } +.tsd-index-panel .tsd-index-section { margin-bottom: 30px !important; } +.tsd-index-panel h3 { margin: 0 -20px 10px -20px; padding: 0 20px 10px 20px; border-bottom: 1px solid #eee; } +.tsd-index-panel ul.tsd-index-list { -webkit-column-count: 3; -moz-column-count: 3; -ms-column-count: 3; -o-column-count: 3; column-count: 3; -webkit-column-gap: 20px; -moz-column-gap: 20px; -ms-column-gap: 20px; -o-column-gap: 20px; column-gap: 20px; padding: 0; list-style: none; line-height: 1.333em; } +@media (max-width: 900px) { .tsd-index-panel ul.tsd-index-list { -webkit-column-count: 1; -moz-column-count: 1; -ms-column-count: 1; -o-column-count: 1; column-count: 1; } } +@media (min-width: 901px) and (max-width: 1024px) { .tsd-index-panel ul.tsd-index-list { -webkit-column-count: 2; -moz-column-count: 2; -ms-column-count: 2; -o-column-count: 2; column-count: 2; } } +.tsd-index-panel ul.tsd-index-list li { -webkit-column-break-inside: avoid; -moz-column-break-inside: avoid; -ms-column-break-inside: avoid; -o-column-break-inside: avoid; column-break-inside: avoid; -webkit-page-break-inside: avoid; -moz-page-break-inside: avoid; -ms-page-break-inside: avoid; -o-page-break-inside: avoid; page-break-inside: avoid; } +.tsd-index-panel a, .tsd-index-panel .tsd-parent-kind-module a { color: #9600ff; } +.tsd-index-panel .tsd-parent-kind-interface a { color: #7da01f; } +.tsd-index-panel .tsd-parent-kind-enum a { color: #cc9900; } +.tsd-index-panel .tsd-parent-kind-class a { color: #4da6ff; } +.tsd-index-panel .tsd-kind-module a { color: #9600ff; } +.tsd-index-panel .tsd-kind-interface a { color: #7da01f; } +.tsd-index-panel .tsd-kind-enum a { color: #cc9900; } +.tsd-index-panel .tsd-kind-class a { color: #4da6ff; } +.tsd-index-panel .tsd-is-private a { color: #808080; } + +.tsd-flag { display: inline-block; padding: 1px 5px; border-radius: 4px; color: #fff; background-color: #808080; text-indent: 0; font-size: 14px; font-weight: normal; } + +.tsd-anchor { position: absolute; top: -100px; } + +.tsd-member { position: relative; } +.tsd-member .tsd-anchor + h3 { margin-top: 0; margin-bottom: 0; border-bottom: none; } + +.tsd-navigation { padding: 0 0 0 40px; } +.tsd-navigation a { display: block; padding-top: 2px; padding-bottom: 2px; border-left: 2px solid transparent; color: #222; text-decoration: none; transition: border-left-color 0.1s; } +.tsd-navigation a:hover { text-decoration: underline; } +.tsd-navigation ul { margin: 0; padding: 0; list-style: none; } +.tsd-navigation li { padding: 0; } + +.tsd-navigation.primary { padding-bottom: 40px; } +.tsd-navigation.primary a { display: block; padding-top: 6px; padding-bottom: 6px; } +.tsd-navigation.primary ul li a { padding-left: 5px; } +.tsd-navigation.primary ul li li a { padding-left: 25px; } +.tsd-navigation.primary ul li li li a { padding-left: 45px; } +.tsd-navigation.primary ul li li li li a { padding-left: 65px; } +.tsd-navigation.primary ul li li li li li a { padding-left: 85px; } +.tsd-navigation.primary ul li li li li li li a { padding-left: 105px; } +.tsd-navigation.primary > ul { border-bottom: 1px solid #eee; } +.tsd-navigation.primary li { border-top: 1px solid #eee; } +.tsd-navigation.primary li.current > a { font-weight: bold; } +.tsd-navigation.primary li.label span { display: block; padding: 20px 0 6px 5px; color: #808080; } +.tsd-navigation.primary li.globals + li > span, .tsd-navigation.primary li.globals + li > a { padding-top: 20px; } + +.tsd-navigation.secondary ul { transition: opacity 0.2s; } +.tsd-navigation.secondary ul li a { padding-left: 25px; } +.tsd-navigation.secondary ul li li a { padding-left: 45px; } +.tsd-navigation.secondary ul li li li a { padding-left: 65px; } +.tsd-navigation.secondary ul li li li li a { padding-left: 85px; } +.tsd-navigation.secondary ul li li li li li a { padding-left: 105px; } +.tsd-navigation.secondary ul li li li li li li a { padding-left: 125px; } +.tsd-navigation.secondary ul.current a { border-left-color: #eee; } +.tsd-navigation.secondary li.focus > a, .tsd-navigation.secondary ul.current li.focus > a { border-left-color: #000; } +.tsd-navigation.secondary li.current { margin-top: 20px; margin-bottom: 20px; border-left-color: #eee; } +.tsd-navigation.secondary li.current > a { font-weight: bold; } + +@media (min-width: 901px) { .menu-sticky-wrap { position: static; } + .no-csspositionsticky .menu-sticky-wrap.sticky { position: fixed; } + .no-csspositionsticky .menu-sticky-wrap.sticky-current { position: fixed; } + .no-csspositionsticky .menu-sticky-wrap.sticky-current ul.before-current, .no-csspositionsticky .menu-sticky-wrap.sticky-current ul.after-current { opacity: 0; } + .no-csspositionsticky .menu-sticky-wrap.sticky-bottom { position: absolute; top: auto !important; left: auto !important; bottom: 0; right: 0; } + .csspositionsticky .menu-sticky-wrap.sticky { position: -webkit-sticky; position: sticky; } + .csspositionsticky .menu-sticky-wrap.sticky-current { position: -webkit-sticky; position: sticky; } } + +.tsd-panel { margin: 20px 0; padding: 20px; background-color: #fff; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } +.tsd-panel:empty { display: none; } +.tsd-panel > h1, .tsd-panel > h2, .tsd-panel > h3 { margin: 1.5em -20px 10px -20px; padding: 0 20px 10px 20px; border-bottom: 1px solid #eee; } +.tsd-panel > h1.tsd-before-signature, .tsd-panel > h2.tsd-before-signature, .tsd-panel > h3.tsd-before-signature { margin-bottom: 0; border-bottom: 0; } +.tsd-panel table { display: block; width: 100%; overflow: auto; margin-top: 10px; word-break: normal; word-break: keep-all; } +.tsd-panel table th { font-weight: bold; } +.tsd-panel table th, .tsd-panel table td { padding: 6px 13px; border: 1px solid #ddd; } +.tsd-panel table tr { background-color: #fff; border-top: 1px solid #ccc; } +.tsd-panel table tr:nth-child(2n) { background-color: #f8f8f8; } + +.tsd-panel-group { margin: 60px 0; } +.tsd-panel-group > h1, .tsd-panel-group > h2, .tsd-panel-group > h3 { padding-left: 20px; padding-right: 20px; } + +#tsd-search { transition: background-color 0.2s; } +#tsd-search .title { position: relative; z-index: 2; } +#tsd-search .field { position: absolute; left: 0; top: 0; right: 40px; height: 40px; } +#tsd-search .field input { box-sizing: border-box; position: relative; top: -50px; z-index: 1; width: 100%; padding: 0 10px; opacity: 0; outline: 0; border: 0; background: transparent; color: #222; } +#tsd-search .field label { position: absolute; overflow: hidden; right: -40px; } +#tsd-search .field input, #tsd-search .title { transition: opacity 0.2s; } +#tsd-search .results { position: absolute; visibility: hidden; top: 40px; width: 100%; margin: 0; padding: 0; list-style: none; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } +#tsd-search .results li { padding: 0 10px; background-color: #fdfdfd; } +#tsd-search .results li:nth-child(even) { background-color: #fff; } +#tsd-search .results li.state { display: none; } +#tsd-search .results li.current, #tsd-search .results li:hover { background-color: #eee; } +#tsd-search .results a { display: block; } +#tsd-search .results a:before { top: 10px; } +#tsd-search .results span.parent { color: #808080; font-weight: normal; } +#tsd-search.has-focus { background-color: #eee; } +#tsd-search.has-focus .field input { top: 0; opacity: 1; } +#tsd-search.has-focus .title { z-index: 0; opacity: 0; } +#tsd-search.has-focus .results { visibility: visible; } +#tsd-search.loading .results li.state.loading { display: block; } +#tsd-search.failure .results li.state.failure { display: block; } + +.tsd-signature { margin: 0 0 1em 0; padding: 10px; border: 1px solid #eee; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 14px; } +.tsd-signature.tsd-kind-icon { padding-left: 30px; } +.tsd-signature.tsd-kind-icon:before { top: 10px; left: 10px; } +.tsd-panel > .tsd-signature { margin-left: -20px; margin-right: -20px; border-width: 1px 0; } +.tsd-panel > .tsd-signature.tsd-kind-icon { padding-left: 40px; } +.tsd-panel > .tsd-signature.tsd-kind-icon:before { left: 20px; } + +.tsd-signature-symbol { color: #808080; font-weight: normal; } + +.tsd-signature-type { font-style: italic; font-weight: normal; } + +.tsd-signatures { padding: 0; margin: 0 0 1em 0; border: 1px solid #eee; } +.tsd-signatures .tsd-signature { margin: 0; border-width: 1px 0 0 0; transition: background-color 0.1s; } +.tsd-signatures .tsd-signature:first-child { border-top-width: 0; } +.tsd-signatures .tsd-signature.current { background-color: #eee; } +.tsd-signatures.active > .tsd-signature { cursor: pointer; } +.tsd-panel > .tsd-signatures { margin-left: -20px; margin-right: -20px; border-width: 1px 0; } +.tsd-panel > .tsd-signatures .tsd-signature.tsd-kind-icon { padding-left: 40px; } +.tsd-panel > .tsd-signatures .tsd-signature.tsd-kind-icon:before { left: 20px; } +.tsd-panel > a.anchor + .tsd-signatures { border-top-width: 0; margin-top: -20px; } + +ul.tsd-descriptions { position: relative; overflow: hidden; transition: height 0.3s; padding: 0; list-style: none; } +ul.tsd-descriptions.active > .tsd-description { display: none; } +ul.tsd-descriptions.active > .tsd-description.current { display: block; } +ul.tsd-descriptions.active > .tsd-description.fade-in { -webkit-animation: fade-in-delayed 0.3s; animation: fade-in-delayed 0.3s; } +ul.tsd-descriptions.active > .tsd-description.fade-out { -webkit-animation: fade-out-delayed 0.3s; animation: fade-out-delayed 0.3s; position: absolute; display: block; top: 0; left: 0; right: 0; opacity: 0; visibility: hidden; } +ul.tsd-descriptions h4, ul.tsd-descriptions .tsd-index-panel h3, .tsd-index-panel ul.tsd-descriptions h3 { font-size: 16px; margin: 1em 0 0.5em 0; } + +ul.tsd-parameters, ul.tsd-type-parameters { list-style: square; margin: 0; padding-left: 20px; } +ul.tsd-parameters > li.tsd-parameter-siganture, ul.tsd-type-parameters > li.tsd-parameter-siganture { list-style: none; margin-left: -20px; } +ul.tsd-parameters h5, ul.tsd-type-parameters h5 { font-size: 16px; margin: 1em 0 0.5em 0; } +ul.tsd-parameters .tsd-comment, ul.tsd-type-parameters .tsd-comment { margin-top: -0.5em; } + +.tsd-sources { font-size: 14px; color: #808080; margin: 0 0 1em 0; } +.tsd-sources a { color: #808080; text-decoration: underline; } +.tsd-sources ul, .tsd-sources p { margin: 0 !important; } +.tsd-sources ul { list-style: none; padding: 0; } + +.tsd-page-toolbar { position: absolute; z-index: 1; top: 0; left: 0; width: 100%; height: 40px; color: #333; background: #fff; border-bottom: 1px solid #eee; } +.tsd-page-toolbar a { color: #333; text-decoration: none; } +.tsd-page-toolbar a.title { font-weight: bold; } +.tsd-page-toolbar a.title:hover { text-decoration: underline; } +.tsd-page-toolbar .table-wrap { display: table; width: 100%; height: 40px; } +.tsd-page-toolbar .table-cell { display: table-cell; position: relative; white-space: nowrap; line-height: 40px; } +.tsd-page-toolbar .table-cell:first-child { width: 100%; } + +.tsd-widget:before, .tsd-select .tsd-select-label:before, .tsd-select .tsd-select-list li:before { content: ""; display: inline-block; width: 40px; height: 40px; margin: 0 -8px 0 0; background-image: url(../images/widgets.png); background-repeat: no-repeat; text-indent: -1024px; vertical-align: bottom; } +@media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { .tsd-widget:before, .tsd-select .tsd-select-label:before, .tsd-select .tsd-select-list li:before { background-image: url(../images/widgets@2x.png); background-size: 320px 40px; } } + +.tsd-widget { display: inline-block; overflow: hidden; opacity: 0.6; height: 40px; transition: opacity 0.1s, background-color 0.2s; vertical-align: bottom; cursor: pointer; } +.tsd-widget:hover { opacity: 0.8; } +.tsd-widget.active { opacity: 1; background-color: #eee; } +.tsd-widget.no-caption { width: 40px; } +.tsd-widget.no-caption:before { margin: 0; } +.tsd-widget.search:before { background-position: 0 0; } +.tsd-widget.menu:before { background-position: -40px 0; } +.tsd-widget.options:before { background-position: -80px 0; } +.tsd-widget.options, .tsd-widget.menu { display: none; } +@media (max-width: 900px) { .tsd-widget.options, .tsd-widget.menu { display: inline-block; } } +input[type=checkbox] + .tsd-widget:before { background-position: -120px 0; } +input[type=checkbox]:checked + .tsd-widget:before { background-position: -160px 0; } + +.tsd-select { position: relative; display: inline-block; height: 40px; transition: opacity 0.1s, background-color 0.2s; vertical-align: bottom; cursor: pointer; } +.tsd-select .tsd-select-label { opacity: 0.6; transition: opacity 0.2s; } +.tsd-select .tsd-select-label:before { background-position: -240px 0; } +.tsd-select.active .tsd-select-label { opacity: 0.8; } +.tsd-select.active .tsd-select-list { visibility: visible; opacity: 1; transition-delay: 0s; } +.tsd-select .tsd-select-list { position: absolute; visibility: hidden; top: 40px; left: 0; margin: 0; padding: 0; opacity: 0; list-style: none; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); transition: visibility 0s 0.2s, opacity 0.2s; } +.tsd-select .tsd-select-list li { padding: 0 20px 0 0; background-color: #fdfdfd; } +.tsd-select .tsd-select-list li:before { background-position: 40px 0; } +.tsd-select .tsd-select-list li:nth-child(even) { background-color: #fff; } +.tsd-select .tsd-select-list li:hover { background-color: #eee; } +.tsd-select .tsd-select-list li.selected:before { background-position: -200px 0; } +@media (max-width: 900px) { .tsd-select .tsd-select-list { top: 0; left: auto; right: 100%; margin-right: -5px; } + .tsd-select .tsd-select-label:before { background-position: -280px 0; } } + +img { max-width: 100%; } diff --git a/docs/assets/css/main.css.map b/docs/assets/css/main.css.map new file mode 100644 index 0000000..bc17fe4 --- /dev/null +++ b/docs/assets/css/main.css.map @@ -0,0 +1,7 @@ +{ +"version": 3, +"mappings": "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", +"sources": ["../../../../src/default/assets/css/vendors/_normalize.sass","../../../../src/default/assets/css/vendors/_highlight.js.sass","../../../../src/default/assets/css/setup/_mixins.sass","../../../../src/default/assets/css/setup/_grid.sass","../../../../src/default/assets/css/setup/_icons.scss","../../../../src/default/assets/css/setup/_animations.sass","../../../../src/default/assets/css/setup/_typography.sass","../../../../src/default/assets/css/_constants.sass","../../../../src/default/assets/css/layouts/_default.sass","../../../../src/default/assets/css/layouts/_minimal.sass","../../../../src/default/assets/css/elements/_comment.sass","../../../../src/default/assets/css/elements/_filter.sass","../../../../src/default/assets/css/elements/_footer.sass","../../../../src/default/assets/css/elements/_hierarchy.sass","../../../../src/default/assets/css/elements/_index.sass","../../../../src/default/assets/css/elements/_member.sass","../../../../src/default/assets/css/elements/_navigation.sass","../../../../src/default/assets/css/elements/_panel.sass","../../../../src/default/assets/css/elements/_search.sass","../../../../src/default/assets/css/elements/_signatures.sass","../../../../src/default/assets/css/elements/_sources.sass","../../../../src/default/assets/css/elements/_toolbar.sass","../../../../src/default/assets/css/elements/_images.sass"], +"names": [], +"file": "main.css" +} diff --git a/docs/assets/images/icons.png b/docs/assets/images/icons.png new file mode 100644 index 0000000..cb2d115 Binary files /dev/null and b/docs/assets/images/icons.png differ diff --git a/docs/assets/images/icons@2x.png b/docs/assets/images/icons@2x.png new file mode 100644 index 0000000..8932ba2 Binary files /dev/null and b/docs/assets/images/icons@2x.png differ diff --git a/docs/assets/images/widgets.png b/docs/assets/images/widgets.png new file mode 100644 index 0000000..c738053 Binary files /dev/null and b/docs/assets/images/widgets.png differ diff --git a/docs/assets/images/widgets@2x.png b/docs/assets/images/widgets@2x.png new file mode 100644 index 0000000..4bbbd57 Binary files /dev/null and b/docs/assets/images/widgets@2x.png differ diff --git a/docs/assets/js/main.js b/docs/assets/js/main.js new file mode 100644 index 0000000..528a3b0 --- /dev/null +++ b/docs/assets/js/main.js @@ -0,0 +1,5 @@ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){function s(a){var b=a.length,c=n.type(a);return"function"!==c&&!n.isWindow(a)&&(!(1!==a.nodeType||!b)||("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a))}function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}function D(a,b){for(;(a=a[b])&&1!==a.nodeType;);return a}function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+Math.random()}function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),"string"==typeof(c=a.getAttribute(d))){try{c="true"===c||"false"!==c&&("null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c)}catch(e){}M.set(a,b,c)}else c=void 0;return c}function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("