@corewire/companion-link 0.0.0 → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,705 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { ConnectionClosedError } from "@corewire/core";
3
+ import { CloseReason, Context, DeviceIdentity, EncryptionAwareConnection } from "@corewire/core/transport";
4
+ import { AccessoryCredentials, PairSetup } from "@corewire/core/pairing";
5
+ import { DiscoverOptions, DiscoveryResult } from "@corewire/core/discovery";
6
+
7
+ //#region src/messages.d.ts
8
+ declare namespace messages_d_exports {
9
+ export { OPackMessage, addToUpNext, appSignInRequest, deregisterInterests, fetchAttentionState, fetchCurrentTopShelfItems, fetchLaunchableApps, fetchMediaControlStatus, fetchNowPlayingInfo, fetchSiriRemoteInfo, fetchSupportedActions, fetchUpNext, fetchUserAccounts, gameControllerEvent, gameControllerStart, gameControllerStop, hidCommand, hidTouchEvent, launchApp, launchUrl, markAsWatched, mediaCaptionSettingGet, mediaCaptionSettingSet, mediaControlCommand, mediaSkipBySeconds, playMedia, registerInterests, removeFromUpNext, restrictedAccessRequest, sessionStart, sessionStop, siriStart, siriStop, switchActiveUserAccount, switchUserAccount, systemInfo, tiChange, tiStart, tiStop, toggleCaptions, toggleFindingMode, toggleReduceLoudSounds, toggleSystemAppearance, touchEvent, touchStart, touchStop, tvProviderRequest, tvrcSessionStart, tvrcSessionStop };
10
+ }
11
+ /** An OPack message for the Companion Link stream. */
12
+ type OPackMessage = Record<string, unknown>;
13
+ /** The `_systemInfo` request of the startup: the identity, model and services of this controller. */
14
+ declare function systemInfo(pairingId: Buffer, name: string, model?: string, sourceVersion?: string): OPackMessage;
15
+ /** Starts a session with the `com.apple.tvremoteservices` service under a session id of our own. */
16
+ declare function sessionStart(localSid: number): OPackMessage;
17
+ declare function sessionStop(localSid: number): OPackMessage;
18
+ /** Starts a TV Remote Control session, which `tvremoted` handles on the Apple TV. */
19
+ declare function tvrcSessionStart(): OPackMessage;
20
+ declare function tvrcSessionStop(): OPackMessage;
21
+ /** One button edge of a {@link HidCommand}; `down` false sends the release. */
22
+ declare function hidCommand(commandId: number, down: boolean): OPackMessage;
23
+ /** Starts the virtual touchpad; the size sets its coordinate space. */
24
+ declare function touchStart(width?: number, height?: number): OPackMessage;
25
+ declare function touchStop(): OPackMessage;
26
+ /**
27
+ * A `_touchC` event, in the coordinate space of the touchpad.
28
+ *
29
+ * @param finger - The finger index, 0-based.
30
+ * @param phase - A {@link TouchPhase} value.
31
+ */
32
+ declare function touchEvent(finger: number, phase: number, x: number, y: number): OPackMessage;
33
+ /**
34
+ * A `_hidT` event, the form an Apple remote sends touchpad gestures in on the wire. Unlike
35
+ * {@link touchEvent}, the coordinates are integers and the event carries a timestamp.
36
+ *
37
+ * @param finger - The finger index, 1-based.
38
+ * @param phase - A {@link HidTouchPhase} value.
39
+ * @param timestamp - A monotonic timestamp in nanoseconds.
40
+ */
41
+ declare function hidTouchEvent(finger: number, phase: number, x: number, y: number, timestamp?: number): OPackMessage;
42
+ /** Starts remote text input (RTI); the reply carries the keyboard session. */
43
+ declare function tiStart(): OPackMessage;
44
+ declare function tiStop(): OPackMessage;
45
+ /** A `_tiC` event with serialized RTITextOperations. */
46
+ declare function tiChange(tiD: Buffer): OPackMessage;
47
+ /**
48
+ * A `_mcc` request.
49
+ *
50
+ * @param commandId - A {@link MediaControlCommand} value.
51
+ * @param content - Extra fields, such as the volume for `SetVolume`.
52
+ */
53
+ declare function mediaControlCommand(commandId: number, content?: Record<string, unknown>): OPackMessage;
54
+ /** Skips forward, or backward for a negative number of seconds. */
55
+ declare function mediaSkipBySeconds(seconds: number): OPackMessage;
56
+ declare function mediaCaptionSettingGet(): OPackMessage;
57
+ declare function mediaCaptionSettingSet(enabled: boolean): OPackMessage;
58
+ declare function launchApp(bundleId: string): OPackMessage;
59
+ /** Opens a URL; the device picks the app through universal links. */
60
+ declare function launchUrl(url: string): OPackMessage;
61
+ /** Asks for the attention (power) state. */
62
+ declare function fetchAttentionState(): OPackMessage;
63
+ declare function fetchLaunchableApps(): OPackMessage;
64
+ declare function fetchMediaControlStatus(): OPackMessage;
65
+ declare function fetchNowPlayingInfo(): OPackMessage;
66
+ declare function fetchSiriRemoteInfo(): OPackMessage;
67
+ declare function fetchSupportedActions(): OPackMessage;
68
+ declare function fetchCurrentTopShelfItems(): OPackMessage;
69
+ declare function fetchUserAccounts(): OPackMessage;
70
+ declare function switchUserAccount(accountId: string): OPackMessage;
71
+ /**
72
+ * Tells the Apple TV which iCloud account (alternate DSID) this controller is active on. An iOS
73
+ * remote sends it during the startup.
74
+ */
75
+ declare function switchActiveUserAccount(iCloudAltDSID: string): OPackMessage;
76
+ /** Registers interest in events the device pushes, such as `_iMC` or `SystemStatus`. */
77
+ declare function registerInterests(events: string[]): OPackMessage;
78
+ declare function deregisterInterests(events: string[]): OPackMessage;
79
+ declare function toggleCaptions(): OPackMessage;
80
+ /** Light appearance when `light` is true, dark when false. */
81
+ declare function toggleSystemAppearance(light: boolean): OPackMessage;
82
+ declare function toggleReduceLoudSounds(enabled: boolean): OPackMessage;
83
+ /** Turns the Find My finding mode on or off. */
84
+ declare function toggleFindingMode(enabled: boolean): OPackMessage;
85
+ /** @param paginationToken - Fetches the page after the one that returned it. */
86
+ declare function fetchUpNext(paginationToken?: string): OPackMessage;
87
+ /** @param kind - The content kind, such as movie or episode. */
88
+ declare function addToUpNext(identifier: string, kind: string): OPackMessage;
89
+ declare function removeFromUpNext(identifier: string, kind: string): OPackMessage;
90
+ declare function markAsWatched(identifier: string, kind: string): OPackMessage;
91
+ declare function playMedia(item: Record<string, unknown>): OPackMessage;
92
+ /** Starts Siri push-to-talk. */
93
+ declare function siriStart(): OPackMessage;
94
+ /** Ends Siri push-to-talk. */
95
+ declare function siriStop(): OPackMessage;
96
+ /** One stick position (-1 to 1 per axis) and the button state, as an event without a reply. */
97
+ declare function gameControllerEvent(x: number, y: number, isDown: boolean): OPackMessage;
98
+ declare function gameControllerStart(): OPackMessage;
99
+ declare function gameControllerStop(): OPackMessage;
100
+ /**
101
+ * Runs the sign-in of an Apple TV app through this controller, as a paired iPhone or iPad does.
102
+ *
103
+ * @param requestType - The kind of sign-in: `appleID`, `password` or `custom`.
104
+ */
105
+ declare function appSignInRequest(bundleId: string, requestType?: string): OPackMessage;
106
+ /** A TV provider (MVPD) sign-in request. */
107
+ declare function tvProviderRequest(providerUrl: string, providerName: string): OPackMessage;
108
+ /** A restricted access (parental controls) approval request. */
109
+ declare function restrictedAccessRequest(restrictionType: string): OPackMessage;
110
+ //#endregion
111
+ //#region src/stream.d.ts
112
+ /** `event` carries every incoming frame with an `_i` that settled no exchange: its name and its `_c`. */
113
+ type StreamEventMap = {
114
+ readonly event: [name: string, content: unknown];
115
+ };
116
+ /**
117
+ * The Companion Link socket: OPack frames of `[type:1][length:3][payload][tag:16?]`, the header
118
+ * being the AAD once encryption is on. The nonce is the 64-bit little-endian message counter at
119
+ * offset 0 of 12 bytes. {@link exchange} waits for the reply with the same `_x`; every other frame
120
+ * with an `_i` becomes an `event`.
121
+ */
122
+ declare class Stream extends EncryptionAwareConnection<StreamEventMap> {
123
+ #private;
124
+ constructor(context: Context, address: string, port: number);
125
+ /**
126
+ * Sends an OPack frame and resolves with the decoded reply. Pairing frames share one slot,
127
+ * because their replies carry no `_x`.
128
+ *
129
+ * @throws {TimeoutError} When no reply arrives in time.
130
+ * @throws {ConnectionClosedError} When the stream ends first.
131
+ */
132
+ exchange(type: number, message: Record<string, unknown>, timeout?: number): Promise<[number, unknown]>;
133
+ /**
134
+ * Encodes an object as OPack and sends it, with the next `_x` unless it carries one.
135
+ *
136
+ * @throws {ConnectionClosedError} When the stream has ended.
137
+ */
138
+ sendOPack(type: number, message: Record<string, unknown>): void;
139
+ /** Writes one frame; encrypted once encryption is on, except NoOp, which stays plaintext. */
140
+ send(type: number, payload: Buffer): void;
141
+ }
142
+ //#endregion
143
+ //#region src/channel.d.ts
144
+ /** A decoded reply; `_c` holds the content. */
145
+ type Reply = Record<string, any>;
146
+ /** Receives the `_c` of an event. */
147
+ type EventListener = (content: unknown) => void;
148
+ /**
149
+ * What the groups of a session send with: encrypted requests that check the reply, events without
150
+ * one, and the events the device sends. Listeners go when the session signal aborts.
151
+ */
152
+ declare class Channel {
153
+ #private;
154
+ /** Aborts when the session ends. */
155
+ get signal(): AbortSignal;
156
+ /** Whether the socket can still carry a frame; false while the session closes. */
157
+ get isConnected(): boolean;
158
+ constructor(stream: Stream, signal: AbortSignal);
159
+ /**
160
+ * Sends a request and resolves with its reply.
161
+ *
162
+ * @throws {CommandError} When the reply carries `_em` or a non-zero `_ec`.
163
+ * @throws {TimeoutError} When no reply arrives in time.
164
+ * @throws {ConnectionClosedError} When the session ends first.
165
+ */
166
+ exchange(message: OPackMessage): Promise<Reply>;
167
+ /**
168
+ * Sends an event, which gets no reply.
169
+ *
170
+ * @throws {ConnectionClosedError} When the session has ended.
171
+ */
172
+ send(message: OPackMessage): void;
173
+ /** Calls the listener with the `_c` of every event with this name, until the session ends. */
174
+ listen(name: string, listener: EventListener): void;
175
+ unlisten(name: string, listener: EventListener): void;
176
+ }
177
+ //#endregion
178
+ //#region src/state.d.ts
179
+ type CompanionLinkStateEventMap = {
180
+ readonly attentionStateChanged: [AttentionState];
181
+ readonly mediaControlFlagsChanged: [flags: number, capabilities: MediaCapabilities];
182
+ readonly nowPlayingInfoChanged: [info: Record<string, unknown> | null];
183
+ readonly supportedActionsChanged: [actions: Record<string, unknown>];
184
+ readonly textInputChanged: [TextInputState];
185
+ readonly volumeAvailabilityChanged: [available: boolean];
186
+ };
187
+ /**
188
+ * What a Companion Link device reports about itself: attention state, media controls, now playing,
189
+ * supported actions and text input. A model without I/O: feed it with {@link apply}, from a session
190
+ * or from recorded events.
191
+ */
192
+ declare class CompanionLinkState extends EventEmitter<CompanionLinkStateEventMap> {
193
+ #private;
194
+ get attentionState(): AttentionState;
195
+ get mediaCapabilities(): MediaCapabilities;
196
+ /** The raw `_iMC` bitmask, see {@link MediaControlFlag}. */
197
+ get mediaControlFlags(): number;
198
+ get nowPlayingInfo(): Record<string, unknown> | null;
199
+ get supportedActions(): Record<string, unknown> | null;
200
+ get textInputState(): TextInputState;
201
+ get volumeAvailable(): boolean;
202
+ constructor();
203
+ /**
204
+ * Applies the `_c` of one event. Events the state does not track are ignored.
205
+ * `FetchAttentionState` takes the content of that reply and always emits, also when the state
206
+ * did not change, so a fresh session reports its attention state once.
207
+ *
208
+ * @throws When the content is malformed; text input falls back to its default state first.
209
+ */
210
+ apply(name: string, content: unknown): void;
211
+ /** Resets every value to its initial one, without events. */
212
+ clear(): void;
213
+ }
214
+ //#endregion
215
+ //#region src/types.d.ts
216
+ /** Where a Companion Link device listens. The port comes from discovery; it is not fixed. */
217
+ type CompanionLinkTarget = {
218
+ readonly address: string;
219
+ readonly port: number; /** Tags the logs of the connection, usually the mDNS name. Defaults to the address. */
220
+ readonly id?: string; /** The TXT record from discovery, when known. */
221
+ readonly txt?: Readonly<Record<string, string>>;
222
+ };
223
+ /** How {@link connect} reaches and authenticates with the device. */
224
+ type ConnectOptions = {
225
+ /** Long-term credentials from a PIN pairing, for pair-verify. The AirPlay credentials of an Apple TV work too. */readonly credentials: AccessoryCredentials; /** Logger to use. Built from {@link CompanionLinkTarget.id} and {@link identity} when omitted. */
226
+ readonly context?: Context; /** Overrides of the identity of the context; ignored when a context is passed. */
227
+ readonly identity?: DeviceIdentity;
228
+ /**
229
+ * The iCloud account (alternate DSID) to announce as active during the startup, as an iOS
230
+ * remote does. Without it the startup sends no `SwitchActiveUserAccountEvent`.
231
+ */
232
+ readonly activeUserAccount?: string;
233
+ /**
234
+ * The state to feed; a new one when omitted. Pass one to keep its listeners across sessions.
235
+ * It is fed as it is: clear it yourself when a new session should start empty.
236
+ */
237
+ readonly state?: CompanionLinkState; /** Ends the session right away, or interrupts the handshake, when aborted. */
238
+ readonly signal?: AbortSignal;
239
+ };
240
+ /** How a PIN pairing is set up. */
241
+ type PairOptions = Pick<ConnectOptions, "context" | "identity" | "signal">;
242
+ /**
243
+ * Why a session ended: this side closed it (`local`), the device did (`remote`), the socket did
244
+ * not open in time (`timeout`), or a socket or frame broke (`error`).
245
+ */
246
+ type SessionCloseReason = CloseReason;
247
+ /** Which playback controls the device offers now, from the `_iMC` flags. */
248
+ type MediaCapabilities = {
249
+ readonly play: boolean;
250
+ readonly pause: boolean;
251
+ readonly nextTrack: boolean;
252
+ readonly previousTrack: boolean;
253
+ readonly fastForward: boolean;
254
+ readonly rewind: boolean;
255
+ readonly volume: boolean;
256
+ readonly skipForward: boolean;
257
+ readonly skipBackward: boolean;
258
+ };
259
+ /** The attention (power) state of the Apple TV. */
260
+ type AttentionState = "unknown" | "asleep" | "screensaver" | "awake" | "idle";
261
+ /** The remote text input (RTI) session on the Apple TV. */
262
+ type TextInputState = {
263
+ /** True while a text field has focus. */readonly isActive: boolean;
264
+ readonly documentText: string; /** True for a password field. */
265
+ readonly isSecure: boolean; /** The keyboard type the field asks for. */
266
+ readonly keyboardType: number;
267
+ readonly autocorrection: boolean;
268
+ readonly autocapitalization: boolean;
269
+ };
270
+ type ButtonPressType = "DoubleTap" | "Hold" | "SingleTap";
271
+ type LaunchableApp = {
272
+ /** The bundle identifier, such as `com.apple.TVMovies`. */readonly bundleId: string;
273
+ readonly name: string;
274
+ };
275
+ type UserAccount = {
276
+ readonly accountId: string;
277
+ readonly name: string;
278
+ };
279
+ /** The phases of the `_touchC` event, the 0-based `UITouchPhase` values. */
280
+ declare const TouchPhase: {
281
+ readonly Began: 0;
282
+ readonly Moved: 1;
283
+ readonly Ended: 2;
284
+ readonly Cancelled: 3;
285
+ readonly Stationary: 4;
286
+ };
287
+ type TouchPhaseValue = typeof TouchPhase[keyof typeof TouchPhase];
288
+ /** The phases of the `_hidT` event. Unlike {@link TouchPhase}, the HID touch stream on the wire is 1-based. */
289
+ declare const HidTouchPhase: {
290
+ readonly Began: 1;
291
+ readonly Moved: 2;
292
+ readonly Stationary: 3;
293
+ readonly Ended: 4;
294
+ readonly Cancelled: 5;
295
+ };
296
+ type HidTouchPhaseValue = typeof HidTouchPhase[keyof typeof HidTouchPhase];
297
+ type SwipeDirection = "up" | "down" | "left" | "right";
298
+ //#endregion
299
+ //#region src/accounts.d.ts
300
+ /** User accounts on the device, and the experimental sign-in, TV provider and parental approval requests. */
301
+ declare class Accounts {
302
+ #private;
303
+ constructor(channel: Channel);
304
+ list(): Promise<UserAccount[]>;
305
+ switch(accountId: string): Promise<void>;
306
+ /** Tells the device which iCloud account (alternate DSID) this controller is active on. */
307
+ switchActive(iCloudAltDSID: string): Promise<void>;
308
+ /** @param requestType The kind of sign-in: `appleID`, `password` or `custom`. */
309
+ requestAppSignIn(bundleId: string, requestType?: string): Promise<object>;
310
+ requestTVProvider(providerUrl: string, providerName: string): Promise<object>;
311
+ requestRestrictedAccess(restrictionType: string): Promise<object>;
312
+ }
313
+ //#endregion
314
+ //#region src/apps.d.ts
315
+ /** The apps on the device: listing, launching, opening a URL and the top shelf. */
316
+ declare class Apps {
317
+ #private;
318
+ constructor(channel: Channel);
319
+ list(): Promise<LaunchableApp[]>;
320
+ launch(bundleId: string): Promise<void>;
321
+ /** Opens a URL; the device picks the app through universal links or the URL scheme. */
322
+ openUrl(url: string): Promise<void>;
323
+ /** The top shelf items the device shows now, as the raw reply. */
324
+ topShelf(): Promise<any>;
325
+ }
326
+ //#endregion
327
+ //#region src/const.d.ts
328
+ /** Siri Remote buttons, as the command ids of the `_hidC` request. */
329
+ declare const HidCommand: {
330
+ readonly Up: 1;
331
+ readonly Down: 2;
332
+ readonly Left: 3;
333
+ readonly Right: 4;
334
+ readonly Menu: 5;
335
+ readonly Select: 6;
336
+ readonly Home: 7;
337
+ readonly VolumeUp: 8;
338
+ readonly VolumeDown: 9;
339
+ readonly Siri: 10;
340
+ readonly Screensaver: 11;
341
+ readonly Sleep: 12;
342
+ readonly Wake: 13;
343
+ readonly PlayPause: 14;
344
+ readonly ChannelIncrement: 15;
345
+ readonly ChannelDecrement: 16;
346
+ readonly Guide: 17;
347
+ readonly PageUp: 18;
348
+ readonly PageDown: 19;
349
+ readonly SkipForward: 20;
350
+ readonly SkipBackward: 21;
351
+ readonly Back: 22;
352
+ readonly Exit: 23;
353
+ readonly Info: 24;
354
+ readonly CaptionsToggle: 25;
355
+ readonly Accessibility: 26;
356
+ readonly InputSelect: 27;
357
+ readonly Mute: 29;
358
+ readonly Power: 30;
359
+ };
360
+ /** Playback and caption commands of the `_mcc` request. */
361
+ declare const MediaControlCommand: {
362
+ readonly Play: 1;
363
+ readonly Pause: 2;
364
+ readonly NextTrack: 3;
365
+ readonly PreviousTrack: 4;
366
+ readonly GetVolume: 5;
367
+ readonly SetVolume: 6;
368
+ readonly SkipBy: 7;
369
+ readonly FastForwardBegin: 8;
370
+ readonly FastForwardEnd: 9;
371
+ readonly RewindBegin: 10;
372
+ readonly RewindEnd: 11;
373
+ readonly GetCaptionSettings: 12;
374
+ readonly SetCaptionSettings: 13;
375
+ };
376
+ /** Bit flags of the media controls the Apple TV supports right now, from the media control status. */
377
+ declare const MediaControlFlag: {
378
+ readonly Play: 0x0001;
379
+ readonly Pause: 0x0002;
380
+ readonly PreviousTrack: 0x0004;
381
+ readonly NextTrack: 0x0008;
382
+ readonly FastForward: 0x0010;
383
+ readonly Rewind: 0x0020;
384
+ readonly Volume: 0x0100;
385
+ readonly SkipForward: 0x0200;
386
+ readonly SkipBackward: 0x0400;
387
+ };
388
+ type HidCommandKey = keyof typeof HidCommand;
389
+ type MediaControlCommandKey = keyof typeof MediaControlCommand;
390
+ //#endregion
391
+ //#region src/hid.d.ts
392
+ /** Remote buttons as HID commands, and the experimental game controller. */
393
+ declare class Hid {
394
+ #private;
395
+ constructor(channel: Channel);
396
+ /** Sends one button edge: down, or up when `down` is false. */
397
+ command(command: HidCommandKey, down?: boolean): Promise<void>;
398
+ /**
399
+ * A full press: down and up for a single tap, that twice without a pause for a double tap,
400
+ * and for a hold the release after `holdDelayMs`.
401
+ */
402
+ press(command: HidCommandKey, type?: ButtonPressType, holdDelayMs?: number): Promise<void>;
403
+ gameControllerStart(): Promise<void>;
404
+ gameControllerStop(): Promise<void>;
405
+ /** One stick position (-1 to 1 per axis) and the button state. */
406
+ sendGameControllerEvent(x: number, y: number, isDown: boolean): void;
407
+ }
408
+ //#endregion
409
+ //#region src/interests.d.ts
410
+ /** The events the device pushes once interest in them is registered, such as `_iMC` or `SystemStatus`. */
411
+ declare class Interests {
412
+ #private;
413
+ constructor(channel: Channel);
414
+ /** Registers interest in the events, in one `_interest` event. */
415
+ register(events: string[]): void;
416
+ /** Deregisters interest in the events; nothing when the socket is gone, so a teardown cannot fail on it. */
417
+ deregister(events: string[]): void;
418
+ /** Listens to an event, until the session ends, and registers interest in it. */
419
+ subscribe(event: string, listener: EventListener): void;
420
+ /** Stops listening, when a listener is given, and deregisters interest in the event. */
421
+ unsubscribe(event: string, listener?: EventListener): void;
422
+ }
423
+ //#endregion
424
+ //#region src/media.d.ts
425
+ /**
426
+ * Media control over `_mcc`, what the device reports about playback, and playing a media item.
427
+ * The flags follow the `_iMC` events; the state reports a change.
428
+ */
429
+ declare class Media {
430
+ #private;
431
+ get capabilities(): MediaCapabilities;
432
+ /** The raw `_iMC` bitmask, see `MediaControlFlag`. */
433
+ get flags(): number;
434
+ get nowPlayingInfo(): Record<string, unknown> | null;
435
+ get supportedActions(): Record<string, unknown> | null;
436
+ get volumeAvailable(): boolean;
437
+ constructor(channel: Channel, state: CompanionLinkState);
438
+ /** @param content Extra fields of the command, such as the volume for `SetVolume`. */
439
+ command(command: MediaControlCommandKey, content?: Record<string, unknown>): Promise<object>;
440
+ /** Skips forward, or backward for a negative number of seconds. */
441
+ skipBy(seconds: number): Promise<object>;
442
+ /** Asks for the media control status; the reply is not used. */
443
+ fetchControlStatus(): Promise<void>;
444
+ /** The now playing information, as the raw reply. */
445
+ fetchNowPlayingInfo(): Promise<any>;
446
+ /** Asks for the supported actions; the reply is not used. */
447
+ fetchSupportedActions(): Promise<void>;
448
+ /** Plays a media item, described by the OPack dictionary the device expects. */
449
+ play(item: Record<string, unknown>): Promise<void>;
450
+ }
451
+ //#endregion
452
+ //#region src/power.d.ts
453
+ /**
454
+ * Attention state and the power buttons. The state follows the `SystemStatus` and `TVSystemStatus`
455
+ * events; its `attentionStateChanged` event reports a change.
456
+ */
457
+ declare class Power {
458
+ #private;
459
+ /** The attention state the device reported last. */
460
+ get attentionState(): AttentionState;
461
+ constructor(channel: Channel, hid: Hid, state: CompanionLinkState);
462
+ /** Asks the device for its attention state; the state is left as it is. */
463
+ fetch(): Promise<AttentionState>;
464
+ isAwake(): Promise<boolean>;
465
+ /** Presses the Wake button. */
466
+ wake(): Promise<void>;
467
+ /** Presses the Sleep button. */
468
+ sleep(): Promise<void>;
469
+ }
470
+ //#endregion
471
+ //#region src/system.d.ts
472
+ /** System settings, Siri and the Up Next queue. */
473
+ declare class System {
474
+ #private;
475
+ constructor(channel: Channel);
476
+ toggleCaptions(): Promise<void>;
477
+ /** The caption settings, over `_mcc`. */
478
+ getCaptionSetting(): Promise<object>;
479
+ setCaptionSetting(enabled: boolean): Promise<object>;
480
+ /** Light appearance when true, dark when false. */
481
+ setAppearance(light: boolean): Promise<void>;
482
+ setReduceLoudSounds(enabled: boolean): Promise<void>;
483
+ setFindingMode(enabled: boolean): Promise<void>;
484
+ /** Push-to-talk begin; see `supportsSiriPTT` on the session. */
485
+ siriStart(): Promise<void>;
486
+ siriStop(): Promise<void>;
487
+ /** What the device knows about the paired Siri Remote, from the plist in the reply. */
488
+ siriRemoteInfo(): Promise<any>;
489
+ /** The Up Next queue, as the raw reply. */
490
+ fetchUpNext(paginationToken?: string): Promise<any>;
491
+ addToUpNext(identifier: string, kind: string): Promise<void>;
492
+ removeFromUpNext(identifier: string, kind: string): Promise<void>;
493
+ markAsWatched(identifier: string, kind: string): Promise<void>;
494
+ }
495
+ //#endregion
496
+ //#region src/text.d.ts
497
+ /**
498
+ * Remote text input into the focused field. Operations run one at a time; each one starts the
499
+ * text input session anew for the keyboard session UUID. An operation queued before the field
500
+ * changed, stopped or the session ended fails instead of typing into the wrong field.
501
+ */
502
+ declare class Text {
503
+ #private;
504
+ /** The text input session the device reported last. */
505
+ get state(): TextInputState;
506
+ constructor(channel: Channel, state: CompanionLinkState);
507
+ /** Replaces the text of the field. */
508
+ set(text: string): Promise<void>;
509
+ append(text: string): Promise<void>;
510
+ clear(): Promise<void>;
511
+ /**
512
+ * Clears the field when asked, then inserts the text when there is any.
513
+ *
514
+ * @throws {Error} When no field has focus, or the field changed before the operation ran.
515
+ */
516
+ command(text: string, clearPreviousInput: boolean): Promise<string | null>;
517
+ /** Starts the text input session; the reply carries the keyboard session in `_c._tiD`. */
518
+ start(): Promise<Record<string, any>>;
519
+ stop(): Promise<void>;
520
+ }
521
+ //#endregion
522
+ //#region src/touch.d.ts
523
+ /** The virtual touchpad. Coordinates run from 0 to 1000 on both axes. */
524
+ declare class Touch {
525
+ #private;
526
+ constructor(channel: Channel, hid: Hid);
527
+ /**
528
+ * Sends a `_touchC` event. Kept for protocol work: tvOS registers no handler for it, so
529
+ * {@link sendHid} is what reaches the device.
530
+ *
531
+ * @param finger The finger index, 0-based.
532
+ * @param phase A `TouchPhase` value.
533
+ */
534
+ send(finger: number, phase: number, x: number, y: number): void;
535
+ /**
536
+ * Sends a `_hidT` event, the form the HID daemon of the Apple TV listens for.
537
+ *
538
+ * @param finger The finger index, 1-based.
539
+ * @param phase A {@link HidTouchPhase} value.
540
+ */
541
+ sendHid(finger: number, phase: number, x: number, y: number): void;
542
+ /**
543
+ * Taps the touch surface, which selects whatever has focus. The HID touch device reports
544
+ * contact and position only, so a touch that lands and lifts moves no focus and clicks
545
+ * nothing; a real remote sends the Select button for a tap.
546
+ *
547
+ * @param holdMs How long to hold, in milliseconds. Omit for a plain tap.
548
+ */
549
+ tap(holdMs?: number): Promise<void>;
550
+ /** Began, at least four Moved samples spread over `duration` milliseconds, then Ended at the end point. */
551
+ swipe(direction: SwipeDirection, duration?: number): Promise<void>;
552
+ }
553
+ //#endregion
554
+ //#region src/session.d.ts
555
+ type EventMap = {
556
+ closed: [reason: SessionCloseReason, error?: Error];
557
+ };
558
+ type SessionParts = {
559
+ readonly target: CompanionLinkTarget;
560
+ readonly context: Context;
561
+ readonly stream: Stream;
562
+ readonly state: CompanionLinkState;
563
+ readonly signal?: AbortSignal;
564
+ };
565
+ /**
566
+ * The device or the socket ended the connection after it opened and before the session was
567
+ * ready, which a caller may want to treat like a session that dropped.
568
+ */
569
+ declare class SetupClosedError extends ConnectionClosedError {
570
+ readonly reason: SessionCloseReason;
571
+ constructor(reason: SessionCloseReason);
572
+ }
573
+ /**
574
+ * Connects to a Companion Link device and returns a ready session. It runs TCP, pair-verify and
575
+ * encryption, then the startup: `_systemInfo`, `_sessionStart`, `TVRCSessionStart`, optionally
576
+ * `SwitchActiveUserAccountEvent`, `_touchStart` and `_tiStart` as requests, the heartbeat, one
577
+ * `_interest` event per state event, and the attention state and media control status.
578
+ *
579
+ * @throws {CredentialsError} Without credentials.
580
+ * @throws {PairingError} When pair-verify fails.
581
+ * @throws {ConnectionClosedError} When the signal aborts or the device closes during the handshake.
582
+ */
583
+ declare function connect(target: CompanionLinkTarget, options: ConnectOptions): Promise<CompanionLinkSession>;
584
+ /**
585
+ * One authenticated Companion Link connection, with its methods in groups. It lives once: it ends
586
+ * exactly once, with a reason, and then its {@link signal} aborts and the heartbeat, listeners and
587
+ * pending requests go with it. Requests on an ended session fail with {@link ConnectionClosedError}.
588
+ */
589
+ declare class CompanionLinkSession extends EventEmitter<EventMap> {
590
+ #private;
591
+ /** Why the session ended, or undefined while it is open. */
592
+ get closeReason(): SessionCloseReason | undefined;
593
+ get context(): Context;
594
+ get isOpen(): boolean;
595
+ /** Aborts with a {@link ConnectionClosedError} when the session ends. */
596
+ get signal(): AbortSignal;
597
+ /** The source version the device reported in `_systemInfo`, such as `715.2`; `0` when it sent none. */
598
+ get sourceVersion(): string;
599
+ /** The encrypted OPack stream, for raw frames. */
600
+ get stream(): Stream;
601
+ /** Media control needs source version 250.3 or later. */
602
+ get supportsMediaControl(): boolean;
603
+ /** Siri push-to-talk needs source version 600.20 or later. */
604
+ get supportsSiriPTT(): boolean;
605
+ /** Remote text input needs source version 340.15 or later. */
606
+ get supportsTextInput(): boolean;
607
+ get target(): CompanionLinkTarget;
608
+ readonly accounts: Accounts;
609
+ readonly apps: Apps;
610
+ readonly hid: Hid;
611
+ readonly interests: Interests;
612
+ readonly media: Media;
613
+ readonly power: Power;
614
+ readonly state: CompanionLinkState;
615
+ readonly system: System;
616
+ readonly text: Text;
617
+ readonly touch: Touch;
618
+ /** Use {@link connect}; a session starts from a verified, encrypted stream. */
619
+ constructor(parts: SessionParts);
620
+ /**
621
+ * Ends the session gracefully: stops the heartbeat, deregisters the interests, stops text
622
+ * input, touch, the remote control session and the service session (each failure ignored),
623
+ * then closes the socket. Resolves once it is closed; a peer that does not finish within
624
+ * {@link CLOSE_TIMEOUT} gets its socket destroyed.
625
+ */
626
+ close(): Promise<void>;
627
+ /** Sends a NoOp frame, which stays plaintext. */
628
+ noOp(): void;
629
+ }
630
+ //#endregion
631
+ //#region src/pairing.d.ts
632
+ type PairM1 = Awaited<ReturnType<PairSetup["m1"]>>;
633
+ /**
634
+ * Pair-verify over OPack frames, followed by the channel keys. The HKDF info strings are named
635
+ * from the device's side: it encrypts with `ServerEncrypt-main`, our read key.
636
+ */
637
+ /**
638
+ * Starts a PIN pairing: connects and sends pair-setup M1, upon which the device shows its PIN.
639
+ * Finish with {@link PinPairing.pin}, which returns the long-term credentials.
640
+ *
641
+ * @throws {PairingError} When the device refuses to start pairing.
642
+ * @throws {ConnectionClosedError} When the signal aborts first.
643
+ */
644
+ declare function pair(target: CompanionLinkTarget, options?: PairOptions): Promise<PinPairing>;
645
+ /** A PIN pairing whose PIN the device shows now. One connection, used once. */
646
+ declare class PinPairing {
647
+ #private;
648
+ /** Aborts when the pairing connection closes. */
649
+ get signal(): AbortSignal;
650
+ constructor(stream: Stream, setup: PairSetup, m1: PairM1);
651
+ /**
652
+ * Runs pair-setup M2 to M6 with the PIN from the screen. The connection stays open; call
653
+ * {@link close} when done.
654
+ *
655
+ * @throws {PairingError} When a step fails or the PIN is wrong.
656
+ */
657
+ pin(code: string): Promise<AccessoryCredentials>;
658
+ /** Closes the pairing connection. */
659
+ close(): void;
660
+ }
661
+ //#endregion
662
+ //#region src/discovery.d.ts
663
+ declare const COMPANION_LINK_SERVICE = "_companion-link._tcp.local";
664
+ /** Finds the devices on the local network that accept Companion Link connections. */
665
+ declare function discover(options?: DiscoverOptions): Promise<DiscoveryResult[]>;
666
+ /** The target of a discovery result. Reads the address, the port, the id and the TXT record only. */
667
+ declare function toTarget(result: DiscoveryResult): CompanionLinkTarget;
668
+ //#endregion
669
+ //#region src/frame.d.ts
670
+ /** The first byte of every frame header; it decides how the payload is parsed and routed. */
671
+ declare const FrameType: {
672
+ readonly Unknown: 0;
673
+ readonly NoOp: 1;
674
+ readonly PairSetupStart: 3;
675
+ readonly PairSetupNext: 4;
676
+ readonly PairVerifyStart: 5;
677
+ readonly PairVerifyNext: 6;
678
+ readonly OPackUnencrypted: 7;
679
+ readonly OPackEncrypted: 8;
680
+ readonly OPackPacked: 9;
681
+ readonly PairingRequest: 10;
682
+ readonly PairingResponse: 11;
683
+ readonly SessionStartRequest: 16;
684
+ readonly SessionStartResponse: 17;
685
+ readonly SessionData: 18;
686
+ readonly FamilyIdentityRequest: 32;
687
+ readonly FamilyIdentityResponse: 33;
688
+ readonly FamilyIdentityUpdate: 34;
689
+ };
690
+ /** The `_t` field of an OPack message. */
691
+ declare const MessageType: {
692
+ readonly Event: 1;
693
+ readonly Request: 2;
694
+ readonly Response: 3;
695
+ };
696
+ /** Frame types with an OPack payload. */
697
+ declare const OPackFrameTypes: number[];
698
+ /** Pair-setup and pair-verify frames. They have no `_x` exchange id, so they get a queue id of their own. */
699
+ declare const PairingFrameTypes: number[];
700
+ //#endregion
701
+ //#region src/utils.d.ts
702
+ /** Maps the numeric attention state of the Apple TV to its name; `unknown` for any other value. */
703
+ declare function convertAttentionState(state: number): AttentionState;
704
+ //#endregion
705
+ export { Accounts, Apps, type AttentionState, type ButtonPressType, COMPANION_LINK_SERVICE, Channel, CompanionLinkSession, CompanionLinkState, type CompanionLinkStateEventMap, type CompanionLinkTarget, type ConnectOptions, type EventListener, FrameType, Hid, HidCommand, type HidCommandKey, HidTouchPhase, type HidTouchPhaseValue, Interests, type LaunchableApp, Media, type MediaCapabilities, MediaControlCommand, type MediaControlCommandKey, MediaControlFlag, MessageType, messages_d_exports as Messages, OPackFrameTypes, type PairOptions, PairingFrameTypes, PinPairing, Power, type Reply, type SessionCloseReason, SetupClosedError, Stream, type StreamEventMap, type SwipeDirection, System, Text, type TextInputState, Touch, TouchPhase, type TouchPhaseValue, type UserAccount, connect, convertAttentionState, discover, pair, toTarget };