homebridge-sony-audio-extended 0.0.5-test → 0.0.5
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/README.md +36 -27
- package/dist/api.d.ts +64 -60
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +14 -24
- package/dist/api.js.map +1 -1
- package/dist/discoverer.d.ts +3 -4
- package/dist/discoverer.d.ts.map +1 -1
- package/dist/discoverer.js +36 -14
- package/dist/discoverer.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/platform.js +2 -2
- package/dist/platform.js.map +1 -1
- package/dist/sonyAudioAccessory.d.ts +0 -1
- package/dist/sonyAudioAccessory.d.ts.map +1 -1
- package/dist/sonyAudioAccessory.js +73 -61
- package/dist/sonyAudioAccessory.js.map +1 -1
- package/dist/sonyAudioAccessorySettings.d.ts +1 -1
- package/dist/sonyAudioAccessorySettings.d.ts.map +1 -1
- package/dist/sonyAudioAccessorySettings.js +1 -5
- package/dist/sonyAudioAccessorySettings.js.map +1 -1
- package/dist/sonyDevice.d.ts +4 -6
- package/dist/sonyDevice.d.ts.map +1 -1
- package/dist/sonyDevice.js +197 -108
- package/dist/sonyDevice.js.map +1 -1
- package/package.json +5 -5
package/dist/sonyDevice.js
CHANGED
|
@@ -4,12 +4,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.SonyDevice = void 0;
|
|
7
|
-
/* eslint-disable max-len */
|
|
8
|
-
const url_1 = require("url");
|
|
9
7
|
const api_1 = require("./api");
|
|
10
8
|
const axios_1 = __importDefault(require("axios"));
|
|
11
|
-
const ws_1 = __importDefault(require("ws"));
|
|
12
9
|
const events_1 = require("events");
|
|
10
|
+
/* eslint-disable max-len */
|
|
11
|
+
const url_1 = require("url");
|
|
12
|
+
const ws_1 = __importDefault(require("ws"));
|
|
13
13
|
/**
|
|
14
14
|
* Categories of devices supported by this module
|
|
15
15
|
*/
|
|
@@ -17,6 +17,10 @@ const COMPATIBLE_DEVICE_CATEGORIES = [
|
|
|
17
17
|
'homeTheaterSystem',
|
|
18
18
|
'personalAudio', // !not tested
|
|
19
19
|
];
|
|
20
|
+
/**
|
|
21
|
+
* Model names that are explicitly supported even if category doesn't match
|
|
22
|
+
*/
|
|
23
|
+
const COMPATIBLE_DEVICE_MODELS = ['STR-AN1000'];
|
|
20
24
|
/**
|
|
21
25
|
* Devices terminals which hasn't in getCurrentExternalTerminalsStatus api
|
|
22
26
|
* from [here](https://developer.sony.com/develop/audio-control-api/api-references/device-uri)
|
|
@@ -29,7 +33,7 @@ const DEVICE_TERMINALS = [
|
|
|
29
33
|
connection: 'connected',
|
|
30
34
|
title: 'DLNA Music',
|
|
31
35
|
uri: 'dlna:music',
|
|
32
|
-
meta: "meta:pc" /*
|
|
36
|
+
meta: "meta:pc" /* PC */,
|
|
33
37
|
},
|
|
34
38
|
},
|
|
35
39
|
{
|
|
@@ -39,7 +43,7 @@ const DEVICE_TERMINALS = [
|
|
|
39
43
|
connection: 'connected',
|
|
40
44
|
title: 'USB Storage',
|
|
41
45
|
uri: 'storage:usb1',
|
|
42
|
-
meta: "meta:usbdac" /*
|
|
46
|
+
meta: "meta:usbdac" /* USBDAC */,
|
|
43
47
|
},
|
|
44
48
|
},
|
|
45
49
|
{
|
|
@@ -49,7 +53,7 @@ const DEVICE_TERMINALS = [
|
|
|
49
53
|
connection: 'connected',
|
|
50
54
|
title: 'FM Radio',
|
|
51
55
|
uri: 'radio:fm',
|
|
52
|
-
meta: "meta:tuner" /*
|
|
56
|
+
meta: "meta:tuner" /* TUNER */,
|
|
53
57
|
},
|
|
54
58
|
},
|
|
55
59
|
{
|
|
@@ -59,7 +63,7 @@ const DEVICE_TERMINALS = [
|
|
|
59
63
|
connection: 'connected',
|
|
60
64
|
title: 'Audio Network',
|
|
61
65
|
uri: 'netService:audio',
|
|
62
|
-
meta: "meta:source" /*
|
|
66
|
+
meta: "meta:source" /* SOURCE */,
|
|
63
67
|
},
|
|
64
68
|
},
|
|
65
69
|
{
|
|
@@ -69,7 +73,7 @@ const DEVICE_TERMINALS = [
|
|
|
69
73
|
connection: 'connected',
|
|
70
74
|
title: 'Multiroom Audio',
|
|
71
75
|
uri: 'multiroom:audio',
|
|
72
|
-
meta: "meta:source" /*
|
|
76
|
+
meta: "meta:source" /* SOURCE */,
|
|
73
77
|
},
|
|
74
78
|
},
|
|
75
79
|
{
|
|
@@ -79,7 +83,7 @@ const DEVICE_TERMINALS = [
|
|
|
79
83
|
connection: 'connected',
|
|
80
84
|
title: 'Cast Audio',
|
|
81
85
|
uri: 'cast:audio',
|
|
82
|
-
meta: "meta:source" /*
|
|
86
|
+
meta: "meta:source" /* SOURCE */,
|
|
83
87
|
},
|
|
84
88
|
},
|
|
85
89
|
{
|
|
@@ -89,7 +93,7 @@ const DEVICE_TERMINALS = [
|
|
|
89
93
|
connection: 'connected',
|
|
90
94
|
title: 'AirPlay',
|
|
91
95
|
uri: 'extInput:airPlay',
|
|
92
|
-
meta: "meta:btaudio" /*
|
|
96
|
+
meta: "meta:btaudio" /* BTAUDIO */,
|
|
93
97
|
},
|
|
94
98
|
},
|
|
95
99
|
];
|
|
@@ -97,22 +101,15 @@ const RE_EXT_OUTPUT = new RegExp('extOutput:*');
|
|
|
97
101
|
const SUBSCRIBE_NOTIFICATIONS = [
|
|
98
102
|
{
|
|
99
103
|
service: 'system',
|
|
100
|
-
notifications: [
|
|
101
|
-
"notifyPowerStatus" /* NotificationMethods.POWER */,
|
|
102
|
-
],
|
|
104
|
+
notifications: ["notifyPowerStatus" /* POWER */],
|
|
103
105
|
},
|
|
104
106
|
{
|
|
105
107
|
service: 'audio',
|
|
106
|
-
notifications: [
|
|
107
|
-
"notifyVolumeInformation" /* NotificationMethods.VOLUME */,
|
|
108
|
-
],
|
|
108
|
+
notifications: ["notifyVolumeInformation" /* VOLUME */],
|
|
109
109
|
},
|
|
110
110
|
{
|
|
111
111
|
service: 'avContent',
|
|
112
|
-
notifications: [
|
|
113
|
-
"notifyExternalTerminalStatus" /* NotificationMethods.TERMINAL */,
|
|
114
|
-
"notifyPlayingContentInfo" /* NotificationMethods.CONTENT */,
|
|
115
|
-
],
|
|
112
|
+
notifications: ["notifyExternalTerminalStatus" /* TERMINAL */, "notifyPlayingContentInfo" /* CONTENT */],
|
|
116
113
|
},
|
|
117
114
|
];
|
|
118
115
|
const RECONNECT_TIMEOUT = 5000;
|
|
@@ -169,7 +166,7 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
169
166
|
this.axiosInstanceSoap = axios_1.default.create({
|
|
170
167
|
baseURL: this.upnpUrl.href,
|
|
171
168
|
headers: {
|
|
172
|
-
|
|
169
|
+
SOAPACTION: '"urn:schemas-sony-com:service:IRCC:1#X_SendIRCC"',
|
|
173
170
|
'Content-Type': 'text/xml; charset="utf-8"',
|
|
174
171
|
},
|
|
175
172
|
timeout: REQUEST_TIMEOUT,
|
|
@@ -184,9 +181,11 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
184
181
|
* Return device id
|
|
185
182
|
*/
|
|
186
183
|
getDeviceID() {
|
|
187
|
-
return this.systemInfo.serial !== ''
|
|
188
|
-
|
|
189
|
-
|
|
184
|
+
return this.systemInfo.serial !== ''
|
|
185
|
+
? this.systemInfo.serial
|
|
186
|
+
: this.systemInfo.macAddr !== ''
|
|
187
|
+
? this.systemInfo.macAddr
|
|
188
|
+
: this.systemInfo.wirelessMacAddr;
|
|
190
189
|
}
|
|
191
190
|
/**
|
|
192
191
|
* Checks the request for API version compliance
|
|
@@ -195,9 +194,9 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
195
194
|
var _a;
|
|
196
195
|
const version = request.version;
|
|
197
196
|
const method = request.method;
|
|
198
|
-
const reqApis = (_a = this.apisInfo) === null || _a === void 0 ? void 0 : _a.find(a => a.service === service);
|
|
199
|
-
const reqApi = reqApis === null || reqApis === void 0 ? void 0 : reqApis.apis.find(m => m.name === method);
|
|
200
|
-
const validVersions = reqApi === null || reqApi === void 0 ? void 0 : reqApi.versions.map(x => x.version);
|
|
197
|
+
const reqApis = (_a = this.apisInfo) === null || _a === void 0 ? void 0 : _a.find((a) => a.service === service);
|
|
198
|
+
const reqApi = reqApis === null || reqApis === void 0 ? void 0 : reqApis.apis.find((m) => m.name === method);
|
|
199
|
+
const validVersions = reqApi === null || reqApi === void 0 ? void 0 : reqApi.versions.map((x) => x.version);
|
|
201
200
|
if (validVersions) {
|
|
202
201
|
return validVersions.includes(version);
|
|
203
202
|
}
|
|
@@ -206,31 +205,125 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
206
205
|
}
|
|
207
206
|
}
|
|
208
207
|
async getExternalTerminals() {
|
|
209
|
-
var _a
|
|
208
|
+
var _a;
|
|
210
209
|
if (!this._externalTerminals) {
|
|
211
210
|
// get the terminals info from device
|
|
212
|
-
const
|
|
213
|
-
let currentExternalTerminalsVersion = null;
|
|
214
|
-
const api = serviceApiInfo === null || serviceApiInfo === void 0 ? void 0 : serviceApiInfo.apis.find(api => api.name === 'getCurrentExternalTerminalsStatus');
|
|
215
|
-
if (api) {
|
|
216
|
-
currentExternalTerminalsVersion = ((_b = (_a = api.versions) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.version) || null;
|
|
217
|
-
}
|
|
218
|
-
const resTerminals = currentExternalTerminalsVersion === '1.2'
|
|
219
|
-
? await this.axiosInstance.post('/avContent', JSON.stringify(api_1.ApiRequestCurrentExternalTerminalsStatusv1_2))
|
|
220
|
-
: await this.axiosInstance.post('/avContent', JSON.stringify(api_1.ApiRequestCurrentExternalTerminalsStatusv1_0));
|
|
211
|
+
const resTerminals = await this.axiosInstance.post('/avContent', JSON.stringify(api_1.ApiRequestCurrentExternalTerminalsStatus));
|
|
221
212
|
const terminals = resTerminals.data;
|
|
222
213
|
this._externalTerminals = terminals.result[0];
|
|
214
|
+
// Try to get all sources using getSourceList API to find missing inputs
|
|
215
|
+
try {
|
|
216
|
+
if (this.validateRequest('avContent', api_1.ApiRequestGetSourceList)) {
|
|
217
|
+
const resSourceList = await this.axiosInstance.post('/avContent', JSON.stringify(api_1.ApiRequestGetSourceList));
|
|
218
|
+
const sourceList = resSourceList.data;
|
|
219
|
+
const sources = sourceList.result[0];
|
|
220
|
+
// Add sources that are not already in terminals
|
|
221
|
+
sources.forEach((source) => {
|
|
222
|
+
var _a, _b;
|
|
223
|
+
// Skip if it's an output (zone)
|
|
224
|
+
if (RE_EXT_OUTPUT.test(source.uri)) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
// Check if this source already exists
|
|
228
|
+
const exists = (_a = this._externalTerminals) === null || _a === void 0 ? void 0 : _a.some((t) => t.uri === source.uri);
|
|
229
|
+
if (!exists) {
|
|
230
|
+
// Map meta string to TerminalTypeMeta enum
|
|
231
|
+
let meta = "" /* NO_INFO */;
|
|
232
|
+
if (source.meta) {
|
|
233
|
+
// Direct mapping for common meta types
|
|
234
|
+
if (source.meta === 'meta:tv') {
|
|
235
|
+
meta = "meta:tv" /* TV */;
|
|
236
|
+
}
|
|
237
|
+
else if (source.meta === 'meta:video') {
|
|
238
|
+
meta = "meta:video" /* VIDEO */;
|
|
239
|
+
}
|
|
240
|
+
else if (source.meta === 'meta:hdmi') {
|
|
241
|
+
meta = "meta:hdmi" /* HDMI */;
|
|
242
|
+
}
|
|
243
|
+
else if (source.meta === 'meta:sat-catv') {
|
|
244
|
+
meta = "meta:sat-catv" /* SAT_CATV */;
|
|
245
|
+
}
|
|
246
|
+
else if (source.meta === 'meta:game') {
|
|
247
|
+
meta = "meta:game" /* GAME */;
|
|
248
|
+
}
|
|
249
|
+
else if (source.meta === 'meta:btaudio') {
|
|
250
|
+
meta = "meta:btaudio" /* BTAUDIO */;
|
|
251
|
+
}
|
|
252
|
+
else if (source.meta === 'meta:source') {
|
|
253
|
+
meta = "meta:source" /* SOURCE */;
|
|
254
|
+
}
|
|
255
|
+
else if (source.meta === 'meta:pc') {
|
|
256
|
+
meta = "meta:pc" /* PC */;
|
|
257
|
+
}
|
|
258
|
+
else if (source.meta === 'meta:optical') {
|
|
259
|
+
meta = "meta:optical" /* OPTICAL */;
|
|
260
|
+
}
|
|
261
|
+
else if (source.meta === 'meta:coaxial') {
|
|
262
|
+
meta = "meta:coaxial" /* COAXIAL */;
|
|
263
|
+
}
|
|
264
|
+
else if (source.meta === 'meta:line') {
|
|
265
|
+
meta = "meta:line" /* LINE */;
|
|
266
|
+
}
|
|
267
|
+
else if (source.meta === 'meta:bd-dvd') {
|
|
268
|
+
meta = "meta:bd-dvd" /* BD_DVD */;
|
|
269
|
+
}
|
|
270
|
+
else if (source.meta === 'meta:sacd-cd') {
|
|
271
|
+
meta = "meta:sacd-cd" /* SACD_CD */;
|
|
272
|
+
}
|
|
273
|
+
else if (source.meta === 'meta:tuner') {
|
|
274
|
+
meta = "meta:tuner" /* TUNER */;
|
|
275
|
+
}
|
|
276
|
+
else if (source.meta === 'meta:usbdac') {
|
|
277
|
+
meta = "meta:usbdac" /* USBDAC */;
|
|
278
|
+
}
|
|
279
|
+
else if (source.meta === 'meta:storage') {
|
|
280
|
+
meta = "meta:usbdac" /* USBDAC */;
|
|
281
|
+
} // storage often uses usbdac meta
|
|
282
|
+
}
|
|
283
|
+
(_b = this._externalTerminals) === null || _b === void 0 ? void 0 : _b.push({
|
|
284
|
+
connection: 'connected',
|
|
285
|
+
title: source.title || source.uri,
|
|
286
|
+
uri: source.uri,
|
|
287
|
+
meta: meta,
|
|
288
|
+
active: 'inactive',
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch (err) {
|
|
295
|
+
this.log.debug('getSourceList API not available or failed, skipping');
|
|
296
|
+
}
|
|
223
297
|
// add other terminals
|
|
224
298
|
const schemes = await this.getSchemes();
|
|
225
|
-
DEVICE_TERMINALS.forEach(t => {
|
|
226
|
-
var _a, _b;
|
|
299
|
+
DEVICE_TERMINALS.forEach((t) => {
|
|
300
|
+
var _a, _b, _c, _d;
|
|
227
301
|
if (t.readonly) {
|
|
228
|
-
if (schemes.includes(t.scheme)) {
|
|
229
|
-
|
|
302
|
+
if (schemes.includes(t.scheme)) {
|
|
303
|
+
// device support that input terminal
|
|
304
|
+
const exists = (_a = this._externalTerminals) === null || _a === void 0 ? void 0 : _a.some((term) => term.uri === t.terminal.uri);
|
|
305
|
+
if (!exists) {
|
|
306
|
+
(_b = this._externalTerminals) === null || _b === void 0 ? void 0 : _b.push(t.terminal);
|
|
307
|
+
}
|
|
230
308
|
}
|
|
231
309
|
}
|
|
232
|
-
else {
|
|
233
|
-
|
|
310
|
+
else {
|
|
311
|
+
// add all non-readonly terminals
|
|
312
|
+
const exists = (_c = this._externalTerminals) === null || _c === void 0 ? void 0 : _c.some((term) => term.uri === t.terminal.uri);
|
|
313
|
+
if (!exists) {
|
|
314
|
+
(_d = this._externalTerminals) === null || _d === void 0 ? void 0 : _d.push(t.terminal);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
// Add auto-naming for zones (outputs)
|
|
319
|
+
(_a = this._externalTerminals) === null || _a === void 0 ? void 0 : _a.forEach((terminal) => {
|
|
320
|
+
if (RE_EXT_OUTPUT.test(terminal.uri)) {
|
|
321
|
+
// Extract zone number from URI like "extOutput:zone?zone=1"
|
|
322
|
+
const zoneMatch = terminal.uri.match(/zone=(\d+)/);
|
|
323
|
+
if (zoneMatch && !terminal.title) {
|
|
324
|
+
const zoneNumber = zoneMatch[1];
|
|
325
|
+
terminal.title = `Zone ${zoneNumber}`;
|
|
326
|
+
}
|
|
234
327
|
}
|
|
235
328
|
});
|
|
236
329
|
}
|
|
@@ -240,7 +333,7 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
240
333
|
* Terminal is read-only and cannot be select by the user?
|
|
241
334
|
*/
|
|
242
335
|
isReadonlyTerminal(terminal) {
|
|
243
|
-
const readonlyTerminalsUri = DEVICE_TERMINALS.find(t => !t.readonly && t.terminal.uri === terminal.uri);
|
|
336
|
+
const readonlyTerminalsUri = DEVICE_TERMINALS.find((t) => !t.readonly && t.terminal.uri === terminal.uri);
|
|
244
337
|
return !!readonlyTerminalsUri;
|
|
245
338
|
}
|
|
246
339
|
async getVolumeInformation() {
|
|
@@ -269,7 +362,8 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
269
362
|
}
|
|
270
363
|
const res = await this.axiosInstance.post('/' + service, JSON.stringify(req));
|
|
271
364
|
const playingInfo = res.data;
|
|
272
|
-
if (playingInfo.result[0].length === 1) {
|
|
365
|
+
if (playingInfo.result[0].length === 1) {
|
|
366
|
+
// info with only one zone
|
|
273
367
|
return this.getTerminalBySource(playingInfo.result[0][0].source || playingInfo.result[0][0].uri);
|
|
274
368
|
}
|
|
275
369
|
return null; // no active zone
|
|
@@ -279,7 +373,7 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
279
373
|
*/
|
|
280
374
|
async getInputs() {
|
|
281
375
|
const exTerminals = await this.getExternalTerminals();
|
|
282
|
-
const inputs = exTerminals === null || exTerminals === void 0 ? void 0 : exTerminals.filter(t => !RE_EXT_OUTPUT.test(t.uri));
|
|
376
|
+
const inputs = exTerminals === null || exTerminals === void 0 ? void 0 : exTerminals.filter((t) => !RE_EXT_OUTPUT.test(t.uri));
|
|
283
377
|
return inputs ? inputs : [];
|
|
284
378
|
}
|
|
285
379
|
/**
|
|
@@ -287,7 +381,7 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
287
381
|
*/
|
|
288
382
|
async getZones() {
|
|
289
383
|
const exTerminals = await this.getExternalTerminals();
|
|
290
|
-
const inputs = exTerminals === null || exTerminals === void 0 ? void 0 : exTerminals.filter(t => RE_EXT_OUTPUT.test(t.uri));
|
|
384
|
+
const inputs = exTerminals === null || exTerminals === void 0 ? void 0 : exTerminals.filter((t) => RE_EXT_OUTPUT.test(t.uri));
|
|
291
385
|
return inputs ? inputs : null;
|
|
292
386
|
}
|
|
293
387
|
/**
|
|
@@ -301,7 +395,7 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
301
395
|
return null;
|
|
302
396
|
}
|
|
303
397
|
else {
|
|
304
|
-
const activeZone = zones.find(zone => zone.active === 'active');
|
|
398
|
+
const activeZone = zones.find((zone) => zone.active === 'active');
|
|
305
399
|
return activeZone ? activeZone : null;
|
|
306
400
|
}
|
|
307
401
|
}
|
|
@@ -311,7 +405,7 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
311
405
|
async getSchemes() {
|
|
312
406
|
const resSchemes = await this.axiosInstance.post('/avContent', JSON.stringify(api_1.ApiRequestGetSchemeList));
|
|
313
407
|
const schemes = resSchemes.data;
|
|
314
|
-
return schemes.result[0].map(s => s.scheme);
|
|
408
|
+
return schemes.result[0].map((s) => s.scheme);
|
|
315
409
|
}
|
|
316
410
|
/**
|
|
317
411
|
* Check the API response for returned error
|
|
@@ -351,7 +445,6 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
351
445
|
* Get info about supported api and system
|
|
352
446
|
*/
|
|
353
447
|
static async createDevice(baseUrl, upnpUrl, udn, log) {
|
|
354
|
-
var _a, _b;
|
|
355
448
|
const axiosInstance = axios_1.default.create({
|
|
356
449
|
baseURL: baseUrl.href,
|
|
357
450
|
headers: { 'content-type': 'application/json' },
|
|
@@ -362,27 +455,27 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
362
455
|
// Checks the device against a compatible category of the device
|
|
363
456
|
const resInterfaceInfo = await axiosInstance.post('/system', JSON.stringify(api_1.ApiRequestGetInterfaceInformation));
|
|
364
457
|
const interfaceInfo = resInterfaceInfo.data;
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
458
|
+
const productCategory = interfaceInfo.result[0].productCategory;
|
|
459
|
+
const modelName = interfaceInfo.result[0].modelName;
|
|
460
|
+
// Log device information for debugging
|
|
461
|
+
log.debug(`Device info - Model: "${modelName}", Category: "${productCategory}"`);
|
|
462
|
+
// Check if device is compatible by category or by model name
|
|
463
|
+
const isCompatibleCategory = COMPATIBLE_DEVICE_CATEGORIES.includes(productCategory);
|
|
464
|
+
const isCompatibleModel = COMPATIBLE_DEVICE_MODELS.some((model) => modelName && modelName.toUpperCase().includes(model.toUpperCase()));
|
|
465
|
+
if (!isCompatibleCategory && !isCompatibleModel) {
|
|
466
|
+
// device has an incompatible category and is not in the compatible models list
|
|
467
|
+
throw new api_1.IncompatibleDeviceCategoryError(`Device at ${baseUrl.href} has an incompatible category "${productCategory}" and model "${modelName}" is not in the supported models list`);
|
|
368
468
|
}
|
|
369
469
|
const resApiInfo = await axiosInstance.post('/guide', JSON.stringify(api_1.ApiRequestSupportedApiInfo));
|
|
370
470
|
const apisInfo = resApiInfo.data.result[0];
|
|
371
|
-
let systemInformationVersion = null;
|
|
372
|
-
const serviceApiInfo = apisInfo.find(v => v.service === 'system');
|
|
373
|
-
const api = serviceApiInfo.apis.find(api => api.name === 'getSystemInformation');
|
|
374
|
-
if (api) {
|
|
375
|
-
systemInformationVersion = ((_b = (_a = api.versions) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.version) || null;
|
|
376
|
-
}
|
|
377
|
-
const ApiRequestSystemInformation = systemInformationVersion === '1.6' ? api_1.ApiRequestSystemInformationv1_6 : api_1.ApiRequestSystemInformationv1_4;
|
|
378
471
|
const device = new SonyDevice(baseUrl, upnpUrl, udn, apisInfo, log);
|
|
379
472
|
// Gets general system information for the device.
|
|
380
473
|
// check the request for API version compliance
|
|
381
474
|
const service = 'system';
|
|
382
|
-
if (!device.validateRequest(service, ApiRequestSystemInformation)) {
|
|
475
|
+
if (!device.validateRequest(service, api_1.ApiRequestSystemInformation)) {
|
|
383
476
|
throw new api_1.UnsupportedVersionApiError(`The specified api version is not supported by the device ${baseUrl.hostname}`);
|
|
384
477
|
}
|
|
385
|
-
const resSystemInfo = await axiosInstance.post('/' + service, JSON.stringify(ApiRequestSystemInformation));
|
|
478
|
+
const resSystemInfo = await axiosInstance.post('/' + service, JSON.stringify(api_1.ApiRequestSystemInformation));
|
|
386
479
|
const systemInfo = resSystemInfo.data.result[0];
|
|
387
480
|
Object.assign(device.systemInfo, systemInfo);
|
|
388
481
|
device.subscribe();
|
|
@@ -392,7 +485,7 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
392
485
|
* Initialize notifications for given events
|
|
393
486
|
*/
|
|
394
487
|
subscribe() {
|
|
395
|
-
SUBSCRIBE_NOTIFICATIONS.forEach(subscriber => this.createWebSocket(subscriber.service));
|
|
488
|
+
SUBSCRIBE_NOTIFICATIONS.forEach((subscriber) => this.createWebSocket(subscriber.service));
|
|
396
489
|
}
|
|
397
490
|
/**
|
|
398
491
|
* Disable all notifications subscriptions and close websocket connections
|
|
@@ -449,24 +542,27 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
449
542
|
ws.on('open', () => {
|
|
450
543
|
this.log.debug(`Device ${this.systemInfo.name} opened a socked ${url.href}`);
|
|
451
544
|
heartbeat(this);
|
|
452
|
-
// To get current notification settings, send an empty 'switchNotifications'
|
|
545
|
+
// To get current notification settings, send an empty 'switchNotifications'
|
|
453
546
|
// message with an ID of '1'
|
|
454
547
|
ws.send(JSON.stringify(this.switchNotifications(1, [], [])));
|
|
455
548
|
});
|
|
456
549
|
ws.on('message', (data) => {
|
|
457
550
|
const response = JSON.parse(data);
|
|
458
551
|
if ('id' in response) {
|
|
459
|
-
if (response.id === 1) {
|
|
552
|
+
if (response.id === 1) {
|
|
553
|
+
// enable notification
|
|
460
554
|
this.log.debug(`Device ${this.systemInfo.name} received initial message ${data}`);
|
|
461
555
|
const enabled = []; // response.result[0].enabled;
|
|
462
556
|
const disabled = []; // response.result[0].disabled;
|
|
463
|
-
const shouldEnabled = SUBSCRIBE_NOTIFICATIONS.filter(s => s.service === service)[0].notifications;
|
|
557
|
+
const shouldEnabled = SUBSCRIBE_NOTIFICATIONS.filter((s) => s.service === service)[0].notifications;
|
|
464
558
|
// if shouldEnabled equal to returned enabled, this mean what nothing to do. All ok
|
|
465
559
|
if (shouldEnabled.length === enabled.length) {
|
|
466
560
|
return;
|
|
467
561
|
}
|
|
468
562
|
// else we need to resubscribe
|
|
469
|
-
const all_notifications = [...response.result[0].enabled].concat([
|
|
563
|
+
const all_notifications = [...response.result[0].enabled].concat([
|
|
564
|
+
...response.result[0].disabled,
|
|
565
|
+
]);
|
|
470
566
|
for (let i = 0; i < all_notifications.length; i++) {
|
|
471
567
|
const item = all_notifications[i];
|
|
472
568
|
if (shouldEnabled.includes(item.name)) {
|
|
@@ -476,14 +572,16 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
476
572
|
disabled.push(item);
|
|
477
573
|
}
|
|
478
574
|
}
|
|
479
|
-
if (shouldEnabled.length !== enabled.length) {
|
|
575
|
+
if (shouldEnabled.length !== enabled.length) {
|
|
576
|
+
// something wrong... or not. For example HT-ZF9 hasn't a notifyExternalTerminalStatus. See #1
|
|
480
577
|
this.log.debug(`Device ${this.systemInfo.name} does not have the required notifier. Should be ${JSON.stringify(shouldEnabled)}, but found ${JSON.stringify(enabled)}`);
|
|
481
578
|
}
|
|
482
579
|
this.log.debug(`Device ${this.systemInfo.name} sent subscribe message ${JSON.stringify(this.switchNotifications(2, disabled, enabled))}`);
|
|
483
|
-
ws['subscriptionCommand'] = JSON.stringify(this.switchNotifications(2, disabled
|
|
580
|
+
ws['subscriptionCommand'] = JSON.stringify(this.switchNotifications(2, disabled, enabled));
|
|
484
581
|
ws.send(ws['subscriptionCommand']);
|
|
485
582
|
}
|
|
486
|
-
else if (response.id === 100) {
|
|
583
|
+
else if (response.id === 100) {
|
|
584
|
+
// unsubscribe from notifications
|
|
487
585
|
clearInterval(ws['heartbeat']);
|
|
488
586
|
clearTimeout(ws['heartbeatTimeout']);
|
|
489
587
|
ws.terminate();
|
|
@@ -492,11 +590,12 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
492
590
|
this.log.debug(`Device ${this.systemInfo.name} received subscription status ${data}`);
|
|
493
591
|
if (this.emitRestoreEvent) {
|
|
494
592
|
this.emitRestoreEvent = false;
|
|
495
|
-
this.emit("restore" /*
|
|
593
|
+
this.emit("restore" /* RESTORE */);
|
|
496
594
|
}
|
|
497
595
|
}
|
|
498
596
|
}
|
|
499
|
-
else {
|
|
597
|
+
else {
|
|
598
|
+
// here handle received notification
|
|
500
599
|
this.log.debug(`Device ${this.systemInfo.name} received notification ${data}`);
|
|
501
600
|
this.handleNotificationMessage(response);
|
|
502
601
|
}
|
|
@@ -533,10 +632,12 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
533
632
|
return {
|
|
534
633
|
method: 'switchNotifications',
|
|
535
634
|
id: id,
|
|
536
|
-
params: [
|
|
635
|
+
params: [
|
|
636
|
+
{
|
|
537
637
|
disabled: disable,
|
|
538
638
|
enabled: enable,
|
|
539
|
-
}
|
|
639
|
+
},
|
|
640
|
+
],
|
|
540
641
|
version: '1.0',
|
|
541
642
|
};
|
|
542
643
|
}
|
|
@@ -546,7 +647,7 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
546
647
|
* @returns
|
|
547
648
|
*/
|
|
548
649
|
getAvailibleNotifications(service) {
|
|
549
|
-
const serviceApiInfo = this.apisInfo.find(v => v.service === service);
|
|
650
|
+
const serviceApiInfo = this.apisInfo.find((v) => v.service === service);
|
|
550
651
|
if (!serviceApiInfo) {
|
|
551
652
|
return [];
|
|
552
653
|
}
|
|
@@ -570,46 +671,50 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
570
671
|
*/
|
|
571
672
|
handleNotificationMessage(message) {
|
|
572
673
|
switch (message.method) {
|
|
573
|
-
case "notifyPowerStatus" /*
|
|
674
|
+
case "notifyPowerStatus" /* POWER */: {
|
|
574
675
|
const msg = message;
|
|
575
676
|
const power = msg.params[0].status === 'active';
|
|
576
|
-
this.emit("power" /*
|
|
677
|
+
this.emit("power" /* POWER */, power);
|
|
577
678
|
break;
|
|
578
679
|
}
|
|
579
|
-
case "notifyVolumeInformation" /*
|
|
680
|
+
case "notifyVolumeInformation" /* VOLUME */: {
|
|
580
681
|
const msg = message;
|
|
581
682
|
const volumeInfo = msg.params[0];
|
|
582
683
|
// update _volumeInformation
|
|
583
684
|
if (this._volumeInformation) {
|
|
584
|
-
const volumeIdx = this._volumeInformation.findIndex(v => v.output === volumeInfo.output);
|
|
685
|
+
const volumeIdx = this._volumeInformation.findIndex((v) => v.output === volumeInfo.output);
|
|
585
686
|
if (volumeIdx !== -1) {
|
|
586
687
|
Object.assign(this._volumeInformation[volumeIdx], volumeInfo);
|
|
587
688
|
}
|
|
588
689
|
}
|
|
589
|
-
const mute = volumeInfo.mute === 'on'
|
|
690
|
+
const mute = volumeInfo.mute === 'on'
|
|
691
|
+
? true
|
|
692
|
+
: volumeInfo.mute === 'off'
|
|
693
|
+
? false
|
|
694
|
+
: null;
|
|
590
695
|
const volume = volumeInfo.volume;
|
|
591
696
|
if (volume !== -1) {
|
|
592
|
-
this.emit("volume" /*
|
|
697
|
+
this.emit("volume" /* VOLUME */, volume);
|
|
593
698
|
}
|
|
594
699
|
if (mute !== null) {
|
|
595
|
-
this.emit("mute" /*
|
|
700
|
+
this.emit("mute" /* MUTE */, mute);
|
|
596
701
|
}
|
|
597
702
|
break;
|
|
598
703
|
}
|
|
599
|
-
case "notifyPlayingContentInfo" /*
|
|
704
|
+
case "notifyPlayingContentInfo" /* CONTENT */: {
|
|
600
705
|
// receive like {"method":"notifyPlayingContentInfo","params":[{"contentKind":"input","output":"extOutput:zone?zone=1","source":"extInput:video?port=1","uri":"extInput:video?port=1"}],"version":"1.0"}
|
|
601
706
|
const msg = message;
|
|
602
707
|
const source = msg.params[0].source || msg.params[0].uri; // maybe overcheck
|
|
603
|
-
this.emit("source" /*
|
|
708
|
+
this.emit("source" /* SOURCE */, source);
|
|
604
709
|
break;
|
|
605
710
|
}
|
|
606
|
-
case "notifyExternalTerminalStatus" /*
|
|
711
|
+
case "notifyExternalTerminalStatus" /* TERMINAL */: {
|
|
607
712
|
// receive like {"method":"notifyExternalTerminalStatus","params":[{"active":"active","connection":"connected","label":"","uri":"extOutput:zone?zone=1"}],"version":"1.0"}
|
|
608
713
|
const msg = message;
|
|
609
714
|
// update _externalTerminals
|
|
610
|
-
msg.params.forEach(updateTerminal => {
|
|
715
|
+
msg.params.forEach((updateTerminal) => {
|
|
611
716
|
if (this._externalTerminals) {
|
|
612
|
-
const terminalIdx = this._externalTerminals.findIndex(t => t.uri === updateTerminal.uri);
|
|
717
|
+
const terminalIdx = this._externalTerminals.findIndex((t) => t.uri === updateTerminal.uri);
|
|
613
718
|
if (terminalIdx !== -1) {
|
|
614
719
|
Object.assign(this._externalTerminals[terminalIdx], updateTerminal);
|
|
615
720
|
}
|
|
@@ -621,7 +726,7 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
621
726
|
// if the device is turning off from an external source, a notivication about power doesn't sends.
|
|
622
727
|
// so, force the power status check
|
|
623
728
|
this.getPowerState().then((active) => {
|
|
624
|
-
this.emit("power" /*
|
|
729
|
+
this.emit("power" /* POWER */, active);
|
|
625
730
|
});
|
|
626
731
|
break;
|
|
627
732
|
}
|
|
@@ -639,7 +744,7 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
639
744
|
if (this._externalTerminals === null) {
|
|
640
745
|
return null;
|
|
641
746
|
}
|
|
642
|
-
const terminals = this._externalTerminals.filter(terminal => terminal.uri === source);
|
|
747
|
+
const terminals = this._externalTerminals.filter((terminal) => terminal.uri === source);
|
|
643
748
|
if (terminals.length !== 0) {
|
|
644
749
|
return terminals[0];
|
|
645
750
|
}
|
|
@@ -655,7 +760,8 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
655
760
|
const service = 'system';
|
|
656
761
|
const resPowerInfo = await this.axiosInstance.post('/' + service, JSON.stringify(api_1.ApiRequestGetPowerStatus));
|
|
657
762
|
const powerInfo = resPowerInfo.data;
|
|
658
|
-
return powerInfo.result[0].status === 'activating' ||
|
|
763
|
+
return (powerInfo.result[0].status === 'activating' ||
|
|
764
|
+
powerInfo.result[0].status === 'active');
|
|
659
765
|
}
|
|
660
766
|
/**
|
|
661
767
|
* Get current volume state with device volume settings
|
|
@@ -667,7 +773,7 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
667
773
|
const volumeInfo = resVolumeInfo.data;
|
|
668
774
|
const activeZone = await this.getActiveZone();
|
|
669
775
|
if (activeZone) {
|
|
670
|
-
const volumeActiveZone = volumeInfo.result[0].find(vi => vi.output === activeZone.uri);
|
|
776
|
+
const volumeActiveZone = volumeInfo.result[0].find((vi) => vi.output === activeZone.uri);
|
|
671
777
|
if (volumeActiveZone) {
|
|
672
778
|
return volumeActiveZone;
|
|
673
779
|
}
|
|
@@ -697,23 +803,6 @@ class SonyDevice extends events_1.EventEmitter {
|
|
|
697
803
|
await this.axiosInstance.post('/' + service, JSON.stringify(reqSetVolume));
|
|
698
804
|
return volumeSelector;
|
|
699
805
|
}
|
|
700
|
-
async setVolumeAbsolute(value) {
|
|
701
|
-
const service = 'audio';
|
|
702
|
-
const zone = await this.getActiveZone();
|
|
703
|
-
const reqSetVolume = {
|
|
704
|
-
id: 98,
|
|
705
|
-
method: 'setAudioVolume',
|
|
706
|
-
params: [
|
|
707
|
-
{
|
|
708
|
-
output: zone ? zone.uri : '',
|
|
709
|
-
volume: String(value),
|
|
710
|
-
},
|
|
711
|
-
],
|
|
712
|
-
version: '1.1',
|
|
713
|
-
};
|
|
714
|
-
await this.axiosInstance.post('/' + service, JSON.stringify(reqSetVolume));
|
|
715
|
-
return value;
|
|
716
|
-
}
|
|
717
806
|
/**
|
|
718
807
|
* Sets the power status of the device.
|
|
719
808
|
* @param power
|