homebridge-xbox-tv 3.7.9-beta.21 → 3.7.9-beta.23
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/package.json +1 -1
- package/src/localApi/structure.js +73 -65
- package/src/localApi/xboxlocalapi.js +4 -88
- package/src/xboxdevice.js +1 -4
package/package.json
CHANGED
|
@@ -1,140 +1,148 @@
|
|
|
1
1
|
class Structure {
|
|
2
2
|
constructor(packet) {
|
|
3
|
-
packet = packet
|
|
4
|
-
this.
|
|
5
|
-
this.totalLength = packet.length;
|
|
3
|
+
this.packet = packet ?? Buffer.alloc(0);
|
|
4
|
+
this.totalLength = this.packet.length;
|
|
6
5
|
this.offset = 0;
|
|
7
|
-
|
|
8
|
-
return this;
|
|
9
|
-
};
|
|
6
|
+
}
|
|
10
7
|
|
|
11
8
|
writeSGString(data) {
|
|
12
9
|
if (typeof data !== 'string') {
|
|
13
10
|
throw new Error('data must be a string');
|
|
14
11
|
}
|
|
15
12
|
|
|
16
|
-
const dataLength = data
|
|
13
|
+
const dataLength = Buffer.byteLength(data, 'utf8');
|
|
17
14
|
if (dataLength > 65535) {
|
|
18
15
|
throw new Error('data exceeds the maximum allowed length');
|
|
19
16
|
}
|
|
20
17
|
|
|
21
18
|
const stringLengthBuffer = Buffer.allocUnsafe(2);
|
|
22
19
|
stringLengthBuffer.writeUInt16BE(dataLength, 0);
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
|
|
21
|
+
const dataBuffer = Buffer.from(data, 'utf8');
|
|
22
|
+
const nullTerminator = Buffer.from([0]);
|
|
23
|
+
|
|
24
|
+
this.add(Buffer.concat([stringLengthBuffer, dataBuffer, nullTerminator]));
|
|
25
25
|
return this;
|
|
26
|
-
}
|
|
26
|
+
}
|
|
27
27
|
|
|
28
28
|
readSGString() {
|
|
29
29
|
const stringLength = this.readUInt16();
|
|
30
30
|
const stringBuffer = this.packet.slice(this.offset, this.offset + stringLength);
|
|
31
|
-
this.offset += stringLength + 1;
|
|
32
|
-
return stringBuffer;
|
|
33
|
-
}
|
|
31
|
+
this.offset += stringLength + 1; // skip null terminator
|
|
32
|
+
return stringBuffer.toString('utf8');
|
|
33
|
+
}
|
|
34
34
|
|
|
35
35
|
writeBytes(data, type) {
|
|
36
36
|
const dataBuffer = Buffer.from(data, type);
|
|
37
37
|
this.add(dataBuffer);
|
|
38
38
|
return this;
|
|
39
|
-
}
|
|
39
|
+
}
|
|
40
40
|
|
|
41
41
|
readBytes(length = false) {
|
|
42
|
-
let rawData
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
rawData = this.packet.slice(this.offset, this.offset + length);
|
|
50
|
-
this.offset += length;
|
|
51
|
-
break
|
|
42
|
+
let rawData;
|
|
43
|
+
if (length === false) {
|
|
44
|
+
rawData = this.packet.slice(this.offset);
|
|
45
|
+
this.offset = this.totalLength;
|
|
46
|
+
} else {
|
|
47
|
+
rawData = this.packet.slice(this.offset, this.offset + length);
|
|
48
|
+
this.offset += length;
|
|
52
49
|
}
|
|
53
50
|
return rawData;
|
|
54
|
-
}
|
|
51
|
+
}
|
|
55
52
|
|
|
56
53
|
writeUInt8(data) {
|
|
57
54
|
if (data < 0 || data > 255) {
|
|
58
55
|
throw new Error('data must be a valid unsigned 8-bit integer');
|
|
59
56
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
this.add(uint8Buffer);
|
|
57
|
+
const buf = Buffer.allocUnsafe(1);
|
|
58
|
+
buf.writeUInt8(data, 0);
|
|
59
|
+
this.add(buf);
|
|
64
60
|
return this;
|
|
65
|
-
}
|
|
61
|
+
}
|
|
66
62
|
|
|
67
63
|
readUInt8() {
|
|
68
|
-
const
|
|
64
|
+
const value = this.packet.readUInt8(this.offset);
|
|
69
65
|
this.offset += 1;
|
|
70
|
-
return
|
|
71
|
-
}
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
72
68
|
|
|
73
69
|
writeUInt16(data) {
|
|
74
70
|
if (data < 0 || data > 65535) {
|
|
75
71
|
throw new Error('data must be a valid unsigned 16-bit integer');
|
|
76
72
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
this.add(uint16Buffer);
|
|
73
|
+
const buf = Buffer.allocUnsafe(2);
|
|
74
|
+
buf.writeUInt16BE(data, 0);
|
|
75
|
+
this.add(buf);
|
|
81
76
|
return this;
|
|
82
|
-
}
|
|
77
|
+
}
|
|
83
78
|
|
|
84
79
|
readUInt16() {
|
|
85
|
-
const
|
|
80
|
+
const value = this.packet.readUInt16BE(this.offset);
|
|
86
81
|
this.offset += 2;
|
|
87
|
-
return
|
|
88
|
-
}
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
89
84
|
|
|
90
85
|
writeUInt32(data) {
|
|
91
|
-
if (data < 0 || data >
|
|
86
|
+
if (data < 0 || data > 0xFFFFFFFF) {
|
|
92
87
|
throw new Error('data must be a valid unsigned 32-bit integer');
|
|
93
88
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
this.add(uint32Buffer);
|
|
89
|
+
const buf = Buffer.allocUnsafe(4);
|
|
90
|
+
buf.writeUInt32BE(data, 0);
|
|
91
|
+
this.add(buf);
|
|
98
92
|
return this;
|
|
99
|
-
}
|
|
93
|
+
}
|
|
100
94
|
|
|
101
95
|
readUInt32() {
|
|
102
|
-
const
|
|
96
|
+
const value = this.packet.readUInt32BE(this.offset);
|
|
103
97
|
this.offset += 4;
|
|
104
|
-
return
|
|
105
|
-
}
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
106
100
|
|
|
107
101
|
writeInt32(data) {
|
|
108
102
|
if (data < -2147483648 || data > 2147483647) {
|
|
109
103
|
throw new Error('data must be a valid signed 32-bit integer');
|
|
110
104
|
}
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
this.add(
|
|
105
|
+
const buf = Buffer.allocUnsafe(4);
|
|
106
|
+
buf.writeInt32BE(data, 0);
|
|
107
|
+
this.add(buf);
|
|
114
108
|
return this;
|
|
115
|
-
}
|
|
109
|
+
}
|
|
116
110
|
|
|
117
111
|
readInt32() {
|
|
118
|
-
const
|
|
112
|
+
const value = this.packet.readInt32BE(this.offset);
|
|
119
113
|
this.offset += 4;
|
|
120
|
-
return
|
|
121
|
-
}
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
writeUInt64(value) {
|
|
118
|
+
if (typeof value !== 'bigint') {
|
|
119
|
+
throw new Error('value must be a BigInt');
|
|
120
|
+
}
|
|
121
|
+
const buf = Buffer.allocUnsafe(8);
|
|
122
|
+
buf.writeUInt32BE(Number(value >> 32n), 0);
|
|
123
|
+
buf.writeUInt32BE(Number(value & 0xFFFFFFFFn), 4);
|
|
124
|
+
this.add(buf);
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
122
127
|
|
|
123
128
|
readUInt64() {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
const high = this.packet.readUInt32BE(this.offset);
|
|
130
|
+
const low = this.packet.readUInt32BE(this.offset + 4);
|
|
131
|
+
this.offset += 8;
|
|
132
|
+
return (BigInt(high) << 32n) | BigInt(low);
|
|
133
|
+
}
|
|
127
134
|
|
|
128
135
|
toBuffer() {
|
|
129
136
|
return this.packet;
|
|
130
|
-
}
|
|
137
|
+
}
|
|
131
138
|
|
|
132
139
|
add(data) {
|
|
133
140
|
if (!Buffer.isBuffer(data)) {
|
|
134
141
|
throw new Error('Data must be a Buffer object');
|
|
135
142
|
}
|
|
136
|
-
|
|
137
143
|
this.packet = Buffer.concat([this.packet, data]);
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
144
|
+
this.totalLength = this.packet.length;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export default Structure;
|
|
@@ -30,6 +30,7 @@ class XboxLocalApi extends EventEmitter {
|
|
|
30
30
|
this.channels = [];
|
|
31
31
|
this.channelRequestId = 0;
|
|
32
32
|
this.mediaRequestId = 0;
|
|
33
|
+
this.fragments = {};
|
|
33
34
|
|
|
34
35
|
//create impulse generator
|
|
35
36
|
this.impulseGenerator = new ImpulseGenerator();
|
|
@@ -205,8 +206,8 @@ class XboxLocalApi extends EventEmitter {
|
|
|
205
206
|
};
|
|
206
207
|
|
|
207
208
|
const channelRequestId = LocalApi.Channels.System[channelName].Id
|
|
208
|
-
const channelCommunicationId = this.channels[
|
|
209
|
-
const channelOpen = this.channels[
|
|
209
|
+
const channelCommunicationId = this.channels[channelRequestId]?.id ?? -1;
|
|
210
|
+
const channelOpen = this.channels[channelRequestId]?.open ?? false;
|
|
210
211
|
|
|
211
212
|
if (channelCommunicationId === -1 || !channelOpen) {
|
|
212
213
|
this.emit('warn', `Channel Id: ${channelCommunicationId}, state: ${channelOpen ? 'Open' : 'Closed'}, trying to open it`);
|
|
@@ -278,91 +279,6 @@ class XboxLocalApi extends EventEmitter {
|
|
|
278
279
|
return true;
|
|
279
280
|
};
|
|
280
281
|
|
|
281
|
-
// Dodaj do klasy XboxLocalApi:
|
|
282
|
-
|
|
283
|
-
async sendSystemButton(channelName, command) {
|
|
284
|
-
if (!this.isConnected) {
|
|
285
|
-
this.emit('warn', `Cannot send command, console not connected`);
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const channelInfo = LocalApi.Channels.System[channelName];
|
|
290
|
-
if (!channelInfo) {
|
|
291
|
-
this.emit('warn', `Unknown channel: ${channelName}`);
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
let channel = this.channels.find(c => c.id !== -1);
|
|
296
|
-
if (!channel) {
|
|
297
|
-
// Otwórz kanał jeśli nie istnieje
|
|
298
|
-
await this.channelOpen(channelInfo.Id, channelInfo.Uuid);
|
|
299
|
-
channel = this.channels.find(c => c.id !== -1);
|
|
300
|
-
if (!channel) {
|
|
301
|
-
this.emit('warn', `Failed to open channel: ${channelName}`);
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const channelId = channel.id;
|
|
307
|
-
|
|
308
|
-
switch (channelInfo.Id) {
|
|
309
|
-
case 0: // Gamepad
|
|
310
|
-
await this._sendGamepadCommand(command, channelId);
|
|
311
|
-
break;
|
|
312
|
-
case 1: // TvRemote
|
|
313
|
-
await this._sendTvRemoteCommand(command, channelId);
|
|
314
|
-
break;
|
|
315
|
-
case 2: // Media
|
|
316
|
-
await this._sendMediaCommand(command, channelId);
|
|
317
|
-
break;
|
|
318
|
-
default:
|
|
319
|
-
this.emit('warn', `Unhandled channel type: ${channelName}`);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Prywatne metody do wysyłania komend
|
|
324
|
-
async _sendGamepadCommand(buttons, channelId) {
|
|
325
|
-
const timestamp = Buffer.from(`000${Date.now()}`, 'hex');
|
|
326
|
-
const press = new MessagePacket('gamepad');
|
|
327
|
-
press.set('timestamp', timestamp);
|
|
328
|
-
press.set('buttons', buttons);
|
|
329
|
-
let seq = await this.getSequenceNumber();
|
|
330
|
-
await this.sendSocketMessage(press.pack(this.crypto, seq, this.sourceParticipantId, channelId), 'gamepad');
|
|
331
|
-
|
|
332
|
-
// Release buttons
|
|
333
|
-
setTimeout(async () => {
|
|
334
|
-
const release = new MessagePacket('gamepad');
|
|
335
|
-
release.set('timestamp', timestamp);
|
|
336
|
-
release.set('buttons', LocalApi.Channels.System.Input.Commands.unpress);
|
|
337
|
-
seq = await this.getSequenceNumber();
|
|
338
|
-
await this.sendSocketMessage(release.pack(this.crypto, seq, this.sourceParticipantId, channelId), 'gamepadUnpress');
|
|
339
|
-
}, 150);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
async _sendTvRemoteCommand(command, channelId) {
|
|
343
|
-
const seq = await this.getSequenceNumber();
|
|
344
|
-
const jsonReq = JSON.stringify({
|
|
345
|
-
msgid: `2ed6c0fd.${seq}`,
|
|
346
|
-
request: LocalApi.Channels.System.TvRemote.MessageType.SendKey,
|
|
347
|
-
params: { button_id: command, device_id: null }
|
|
348
|
-
});
|
|
349
|
-
const json = new MessagePacket('json');
|
|
350
|
-
json.set('json', jsonReq);
|
|
351
|
-
await this.sendSocketMessage(json.pack(this.crypto, seq, this.sourceParticipantId, channelId), 'json');
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
async _sendMediaCommand(command, channelId) {
|
|
355
|
-
const requestIdHex = (`0000000000000000${this.mediaRequestId}`).slice(-16);
|
|
356
|
-
const media = new MessagePacket('mediaCommand');
|
|
357
|
-
media.set('requestId', Buffer.from(requestIdHex, 'hex'));
|
|
358
|
-
media.set('titleId', 0);
|
|
359
|
-
media.set('command', command);
|
|
360
|
-
const seq = await this.getSequenceNumber();
|
|
361
|
-
await this.sendSocketMessage(media.pack(this.crypto, seq, this.sourceParticipantId, channelId), 'mediaCommand');
|
|
362
|
-
this.mediaRequestId++;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
|
|
366
282
|
async disconnect() {
|
|
367
283
|
if (this.enableDebugMode) this.emit('debug', 'Disconnecting...');
|
|
368
284
|
|
|
@@ -475,7 +391,7 @@ class XboxLocalApi extends EventEmitter {
|
|
|
475
391
|
switch (type) {
|
|
476
392
|
case 'json':
|
|
477
393
|
// Object to hold fragments
|
|
478
|
-
const fragments =
|
|
394
|
+
const fragments = this.fragments;
|
|
479
395
|
const jsonMessage = JSON.parse(packet.payloadProtected.json);
|
|
480
396
|
const datagramId = jsonMessage.datagramId;
|
|
481
397
|
|
package/src/xboxdevice.js
CHANGED
|
@@ -658,10 +658,7 @@ class XboxDevice extends EventEmitter {
|
|
|
658
658
|
break;
|
|
659
659
|
}
|
|
660
660
|
|
|
661
|
-
|
|
662
|
-
await this.xboxLocalApi.sendSystemButton('Input', LocalApi.Channels.System.Input.Commands[command]);
|
|
663
|
-
//await xboxLocalApi.sendSystemButton('TvRemote', LocalApi.Channels.System.TvRemote.Buttons.Power);
|
|
664
|
-
//await xboxLocalApi.sendSystemButton('Media', LocalApi.Channels.System.Media.Commands[command]);
|
|
661
|
+
await this.xboxWebApi.send(channelName, 'InjectKey', [{ 'keyType': command }]);
|
|
665
662
|
if (!this.disableLogInfo) this.emit('info', `Remote Key: ${command}`);
|
|
666
663
|
} catch (error) {
|
|
667
664
|
this.emit('warn', `set Remote Key error: ${JSON.stringify(error, null, 2)}`);
|