@basmilius/apple-devices 0.1.2 → 0.2.0
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.
- package/dist/airplay/device.d.ts +1 -1
- package/dist/airplay/state.d.ts +19 -19
- package/dist/companion-link/device.d.ts +2 -2
- package/dist/index.js +101 -90
- package/dist/model/apple-tv.d.ts +7 -6
- package/dist/model/homepod-base.d.ts +7 -6
- package/package.json +5 -5
package/dist/airplay/device.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export default class extends EventEmitter<EventMap> {
|
|
|
18
18
|
get volume(): Volume;
|
|
19
19
|
get timingServer(): TimingServer | undefined;
|
|
20
20
|
set timingServer(timingServer: TimingServer | undefined);
|
|
21
|
-
constructor(discoveryResult: DiscoveryResult);
|
|
21
|
+
constructor(deviceId: string, discoveryResult: DiscoveryResult);
|
|
22
22
|
connect(): Promise<void>;
|
|
23
23
|
disconnect(): Promise<void>;
|
|
24
24
|
disconnectSafely(): Promise<void>;
|
package/dist/airplay/state.d.ts
CHANGED
|
@@ -34,23 +34,23 @@ export default class extends EventEmitter<EventMap> {
|
|
|
34
34
|
get volumeCapabilities(): Proto.VolumeCapabilities_Enum;
|
|
35
35
|
constructor(device: Device);
|
|
36
36
|
clear(): void;
|
|
37
|
-
onDeviceInfo(message: Proto.DeviceInfoMessage):
|
|
38
|
-
onDeviceInfoUpdate(message: Proto.DeviceInfoMessage):
|
|
39
|
-
onOriginClientProperties(message: Proto.OriginClientPropertiesMessage):
|
|
40
|
-
onPlayerClientProperties(message: Proto.PlayerClientPropertiesMessage):
|
|
41
|
-
onRemoveClient(message: Proto.RemoveClientMessage):
|
|
42
|
-
onSendCommandResult(message: Proto.SendCommandResultMessage):
|
|
43
|
-
onSetArtwork(message: Proto.SetArtworkMessage):
|
|
44
|
-
onSetDefaultSupportedCommands(message: Proto.SetDefaultSupportedCommandsMessage):
|
|
45
|
-
onSetNowPlayingClient(message: Proto.SetNowPlayingClientMessage):
|
|
46
|
-
onSetNowPlayingPlayer(message: Proto.SetNowPlayingPlayerMessage):
|
|
47
|
-
onSetState(message: Proto.SetStateMessage):
|
|
48
|
-
onUpdateContentItem(message: Proto.UpdateContentItemMessage):
|
|
49
|
-
onUpdateContentItemArtwork(message: Proto.UpdateContentItemArtworkMessage):
|
|
50
|
-
onUpdatePlayer(message: Proto.UpdatePlayerMessage):
|
|
51
|
-
onUpdateClient(message: Proto.UpdateClientMessage):
|
|
52
|
-
onUpdateOutputDevice(message: Proto.UpdateOutputDeviceMessage):
|
|
53
|
-
onVolumeControlAvailability(message: Proto.VolumeControlAvailabilityMessage):
|
|
54
|
-
onVolumeControlCapabilitiesDidChange(message: Proto.VolumeControlCapabilitiesDidChangeMessage):
|
|
55
|
-
onVolumeDidChange(message: Proto.VolumeDidChangeMessage):
|
|
37
|
+
onDeviceInfo(message: Proto.DeviceInfoMessage): void;
|
|
38
|
+
onDeviceInfoUpdate(message: Proto.DeviceInfoMessage): void;
|
|
39
|
+
onOriginClientProperties(message: Proto.OriginClientPropertiesMessage): void;
|
|
40
|
+
onPlayerClientProperties(message: Proto.PlayerClientPropertiesMessage): void;
|
|
41
|
+
onRemoveClient(message: Proto.RemoveClientMessage): void;
|
|
42
|
+
onSendCommandResult(message: Proto.SendCommandResultMessage): void;
|
|
43
|
+
onSetArtwork(message: Proto.SetArtworkMessage): void;
|
|
44
|
+
onSetDefaultSupportedCommands(message: Proto.SetDefaultSupportedCommandsMessage): void;
|
|
45
|
+
onSetNowPlayingClient(message: Proto.SetNowPlayingClientMessage): void;
|
|
46
|
+
onSetNowPlayingPlayer(message: Proto.SetNowPlayingPlayerMessage): void;
|
|
47
|
+
onSetState(message: Proto.SetStateMessage): void;
|
|
48
|
+
onUpdateContentItem(message: Proto.UpdateContentItemMessage): void;
|
|
49
|
+
onUpdateContentItemArtwork(message: Proto.UpdateContentItemArtworkMessage): void;
|
|
50
|
+
onUpdatePlayer(message: Proto.UpdatePlayerMessage): void;
|
|
51
|
+
onUpdateClient(message: Proto.UpdateClientMessage): void;
|
|
52
|
+
onUpdateOutputDevice(message: Proto.UpdateOutputDeviceMessage): void;
|
|
53
|
+
onVolumeControlAvailability(message: Proto.VolumeControlAvailabilityMessage): void;
|
|
54
|
+
onVolumeControlCapabilitiesDidChange(message: Proto.VolumeControlCapabilitiesDidChangeMessage): void;
|
|
55
|
+
onVolumeDidChange(message: Proto.VolumeDidChangeMessage): void;
|
|
56
56
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
2
|
import type { AccessoryCredentials, DiscoveryResult } from "@basmilius/apple-common";
|
|
3
|
-
import type
|
|
3
|
+
import { type AttentionState, type ButtonPressType, type HidCommandKey, type LaunchableApp, type MediaControlCommandKey, type UserAccount } from "@basmilius/apple-companion-link";
|
|
4
4
|
type EventMap = {
|
|
5
5
|
connected: [];
|
|
6
6
|
disconnected: [unexpected: boolean];
|
|
@@ -11,7 +11,7 @@ export default class extends EventEmitter<EventMap> {
|
|
|
11
11
|
get discoveryResult(): DiscoveryResult;
|
|
12
12
|
set discoveryResult(discoveryResult: DiscoveryResult);
|
|
13
13
|
get isConnected(): boolean;
|
|
14
|
-
constructor(discoveryResult: DiscoveryResult);
|
|
14
|
+
constructor(deviceId: string, discoveryResult: DiscoveryResult);
|
|
15
15
|
connect(): Promise<void>;
|
|
16
16
|
disconnect(): Promise<void>;
|
|
17
17
|
disconnectSafely(): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -16,12 +16,11 @@ var STATE_SUBSCRIBE_SYMBOL = Symbol("com.basmilius.airplay:subscribe");
|
|
|
16
16
|
var STATE_UNSUBSCRIBE_SYMBOL = Symbol("com.basmilius.airplay:unsubscribe");
|
|
17
17
|
// src/airplay/device.ts
|
|
18
18
|
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
19
|
+
import { DataStreamMessage as DataStreamMessage3, Proto as Proto5, Protocol } from "@basmilius/apple-airplay";
|
|
20
|
+
import { waitFor as waitFor2 } from "@basmilius/apple-common";
|
|
21
21
|
|
|
22
22
|
// src/airplay/remote.ts
|
|
23
|
-
import { DataStreamMessage } from "@basmilius/apple-airplay";
|
|
24
|
-
import { Proto } from "@basmilius/apple-airplay";
|
|
23
|
+
import { DataStreamMessage, Proto } from "@basmilius/apple-airplay";
|
|
25
24
|
import { waitFor } from "@basmilius/apple-common";
|
|
26
25
|
class remote_default {
|
|
27
26
|
get #dataStream() {
|
|
@@ -1270,7 +1269,7 @@ class state_default extends EventEmitter {
|
|
|
1270
1269
|
this.onVolumeControlCapabilitiesDidChange = this.onVolumeControlCapabilitiesDidChange.bind(this);
|
|
1271
1270
|
this.onVolumeDidChange = this.onVolumeDidChange.bind(this);
|
|
1272
1271
|
}
|
|
1273
|
-
|
|
1272
|
+
[STATE_SUBSCRIBE_SYMBOL]() {
|
|
1274
1273
|
this.#dataStream.on("deviceInfo", this.onDeviceInfo);
|
|
1275
1274
|
this.#dataStream.on("deviceInfoUpdate", this.onDeviceInfoUpdate);
|
|
1276
1275
|
this.#dataStream.on("originClientProperties", this.onOriginClientProperties);
|
|
@@ -1291,7 +1290,7 @@ class state_default extends EventEmitter {
|
|
|
1291
1290
|
this.#dataStream.on("volumeControlCapabilitiesDidChange", this.onVolumeControlCapabilitiesDidChange);
|
|
1292
1291
|
this.#dataStream.on("volumeDidChange", this.onVolumeDidChange);
|
|
1293
1292
|
}
|
|
1294
|
-
|
|
1293
|
+
[STATE_UNSUBSCRIBE_SYMBOL]() {
|
|
1295
1294
|
const dataStream = this.#dataStream;
|
|
1296
1295
|
if (!dataStream) {
|
|
1297
1296
|
return;
|
|
@@ -1324,7 +1323,7 @@ class state_default extends EventEmitter {
|
|
|
1324
1323
|
this.#volumeAvailable = false;
|
|
1325
1324
|
this.#volumeCapabilities = Proto3.VolumeCapabilities_Enum.None;
|
|
1326
1325
|
}
|
|
1327
|
-
|
|
1326
|
+
onDeviceInfo(message) {
|
|
1328
1327
|
if (message.clusterID) {
|
|
1329
1328
|
this.#outputDeviceUID = message.clusterID;
|
|
1330
1329
|
} else if (message.deviceUID) {
|
|
@@ -1334,7 +1333,7 @@ class state_default extends EventEmitter {
|
|
|
1334
1333
|
}
|
|
1335
1334
|
this.emit("deviceInfo", message);
|
|
1336
1335
|
}
|
|
1337
|
-
|
|
1336
|
+
onDeviceInfoUpdate(message) {
|
|
1338
1337
|
if (message.clusterID) {
|
|
1339
1338
|
this.#outputDeviceUID = message.clusterID;
|
|
1340
1339
|
} else if (message.deviceUID) {
|
|
@@ -1344,36 +1343,36 @@ class state_default extends EventEmitter {
|
|
|
1344
1343
|
}
|
|
1345
1344
|
this.emit("deviceInfoUpdate", message);
|
|
1346
1345
|
}
|
|
1347
|
-
|
|
1346
|
+
onOriginClientProperties(message) {
|
|
1348
1347
|
this.emit("originClientProperties", message);
|
|
1349
1348
|
}
|
|
1350
|
-
|
|
1349
|
+
onPlayerClientProperties(message) {
|
|
1351
1350
|
this.emit("playerClientProperties", message);
|
|
1352
1351
|
}
|
|
1353
|
-
|
|
1352
|
+
onRemoveClient(message) {
|
|
1354
1353
|
if (!(message.client.bundleIdentifier in this.#clients)) {
|
|
1355
1354
|
return;
|
|
1356
1355
|
}
|
|
1357
1356
|
delete this.#clients[message.client.bundleIdentifier];
|
|
1358
1357
|
this.emit("clients", this.#clients);
|
|
1359
1358
|
}
|
|
1360
|
-
|
|
1359
|
+
onSendCommandResult(message) {
|
|
1361
1360
|
this.emit("sendCommandResult", message);
|
|
1362
1361
|
}
|
|
1363
|
-
|
|
1362
|
+
onSetArtwork(message) {
|
|
1364
1363
|
this.emit("setArtwork", message);
|
|
1365
1364
|
}
|
|
1366
|
-
|
|
1365
|
+
onSetDefaultSupportedCommands(message) {
|
|
1367
1366
|
this.emit("setDefaultSupportedCommands", message);
|
|
1368
1367
|
}
|
|
1369
|
-
|
|
1368
|
+
onSetNowPlayingClient(message) {
|
|
1370
1369
|
this.#nowPlayingClientBundleIdentifier = message.client?.bundleIdentifier ?? null;
|
|
1371
1370
|
this.emit("setNowPlayingClient", message);
|
|
1372
1371
|
}
|
|
1373
|
-
|
|
1372
|
+
onSetNowPlayingPlayer(message) {
|
|
1374
1373
|
this.emit("setNowPlayingPlayer", message);
|
|
1375
1374
|
}
|
|
1376
|
-
|
|
1375
|
+
onSetState(message) {
|
|
1377
1376
|
const client = this.#client(message.playerPath.client.bundleIdentifier, message.displayName);
|
|
1378
1377
|
if (message.playbackState) {
|
|
1379
1378
|
client.setPlaybackState(message.playbackState, message.playbackStateTimestamp);
|
|
@@ -1386,7 +1385,7 @@ class state_default extends EventEmitter {
|
|
|
1386
1385
|
}
|
|
1387
1386
|
this.emit("setState", message);
|
|
1388
1387
|
}
|
|
1389
|
-
|
|
1388
|
+
onUpdateContentItem(message) {
|
|
1390
1389
|
const client = this.#client(message.playerPath.client.bundleIdentifier, message.playerPath.client.displayName);
|
|
1391
1390
|
if (!client) {
|
|
1392
1391
|
return;
|
|
@@ -1396,30 +1395,30 @@ class state_default extends EventEmitter {
|
|
|
1396
1395
|
}
|
|
1397
1396
|
this.emit("updateContentItem", message);
|
|
1398
1397
|
}
|
|
1399
|
-
|
|
1398
|
+
onUpdateContentItemArtwork(message) {
|
|
1400
1399
|
this.emit("updateContentItemArtwork", message);
|
|
1401
1400
|
}
|
|
1402
|
-
|
|
1401
|
+
onUpdatePlayer(message) {
|
|
1403
1402
|
this.emit("updatePlayer", message);
|
|
1404
1403
|
}
|
|
1405
|
-
|
|
1404
|
+
onUpdateClient(message) {
|
|
1406
1405
|
this.#client(message.client.bundleIdentifier, message.client.displayName);
|
|
1407
1406
|
this.emit("clients", this.#clients);
|
|
1408
1407
|
}
|
|
1409
|
-
|
|
1408
|
+
onUpdateOutputDevice(message) {
|
|
1410
1409
|
this.emit("updateOutputDevice", message);
|
|
1411
1410
|
}
|
|
1412
|
-
|
|
1411
|
+
onVolumeControlAvailability(message) {
|
|
1413
1412
|
this.#volumeAvailable = message.volumeControlAvailable;
|
|
1414
1413
|
this.#volumeCapabilities = message.volumeCapabilities;
|
|
1415
1414
|
this.emit("volumeControlAvailability", message.volumeControlAvailable, message.volumeCapabilities);
|
|
1416
1415
|
}
|
|
1417
|
-
|
|
1416
|
+
onVolumeControlCapabilitiesDidChange(message) {
|
|
1418
1417
|
this.#volumeAvailable = message.capabilities.volumeControlAvailable;
|
|
1419
1418
|
this.#volumeCapabilities = message.capabilities.volumeCapabilities;
|
|
1420
1419
|
this.emit("volumeControlCapabilitiesDidChange", message.capabilities.volumeControlAvailable, message.capabilities.volumeCapabilities);
|
|
1421
1420
|
}
|
|
1422
|
-
|
|
1421
|
+
onVolumeDidChange(message) {
|
|
1423
1422
|
this.#volume = message.volume;
|
|
1424
1423
|
this.emit("volumeDidChange", message.volume);
|
|
1425
1424
|
}
|
|
@@ -1437,11 +1436,10 @@ class state_default extends EventEmitter {
|
|
|
1437
1436
|
|
|
1438
1437
|
// src/airplay/volume.ts
|
|
1439
1438
|
import { DataStreamMessage as DataStreamMessage2, Proto as Proto4 } from "@basmilius/apple-airplay";
|
|
1440
|
-
import { reporter } from "@basmilius/apple-common";
|
|
1441
1439
|
var VOLUME_STEP = 0.05;
|
|
1442
1440
|
|
|
1443
1441
|
class volume_default {
|
|
1444
|
-
get #
|
|
1442
|
+
get #protocol() {
|
|
1445
1443
|
return this.#device[PROTOCOL];
|
|
1446
1444
|
}
|
|
1447
1445
|
get #state() {
|
|
@@ -1483,7 +1481,7 @@ class volume_default {
|
|
|
1483
1481
|
if (!this.#state.outputDeviceUID) {
|
|
1484
1482
|
throw new Error("No output device active.");
|
|
1485
1483
|
}
|
|
1486
|
-
const response = await this.#
|
|
1484
|
+
const response = await this.#protocol.dataStream.exchange(DataStreamMessage2.getVolume(this.#state.outputDeviceUID));
|
|
1487
1485
|
if (response.type === Proto4.ProtocolMessage_Type.GET_VOLUME_RESULT_MESSAGE) {
|
|
1488
1486
|
const message = DataStreamMessage2.getExtension(response, Proto4.getVolumeResultMessage);
|
|
1489
1487
|
return message.volume;
|
|
@@ -1498,8 +1496,8 @@ class volume_default {
|
|
|
1498
1496
|
throw new Error("Absolute volume control is not available.");
|
|
1499
1497
|
}
|
|
1500
1498
|
volume = Math.min(1, Math.max(0, volume));
|
|
1501
|
-
|
|
1502
|
-
await this.#
|
|
1499
|
+
this.#protocol.context.logger.info(`Setting volume to ${volume} for device ${this.#state.outputDeviceUID}`);
|
|
1500
|
+
await this.#protocol.dataStream.exchange(DataStreamMessage2.setVolume(this.#state.outputDeviceUID, volume));
|
|
1503
1501
|
}
|
|
1504
1502
|
}
|
|
1505
1503
|
|
|
@@ -1515,7 +1513,7 @@ class device_default extends EventEmitter2 {
|
|
|
1515
1513
|
this.#discoveryResult = discoveryResult;
|
|
1516
1514
|
}
|
|
1517
1515
|
get isConnected() {
|
|
1518
|
-
return this.#protocol?.
|
|
1516
|
+
return this.#protocol?.controlStream?.isConnected ?? false;
|
|
1519
1517
|
}
|
|
1520
1518
|
get remote() {
|
|
1521
1519
|
return this.#remote;
|
|
@@ -1532,6 +1530,7 @@ class device_default extends EventEmitter2 {
|
|
|
1532
1530
|
set timingServer(timingServer) {
|
|
1533
1531
|
this.#timingServer = timingServer;
|
|
1534
1532
|
}
|
|
1533
|
+
#deviceId;
|
|
1535
1534
|
#remote;
|
|
1536
1535
|
#state;
|
|
1537
1536
|
#volume;
|
|
@@ -1542,8 +1541,9 @@ class device_default extends EventEmitter2 {
|
|
|
1542
1541
|
#keys;
|
|
1543
1542
|
#protocol;
|
|
1544
1543
|
#timingServer;
|
|
1545
|
-
constructor(discoveryResult) {
|
|
1544
|
+
constructor(deviceId, discoveryResult) {
|
|
1546
1545
|
super();
|
|
1546
|
+
this.#deviceId = deviceId;
|
|
1547
1547
|
this.#discoveryResult = discoveryResult;
|
|
1548
1548
|
this.#remote = new remote_default(this);
|
|
1549
1549
|
this.#state = new state_default(this);
|
|
@@ -1552,10 +1552,10 @@ class device_default extends EventEmitter2 {
|
|
|
1552
1552
|
async connect() {
|
|
1553
1553
|
this.#disconnect = false;
|
|
1554
1554
|
this.#state.clear();
|
|
1555
|
-
this.#protocol = new
|
|
1556
|
-
this.#protocol.
|
|
1557
|
-
this.#protocol.
|
|
1558
|
-
this.#protocol.
|
|
1555
|
+
this.#protocol = new Protocol(this.#deviceId, this.#discoveryResult);
|
|
1556
|
+
this.#protocol.controlStream.on("close", async () => this.#onClose());
|
|
1557
|
+
this.#protocol.controlStream.on("error", async (err) => this.#onError(err));
|
|
1558
|
+
this.#protocol.controlStream.on("timeout", async () => this.#onTimeout());
|
|
1559
1559
|
await this.#protocol.connect();
|
|
1560
1560
|
if (this.#credentials) {
|
|
1561
1561
|
this.#keys = await this.#protocol.verify.start(this.#credentials);
|
|
@@ -1591,11 +1591,11 @@ class device_default extends EventEmitter2 {
|
|
|
1591
1591
|
await this.#protocol.feedback();
|
|
1592
1592
|
await this.#protocol.dataStream.exchange(DataStreamMessage3.setConnectionState(Proto5.SetConnectionStateMessage_ConnectionState.Connected));
|
|
1593
1593
|
} catch (err) {
|
|
1594
|
-
|
|
1594
|
+
this.#protocol.context.logger.error("Feedback error", err);
|
|
1595
1595
|
}
|
|
1596
1596
|
}
|
|
1597
1597
|
async#onClose() {
|
|
1598
|
-
|
|
1598
|
+
this.#protocol.context.logger.net("#onClose() called on airplay device.");
|
|
1599
1599
|
if (!this.#disconnect) {
|
|
1600
1600
|
await this.disconnectSafely();
|
|
1601
1601
|
this.emit("disconnected", true);
|
|
@@ -1604,21 +1604,21 @@ class device_default extends EventEmitter2 {
|
|
|
1604
1604
|
}
|
|
1605
1605
|
}
|
|
1606
1606
|
async#onError(err) {
|
|
1607
|
-
|
|
1607
|
+
this.#protocol.context.logger.error("AirPlay error", err);
|
|
1608
1608
|
}
|
|
1609
1609
|
async#onTimeout() {
|
|
1610
|
-
|
|
1611
|
-
await this.#protocol.
|
|
1610
|
+
this.#protocol.context.logger.error("AirPlay timeout");
|
|
1611
|
+
await this.#protocol.controlStream.destroy();
|
|
1612
1612
|
}
|
|
1613
1613
|
async#setup() {
|
|
1614
1614
|
const keys = this.#keys;
|
|
1615
|
-
|
|
1616
|
-
|
|
1615
|
+
this.#protocol.controlStream.enableEncryption(keys.accessoryToControllerKey, keys.controllerToAccessoryKey);
|
|
1616
|
+
this.#unsubscribe();
|
|
1617
1617
|
if (this.#timingServer) {
|
|
1618
|
-
|
|
1618
|
+
this.#protocol.useTimingServer(this.#timingServer);
|
|
1619
1619
|
}
|
|
1620
1620
|
await this.#protocol.setupEventStream(keys.pairingId, keys.sharedSecret);
|
|
1621
|
-
await this.#protocol.setupDataStream(keys.sharedSecret,
|
|
1621
|
+
await this.#protocol.setupDataStream(keys.sharedSecret, () => this.#subscribe());
|
|
1622
1622
|
this.#protocol.dataStream.on("error", async (err) => this.#onError(err));
|
|
1623
1623
|
this.#protocol.dataStream.on("timeout", async () => this.#onTimeout());
|
|
1624
1624
|
this.#protocol.eventStream.on("error", async (err) => this.#onError(err));
|
|
@@ -1647,17 +1647,17 @@ class device_default extends EventEmitter2 {
|
|
|
1647
1647
|
if (!result) {
|
|
1648
1648
|
await this.#onError(new Error("Device did not respond in time with its info."));
|
|
1649
1649
|
} else {
|
|
1650
|
-
|
|
1650
|
+
this.#protocol.context.logger.info("Device info received successfully, protocol should be ready.");
|
|
1651
1651
|
}
|
|
1652
1652
|
}
|
|
1653
|
-
|
|
1654
|
-
|
|
1653
|
+
#subscribe() {
|
|
1654
|
+
this.#state[STATE_SUBSCRIBE_SYMBOL]();
|
|
1655
1655
|
}
|
|
1656
|
-
|
|
1656
|
+
#unsubscribe() {
|
|
1657
1657
|
try {
|
|
1658
|
-
|
|
1658
|
+
this.#state[STATE_UNSUBSCRIBE_SYMBOL]();
|
|
1659
1659
|
} catch (err) {
|
|
1660
|
-
|
|
1660
|
+
this.#protocol.context.logger.error("State unsubscribe error", err);
|
|
1661
1661
|
}
|
|
1662
1662
|
}
|
|
1663
1663
|
}
|
|
@@ -1665,8 +1665,7 @@ class device_default extends EventEmitter2 {
|
|
|
1665
1665
|
var PROTOCOL2 = Symbol("com.basmilius.companion-link:protocol");
|
|
1666
1666
|
// src/companion-link/device.ts
|
|
1667
1667
|
import { EventEmitter as EventEmitter3 } from "node:events";
|
|
1668
|
-
import {
|
|
1669
|
-
import { CompanionLink, convertAttentionState } from "@basmilius/apple-companion-link";
|
|
1668
|
+
import { convertAttentionState, Protocol as Protocol2 } from "@basmilius/apple-companion-link";
|
|
1670
1669
|
class device_default2 extends EventEmitter3 {
|
|
1671
1670
|
get [PROTOCOL2]() {
|
|
1672
1671
|
return this.#protocol;
|
|
@@ -1678,16 +1677,18 @@ class device_default2 extends EventEmitter3 {
|
|
|
1678
1677
|
this.#discoveryResult = discoveryResult;
|
|
1679
1678
|
}
|
|
1680
1679
|
get isConnected() {
|
|
1681
|
-
return this.#protocol?.
|
|
1680
|
+
return this.#protocol?.stream?.isConnected ?? false;
|
|
1682
1681
|
}
|
|
1682
|
+
#deviceId;
|
|
1683
1683
|
#credentials;
|
|
1684
1684
|
#disconnect = false;
|
|
1685
1685
|
#discoveryResult;
|
|
1686
1686
|
#heartbeatInterval;
|
|
1687
1687
|
#keys;
|
|
1688
1688
|
#protocol;
|
|
1689
|
-
constructor(discoveryResult) {
|
|
1689
|
+
constructor(deviceId, discoveryResult) {
|
|
1690
1690
|
super();
|
|
1691
|
+
this.#deviceId = deviceId;
|
|
1691
1692
|
this.#discoveryResult = discoveryResult;
|
|
1692
1693
|
this.onSystemStatus = this.onSystemStatus.bind(this);
|
|
1693
1694
|
this.onTVSystemStatus = this.onTVSystemStatus.bind(this);
|
|
@@ -1697,10 +1698,10 @@ class device_default2 extends EventEmitter3 {
|
|
|
1697
1698
|
throw new Error("Credentials are required to connect to a Companion Link device.");
|
|
1698
1699
|
}
|
|
1699
1700
|
this.#disconnect = false;
|
|
1700
|
-
this.#protocol = new
|
|
1701
|
-
this.#protocol.
|
|
1702
|
-
this.#protocol.
|
|
1703
|
-
this.#protocol.
|
|
1701
|
+
this.#protocol = new Protocol2(this.#deviceId, this.#discoveryResult);
|
|
1702
|
+
this.#protocol.stream.on("close", async () => this.#onClose());
|
|
1703
|
+
this.#protocol.stream.on("error", async (err) => this.#onError(err));
|
|
1704
|
+
this.#protocol.stream.on("timeout", async () => this.#onTimeout());
|
|
1704
1705
|
await this.#protocol.connect();
|
|
1705
1706
|
this.#keys = await this.#protocol.verify.start(this.#credentials);
|
|
1706
1707
|
await this.#setup();
|
|
@@ -1748,11 +1749,11 @@ class device_default2 extends EventEmitter3 {
|
|
|
1748
1749
|
try {
|
|
1749
1750
|
await this.#protocol._systemInfo(this.#credentials.pairingId);
|
|
1750
1751
|
} catch (err) {
|
|
1751
|
-
|
|
1752
|
+
this.#protocol.context.logger.error("Heartbeat error", err);
|
|
1752
1753
|
}
|
|
1753
1754
|
}
|
|
1754
1755
|
async#onClose() {
|
|
1755
|
-
|
|
1756
|
+
this.#protocol.context.logger.net("#onClose() called on companion link device.");
|
|
1756
1757
|
if (!this.#disconnect) {
|
|
1757
1758
|
await this.disconnectSafely();
|
|
1758
1759
|
this.emit("disconnected", true);
|
|
@@ -1761,15 +1762,15 @@ class device_default2 extends EventEmitter3 {
|
|
|
1761
1762
|
}
|
|
1762
1763
|
}
|
|
1763
1764
|
async#onError(err) {
|
|
1764
|
-
|
|
1765
|
+
this.#protocol.context.logger.error("Companion Link error", err);
|
|
1765
1766
|
}
|
|
1766
1767
|
async#onTimeout() {
|
|
1767
|
-
|
|
1768
|
-
await this.#protocol.
|
|
1768
|
+
this.#protocol.context.logger.error("Companion Link timeout");
|
|
1769
|
+
await this.#protocol.stream.destroy();
|
|
1769
1770
|
}
|
|
1770
1771
|
async#setup() {
|
|
1771
1772
|
const keys = this.#keys;
|
|
1772
|
-
|
|
1773
|
+
this.#protocol.stream.enableEncryption(keys.accessoryToControllerKey, keys.controllerToAccessoryKey);
|
|
1773
1774
|
await this.#protocol._systemInfo(this.#credentials.pairingId);
|
|
1774
1775
|
await this.#protocol._sessionStart();
|
|
1775
1776
|
await this.#protocol._tvrcSessionStart();
|
|
@@ -1792,17 +1793,17 @@ class device_default2 extends EventEmitter3 {
|
|
|
1792
1793
|
} catch (_) {}
|
|
1793
1794
|
}
|
|
1794
1795
|
async onSystemStatus(data) {
|
|
1795
|
-
|
|
1796
|
+
this.#protocol.context.logger.info("System Status", data);
|
|
1796
1797
|
this.emit("power", convertAttentionState(data.state));
|
|
1797
1798
|
}
|
|
1798
1799
|
async onTVSystemStatus(data) {
|
|
1799
|
-
|
|
1800
|
+
this.#protocol.context.logger.info("TV System Status", data);
|
|
1800
1801
|
this.emit("power", convertAttentionState(data.state));
|
|
1801
1802
|
}
|
|
1802
1803
|
}
|
|
1803
1804
|
// src/model/apple-tv.ts
|
|
1804
1805
|
import { EventEmitter as EventEmitter4 } from "node:events";
|
|
1805
|
-
import
|
|
1806
|
+
import * as AirPlay from "@basmilius/apple-airplay";
|
|
1806
1807
|
class apple_tv_default extends EventEmitter4 {
|
|
1807
1808
|
get airplay() {
|
|
1808
1809
|
return this.#airplay;
|
|
@@ -1813,6 +1814,9 @@ class apple_tv_default extends EventEmitter4 {
|
|
|
1813
1814
|
get bundleIdentifier() {
|
|
1814
1815
|
return this.#airplay.state.nowPlayingClient?.bundleIdentifier ?? null;
|
|
1815
1816
|
}
|
|
1817
|
+
get deviceId() {
|
|
1818
|
+
return this.#deviceId;
|
|
1819
|
+
}
|
|
1816
1820
|
get displayName() {
|
|
1817
1821
|
return this.#airplay.state.nowPlayingClient?.displayName ?? null;
|
|
1818
1822
|
}
|
|
@@ -1820,24 +1824,26 @@ class apple_tv_default extends EventEmitter4 {
|
|
|
1820
1824
|
return this.#airplay.isConnected && this.#companionLink.isConnected;
|
|
1821
1825
|
}
|
|
1822
1826
|
get isPlaying() {
|
|
1823
|
-
return this.playbackState ===
|
|
1827
|
+
return this.playbackState === AirPlay.Proto.PlaybackState_Enum.Playing;
|
|
1824
1828
|
}
|
|
1825
1829
|
get playbackQueue() {
|
|
1826
1830
|
return this.#airplay.state.nowPlayingClient?.playbackQueue ?? null;
|
|
1827
1831
|
}
|
|
1828
1832
|
get playbackState() {
|
|
1829
|
-
return this.#airplay.state.nowPlayingClient?.playbackState ??
|
|
1833
|
+
return this.#airplay.state.nowPlayingClient?.playbackState ?? AirPlay.Proto.PlaybackState_Enum.Unknown;
|
|
1830
1834
|
}
|
|
1831
1835
|
get playbackStateTimestamp() {
|
|
1832
1836
|
return this.#airplay.state.nowPlayingClient?.playbackStateTimestamp ?? -1;
|
|
1833
1837
|
}
|
|
1834
1838
|
#airplay;
|
|
1835
1839
|
#companionLink;
|
|
1840
|
+
#deviceId;
|
|
1836
1841
|
#disconnect = false;
|
|
1837
|
-
constructor(airplayDiscoveryResult, companionLinkDiscoveryResult) {
|
|
1842
|
+
constructor(deviceId, airplayDiscoveryResult, companionLinkDiscoveryResult) {
|
|
1838
1843
|
super();
|
|
1839
|
-
this.#
|
|
1840
|
-
this.#
|
|
1844
|
+
this.#deviceId = deviceId;
|
|
1845
|
+
this.#airplay = new device_default(this.#deviceId, airplayDiscoveryResult);
|
|
1846
|
+
this.#companionLink = new device_default2(this.#deviceId, companionLinkDiscoveryResult);
|
|
1841
1847
|
this.#airplay.on("connected", () => this.#onConnected());
|
|
1842
1848
|
this.#airplay.on("disconnected", (unexpected) => this.#onDisconnected(unexpected));
|
|
1843
1849
|
this.#companionLink.on("connected", () => this.#onConnected());
|
|
@@ -1861,22 +1867,22 @@ class apple_tv_default extends EventEmitter4 {
|
|
|
1861
1867
|
await this.#airplay.remote.wake();
|
|
1862
1868
|
}
|
|
1863
1869
|
async pause() {
|
|
1864
|
-
await this.#airplay.sendCommand(
|
|
1870
|
+
await this.#airplay.sendCommand(AirPlay.Proto.Command.Pause);
|
|
1865
1871
|
}
|
|
1866
1872
|
async playPause() {
|
|
1867
|
-
await this.#airplay.sendCommand(
|
|
1873
|
+
await this.#airplay.sendCommand(AirPlay.Proto.Command.TogglePlayPause);
|
|
1868
1874
|
}
|
|
1869
1875
|
async play() {
|
|
1870
|
-
await this.#airplay.sendCommand(
|
|
1876
|
+
await this.#airplay.sendCommand(AirPlay.Proto.Command.Play);
|
|
1871
1877
|
}
|
|
1872
1878
|
async stop() {
|
|
1873
|
-
await this.#airplay.sendCommand(
|
|
1879
|
+
await this.#airplay.sendCommand(AirPlay.Proto.Command.Stop);
|
|
1874
1880
|
}
|
|
1875
1881
|
async next() {
|
|
1876
|
-
await this.#airplay.sendCommand(
|
|
1882
|
+
await this.#airplay.sendCommand(AirPlay.Proto.Command.NextInContext);
|
|
1877
1883
|
}
|
|
1878
1884
|
async previous() {
|
|
1879
|
-
await this.#airplay.sendCommand(
|
|
1885
|
+
await this.#airplay.sendCommand(AirPlay.Proto.Command.PreviousInContext);
|
|
1880
1886
|
}
|
|
1881
1887
|
async volumeDown() {
|
|
1882
1888
|
await this.#airplay.remote.volumeDown();
|
|
@@ -1918,7 +1924,7 @@ class apple_tv_default extends EventEmitter4 {
|
|
|
1918
1924
|
}
|
|
1919
1925
|
// src/model/homepod-base.ts
|
|
1920
1926
|
import { EventEmitter as EventEmitter5 } from "node:events";
|
|
1921
|
-
import
|
|
1927
|
+
import * as AirPlay2 from "@basmilius/apple-airplay";
|
|
1922
1928
|
class homepod_base_default extends EventEmitter5 {
|
|
1923
1929
|
get airplay() {
|
|
1924
1930
|
return this.#airplay;
|
|
@@ -1926,6 +1932,9 @@ class homepod_base_default extends EventEmitter5 {
|
|
|
1926
1932
|
get bundleIdentifier() {
|
|
1927
1933
|
return this.#airplay.state.nowPlayingClient?.bundleIdentifier ?? null;
|
|
1928
1934
|
}
|
|
1935
|
+
get deviceId() {
|
|
1936
|
+
return this.#deviceId;
|
|
1937
|
+
}
|
|
1929
1938
|
get displayName() {
|
|
1930
1939
|
return this.#airplay.state.nowPlayingClient?.displayName ?? null;
|
|
1931
1940
|
}
|
|
@@ -1933,13 +1942,13 @@ class homepod_base_default extends EventEmitter5 {
|
|
|
1933
1942
|
return this.#airplay.isConnected;
|
|
1934
1943
|
}
|
|
1935
1944
|
get isPlaying() {
|
|
1936
|
-
return this.playbackState ===
|
|
1945
|
+
return this.playbackState === AirPlay2.Proto.PlaybackState_Enum.Playing;
|
|
1937
1946
|
}
|
|
1938
1947
|
get playbackQueue() {
|
|
1939
1948
|
return this.#airplay.state.nowPlayingClient?.playbackQueue ?? null;
|
|
1940
1949
|
}
|
|
1941
1950
|
get playbackState() {
|
|
1942
|
-
return this.#airplay.state.nowPlayingClient?.playbackState ??
|
|
1951
|
+
return this.#airplay.state.nowPlayingClient?.playbackState ?? AirPlay2.Proto.PlaybackState_Enum.Unknown;
|
|
1943
1952
|
}
|
|
1944
1953
|
get playbackStateTimestamp() {
|
|
1945
1954
|
return this.#airplay.state.nowPlayingClient?.playbackStateTimestamp ?? -1;
|
|
@@ -1948,10 +1957,12 @@ class homepod_base_default extends EventEmitter5 {
|
|
|
1948
1957
|
return this.#airplay.state.volume ?? 0;
|
|
1949
1958
|
}
|
|
1950
1959
|
#airplay;
|
|
1960
|
+
#deviceId;
|
|
1951
1961
|
#disconnect = false;
|
|
1952
|
-
constructor(discoveryResult) {
|
|
1962
|
+
constructor(deviceId, discoveryResult) {
|
|
1953
1963
|
super();
|
|
1954
|
-
this.#
|
|
1964
|
+
this.#deviceId = deviceId;
|
|
1965
|
+
this.#airplay = new device_default(this.#deviceId, discoveryResult);
|
|
1955
1966
|
this.#airplay.on("connected", () => this.#onConnected());
|
|
1956
1967
|
this.#airplay.on("disconnected", (unexpected) => this.#onDisconnected(unexpected));
|
|
1957
1968
|
}
|
|
@@ -1963,22 +1974,22 @@ class homepod_base_default extends EventEmitter5 {
|
|
|
1963
1974
|
await this.#airplay.disconnect();
|
|
1964
1975
|
}
|
|
1965
1976
|
async pause() {
|
|
1966
|
-
await this.#airplay.sendCommand(
|
|
1977
|
+
await this.#airplay.sendCommand(AirPlay2.Proto.Command.Pause);
|
|
1967
1978
|
}
|
|
1968
1979
|
async playPause() {
|
|
1969
|
-
await this.#airplay.sendCommand(
|
|
1980
|
+
await this.#airplay.sendCommand(AirPlay2.Proto.Command.TogglePlayPause);
|
|
1970
1981
|
}
|
|
1971
1982
|
async play() {
|
|
1972
|
-
await this.#airplay.sendCommand(
|
|
1983
|
+
await this.#airplay.sendCommand(AirPlay2.Proto.Command.Play);
|
|
1973
1984
|
}
|
|
1974
1985
|
async stop() {
|
|
1975
|
-
await this.#airplay.sendCommand(
|
|
1986
|
+
await this.#airplay.sendCommand(AirPlay2.Proto.Command.Stop);
|
|
1976
1987
|
}
|
|
1977
1988
|
async next() {
|
|
1978
|
-
await this.#airplay.sendCommand(
|
|
1989
|
+
await this.#airplay.sendCommand(AirPlay2.Proto.Command.NextInContext);
|
|
1979
1990
|
}
|
|
1980
1991
|
async previous() {
|
|
1981
|
-
await this.#airplay.sendCommand(
|
|
1992
|
+
await this.#airplay.sendCommand(AirPlay2.Proto.Command.PreviousInContext);
|
|
1982
1993
|
}
|
|
1983
1994
|
async getCommandInfo(command) {
|
|
1984
1995
|
const client = this.#airplay.state.nowPlayingClient;
|
package/dist/model/apple-tv.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
|
-
import { Proto } from "@basmilius/apple-airplay";
|
|
3
2
|
import type { AccessoryCredentials, DiscoveryResult } from "@basmilius/apple-common";
|
|
3
|
+
import * as AirPlay from "@basmilius/apple-airplay";
|
|
4
4
|
import { AirPlayDevice } from "../airplay";
|
|
5
5
|
import { CompanionLinkDevice } from "../companion-link";
|
|
6
6
|
type EventMap = {
|
|
@@ -12,13 +12,14 @@ export default class extends EventEmitter<EventMap> {
|
|
|
12
12
|
get airplay(): AirPlayDevice;
|
|
13
13
|
get companionLink(): CompanionLinkDevice;
|
|
14
14
|
get bundleIdentifier(): string | null;
|
|
15
|
+
get deviceId(): string;
|
|
15
16
|
get displayName(): string | null;
|
|
16
17
|
get isConnected(): boolean;
|
|
17
18
|
get isPlaying(): boolean;
|
|
18
|
-
get playbackQueue(): Proto.PlaybackQueue | null;
|
|
19
|
-
get playbackState(): Proto.PlaybackState_Enum;
|
|
19
|
+
get playbackQueue(): AirPlay.Proto.PlaybackQueue | null;
|
|
20
|
+
get playbackState(): AirPlay.Proto.PlaybackState_Enum;
|
|
20
21
|
get playbackStateTimestamp(): number;
|
|
21
|
-
constructor(airplayDiscoveryResult: DiscoveryResult, companionLinkDiscoveryResult: DiscoveryResult);
|
|
22
|
+
constructor(deviceId: string, airplayDiscoveryResult: DiscoveryResult, companionLinkDiscoveryResult: DiscoveryResult);
|
|
22
23
|
connect(credentials: AccessoryCredentials): Promise<void>;
|
|
23
24
|
disconnect(): Promise<void>;
|
|
24
25
|
turnOff(): Promise<void>;
|
|
@@ -32,6 +33,6 @@ export default class extends EventEmitter<EventMap> {
|
|
|
32
33
|
volumeDown(): Promise<void>;
|
|
33
34
|
volumeMute(): Promise<void>;
|
|
34
35
|
volumeUp(): Promise<void>;
|
|
35
|
-
getCommandInfo(command: Proto.Command): Promise<Proto.CommandInfo | null>;
|
|
36
|
-
isCommandSupported(command: Proto.Command): Promise<boolean>;
|
|
36
|
+
getCommandInfo(command: AirPlay.Proto.Command): Promise<AirPlay.Proto.CommandInfo | null>;
|
|
37
|
+
isCommandSupported(command: AirPlay.Proto.Command): Promise<boolean>;
|
|
37
38
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
|
-
import { Proto } from "@basmilius/apple-airplay";
|
|
3
2
|
import type { DiscoveryResult } from "@basmilius/apple-common";
|
|
3
|
+
import * as AirPlay from "@basmilius/apple-airplay";
|
|
4
4
|
import { AirPlayDevice } from "../airplay";
|
|
5
5
|
type EventMap = {
|
|
6
6
|
connected: [];
|
|
@@ -10,14 +10,15 @@ export default abstract class extends EventEmitter<EventMap> {
|
|
|
10
10
|
#private;
|
|
11
11
|
get airplay(): AirPlayDevice;
|
|
12
12
|
get bundleIdentifier(): string | null;
|
|
13
|
+
get deviceId(): string;
|
|
13
14
|
get displayName(): string | null;
|
|
14
15
|
get isConnected(): boolean;
|
|
15
16
|
get isPlaying(): boolean;
|
|
16
|
-
get playbackQueue(): Proto.PlaybackQueue | null;
|
|
17
|
-
get playbackState(): Proto.PlaybackState_Enum;
|
|
17
|
+
get playbackQueue(): AirPlay.Proto.PlaybackQueue | null;
|
|
18
|
+
get playbackState(): AirPlay.Proto.PlaybackState_Enum;
|
|
18
19
|
get playbackStateTimestamp(): number;
|
|
19
20
|
get volume(): number;
|
|
20
|
-
constructor(discoveryResult: DiscoveryResult);
|
|
21
|
+
constructor(deviceId: string, discoveryResult: DiscoveryResult);
|
|
21
22
|
connect(): Promise<void>;
|
|
22
23
|
disconnect(): Promise<void>;
|
|
23
24
|
pause(): Promise<void>;
|
|
@@ -26,6 +27,6 @@ export default abstract class extends EventEmitter<EventMap> {
|
|
|
26
27
|
stop(): Promise<void>;
|
|
27
28
|
next(): Promise<void>;
|
|
28
29
|
previous(): Promise<void>;
|
|
29
|
-
getCommandInfo(command: Proto.Command): Promise<Proto.CommandInfo | null>;
|
|
30
|
-
isCommandSupported(command: Proto.Command): Promise<boolean>;
|
|
30
|
+
getCommandInfo(command: AirPlay.Proto.Command): Promise<AirPlay.Proto.CommandInfo | null>;
|
|
31
|
+
isCommandSupported(command: AirPlay.Proto.Command): Promise<boolean>;
|
|
31
32
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basmilius/apple-devices",
|
|
3
3
|
"description": "Exposes various Apple devices to connect with either AirPlay or Companion Link.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": {
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@basmilius/apple-airplay": "0.
|
|
47
|
-
"@basmilius/apple-common": "0.
|
|
48
|
-
"@basmilius/apple-companion-link": "0.
|
|
49
|
-
"@basmilius/apple-encoding": "0.
|
|
46
|
+
"@basmilius/apple-airplay": "0.2.0",
|
|
47
|
+
"@basmilius/apple-common": "0.2.0",
|
|
48
|
+
"@basmilius/apple-companion-link": "0.2.0",
|
|
49
|
+
"@basmilius/apple-encoding": "0.2.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@basmilius/tools": "^2.23.0",
|