homebridge-xbox-tv 3.7.1 → 3.7.2
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/homebridge-ui/public/index.html +1 -1
- package/package.json +1 -1
- package/src/webApi/xboxwebapi.js +3 -1
- package/src/xboxdevice.js +100 -130
package/package.json
CHANGED
package/src/webApi/xboxwebapi.js
CHANGED
|
@@ -250,12 +250,14 @@ class XboxWebApi extends EventEmitter {
|
|
|
250
250
|
'contentType': contentType
|
|
251
251
|
}
|
|
252
252
|
appsArray.push(input);
|
|
253
|
-
this.emit('addRemoveOrUpdateInput', input, false);
|
|
254
253
|
}
|
|
255
254
|
|
|
256
255
|
//save inputs
|
|
257
256
|
await this.saveData(this.inputsFile, appsArray);
|
|
258
257
|
|
|
258
|
+
//emit apps
|
|
259
|
+
this.emit('addRemoveOrUpdateInput', appsArray, false);
|
|
260
|
+
|
|
259
261
|
//emit restFul and mqtt
|
|
260
262
|
this.emit('restFul', 'apps', apps);
|
|
261
263
|
this.emit('mqtt', 'Apps', apps);
|
package/src/xboxdevice.js
CHANGED
|
@@ -104,7 +104,6 @@ class XboxDevice extends EventEmitter {
|
|
|
104
104
|
this.buttonsConfiguredCount = this.buttonsConfigured.length || 0;
|
|
105
105
|
|
|
106
106
|
//variable
|
|
107
|
-
this.allServices = [];
|
|
108
107
|
this.sensorsInputsServices = [];
|
|
109
108
|
this.buttonsServices = [];
|
|
110
109
|
this.inputIdentifier = 1;
|
|
@@ -348,16 +347,6 @@ class XboxDevice extends EventEmitter {
|
|
|
348
347
|
// Sort inputs in memory
|
|
349
348
|
this.inputsServices.sort(sortFn);
|
|
350
349
|
|
|
351
|
-
// Reassign identifiers (start at 1)
|
|
352
|
-
this.inputsServices.forEach((svc, index) => {
|
|
353
|
-
const newIdentifier = index + 1;
|
|
354
|
-
svc.identifier = newIdentifier;
|
|
355
|
-
|
|
356
|
-
if (svc.testCharacteristic(Characteristic.Identifier)) {
|
|
357
|
-
svc.updateCharacteristic(Characteristic.Identifier, newIdentifier);
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
|
|
361
350
|
// Debug dump
|
|
362
351
|
if (this.enableDebugMode) {
|
|
363
352
|
const orderDump = this.inputsServices.map(svc => ({ name: svc.name, reference: svc.reference, identifier: svc.identifier, }));
|
|
@@ -373,122 +362,118 @@ class XboxDevice extends EventEmitter {
|
|
|
373
362
|
}
|
|
374
363
|
}
|
|
375
364
|
|
|
376
|
-
async addRemoveOrUpdateInput(
|
|
365
|
+
async addRemoveOrUpdateInput(inputs, remove = false) {
|
|
377
366
|
try {
|
|
378
|
-
// Safety: no services
|
|
379
367
|
if (!this.inputsServices) return;
|
|
380
368
|
|
|
381
|
-
|
|
382
|
-
if (!remove) {
|
|
369
|
+
for (const input of inputs) {
|
|
383
370
|
const contentType = input.contentType;
|
|
384
371
|
const filterGames = this.filterGames && contentType === 'Game';
|
|
385
372
|
const filterApps = this.filterApps && contentType === 'App';
|
|
386
373
|
const filterSystemApps = this.filterSystemApps && contentType === 'systemApp';
|
|
387
374
|
const filterDlc = this.filterDlc && contentType === 'Dlc';
|
|
388
375
|
|
|
389
|
-
if (filterGames || filterApps || filterSystemApps || filterDlc
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Input reference
|
|
395
|
-
const inputReference = input.reference;
|
|
396
|
-
|
|
397
|
-
// --- REMOVE ---
|
|
398
|
-
if (remove) {
|
|
399
|
-
const svc = this.inputsServices.find(s => s.reference === inputReference);
|
|
400
|
-
if (svc) {
|
|
401
|
-
if (this.enableDebugMode) this.emit('debug', `Removing input: ${input.name} (${inputReference})`);
|
|
402
|
-
this.accessory.removeService(svc);
|
|
403
|
-
this.inputsServices = this.inputsServices.filter(s => s.reference !== inputReference);
|
|
404
|
-
await this.displayOrder();
|
|
405
|
-
return true;
|
|
406
|
-
}
|
|
407
|
-
if (this.enableDebugMode) this.emit('debug', `Remove failed (not found): ${input.name} (${inputReference})`);
|
|
408
|
-
return false;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// --- ADD OR UPDATE ---
|
|
412
|
-
let inputService = this.inputsServices.find(s => s.reference === inputReference);
|
|
376
|
+
if ((this.inputsServices.length >= 85 && !remove) || filterGames || filterApps || filterSystemApps || filterDlc) continue;
|
|
413
377
|
|
|
414
|
-
|
|
415
|
-
const sanitizedName = await this.sanitizeString(savedName);
|
|
416
|
-
const inputVisibility = this.savedInputsTargetVisibility[inputReference] ?? 0;
|
|
378
|
+
const inputReference = input.reference;
|
|
417
379
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
380
|
+
// --- REMOVE ---
|
|
381
|
+
if (remove) {
|
|
382
|
+
const svc = this.inputsServices.find(s => s.reference === inputReference);
|
|
383
|
+
if (svc) {
|
|
384
|
+
if (this.enableDebugMode) this.emit('debug', `Removing input: ${input.name} (${inputReference})`);
|
|
385
|
+
this.accessory.removeService(svc);
|
|
386
|
+
this.inputsServices = this.inputsServices.filter(s => s.reference !== inputReference);
|
|
387
|
+
await this.displayOrder();
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
if (this.enableDebugMode) this.emit('debug', `Remove failed (not found): ${input.name} (${inputReference})`);
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
422
393
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
394
|
+
// --- ADD OR UPDATE ---
|
|
395
|
+
let inputService = this.inputsServices.find(s => s.reference === inputReference);
|
|
396
|
+
|
|
397
|
+
const savedName = this.savedInputsNames[inputReference] ?? input.name;
|
|
398
|
+
const sanitizedName = await this.sanitizeString(savedName);
|
|
399
|
+
const inputMode = input.mode ?? 0;
|
|
400
|
+
const inputVisibility = this.savedInputsTargetVisibility[inputReference] ?? 0;
|
|
401
|
+
|
|
402
|
+
if (inputService) {
|
|
403
|
+
// === UPDATE EXISTING ===
|
|
404
|
+
inputService.name = sanitizedName;
|
|
405
|
+
inputService.visibility = inputVisibility;
|
|
406
|
+
|
|
407
|
+
inputService
|
|
408
|
+
.updateCharacteristic(Characteristic.Name, sanitizedName)
|
|
409
|
+
.updateCharacteristic(Characteristic.ConfiguredName, sanitizedName)
|
|
410
|
+
.updateCharacteristic(Characteristic.TargetVisibilityState, inputVisibility)
|
|
411
|
+
.updateCharacteristic(Characteristic.CurrentVisibilityState, inputVisibility);
|
|
412
|
+
|
|
413
|
+
if (this.enableDebugMode) this.emit('debug', `Updated input: ${input.name} (${inputReference})`);
|
|
414
|
+
} else {
|
|
415
|
+
// === CREATE NEW ===
|
|
416
|
+
const identifier = this.inputsServices.length + 1;
|
|
417
|
+
inputService = this.accessory.addService(Service.InputSource, sanitizedName, `Input ${identifier}`);
|
|
418
|
+
|
|
419
|
+
// Custom props
|
|
420
|
+
Object.assign(inputService, {
|
|
421
|
+
identifier,
|
|
422
|
+
reference: inputReference,
|
|
423
|
+
name: sanitizedName,
|
|
424
|
+
mode: inputMode,
|
|
425
|
+
visibility: inputVisibility,
|
|
426
|
+
});
|
|
428
427
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
.setCharacteristic(Characteristic.IsConfigured, 1)
|
|
446
|
-
.setCharacteristic(Characteristic.InputSourceType, 0) // 0=HDMI-like Input, 1=Tuner/Channel
|
|
447
|
-
.setCharacteristic(Characteristic.CurrentVisibilityState, inputVisibility)
|
|
448
|
-
.setCharacteristic(Characteristic.TargetVisibilityState, inputVisibility);
|
|
449
|
-
|
|
450
|
-
// --- ConfiguredName rename persistence ---
|
|
451
|
-
inputService.getCharacteristic(Characteristic.ConfiguredName)
|
|
452
|
-
.onSet(async (value) => {
|
|
453
|
-
try {
|
|
454
|
-
inputService.name = value;
|
|
455
|
-
this.savedInputsNames[inputReference] = value;
|
|
456
|
-
await this.saveData(this.inputsNamesFile, this.savedInputsNames);
|
|
428
|
+
inputService
|
|
429
|
+
.setCharacteristic(Characteristic.Identifier, identifier)
|
|
430
|
+
.setCharacteristic(Characteristic.Name, sanitizedName)
|
|
431
|
+
.setCharacteristic(Characteristic.ConfiguredName, sanitizedName)
|
|
432
|
+
.setCharacteristic(Characteristic.IsConfigured, 0)
|
|
433
|
+
.setCharacteristic(Characteristic.InputSourceType, inputMode) // 0=HDMI-like Input, 1=Tuner/Channel
|
|
434
|
+
.setCharacteristic(Characteristic.CurrentVisibilityState, inputVisibility)
|
|
435
|
+
.setCharacteristic(Characteristic.TargetVisibilityState, inputVisibility);
|
|
436
|
+
|
|
437
|
+
// --- ConfiguredName rename persistence ---
|
|
438
|
+
inputService.getCharacteristic(Characteristic.ConfiguredName)
|
|
439
|
+
.onSet(async (value) => {
|
|
440
|
+
try {
|
|
441
|
+
inputService.name = value;
|
|
442
|
+
this.savedInputsNames[inputReference] = value;
|
|
443
|
+
await this.saveData(this.inputsNamesFile, this.savedInputsNames);
|
|
457
444
|
|
|
458
|
-
|
|
445
|
+
if (this.enableDebugMode) this.emit('debug', `Saved Input: ${inputService.name}, Reference: ${inputReference}`);
|
|
459
446
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
if (index !== -1) this.inputsServices[index].name = value;
|
|
447
|
+
const index = this.inputsServices.findIndex(s => s.reference === inputReference);
|
|
448
|
+
if (index !== -1) this.inputsServices[index].name = value;
|
|
463
449
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
450
|
+
await this.displayOrder();
|
|
451
|
+
} catch (error) {
|
|
452
|
+
this.emit('warn', `Save Input Name error: ${error}`);
|
|
453
|
+
}
|
|
454
|
+
});
|
|
469
455
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
456
|
+
// --- TargetVisibility persistence ---
|
|
457
|
+
inputService.getCharacteristic(Characteristic.TargetVisibilityState)
|
|
458
|
+
.onSet(async (state) => {
|
|
459
|
+
try {
|
|
460
|
+
inputService.visibility = state;
|
|
461
|
+
this.savedInputsTargetVisibility[inputReference] = state;
|
|
462
|
+
await this.saveData(this.inputsTargetVisibilityFile, this.savedInputsTargetVisibility);
|
|
477
463
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
464
|
+
if (this.enableDebugMode) this.emit('debug', `Saved Input: ${inputService.name}, Target Visibility: ${state ? 'HIDDEN' : 'SHOWN'}`);
|
|
465
|
+
} catch (error) {
|
|
466
|
+
this.emit('warn', `Save Target Visibility error: ${error}`);
|
|
467
|
+
}
|
|
468
|
+
});
|
|
483
469
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
this.allServices.push(inputService);
|
|
470
|
+
this.inputsServices.push(inputService);
|
|
471
|
+
this.televisionService.addLinkedService(inputService);
|
|
487
472
|
|
|
488
|
-
|
|
473
|
+
if (this.enableDebugMode) this.emit('debug', `Added new input: ${input.name} (${inputReference})`);
|
|
474
|
+
}
|
|
489
475
|
}
|
|
490
476
|
|
|
491
|
-
// Normalize identifiers and order
|
|
492
477
|
await this.displayOrder();
|
|
493
478
|
return true;
|
|
494
479
|
} catch (error) {
|
|
@@ -514,7 +499,6 @@ class XboxDevice extends EventEmitter {
|
|
|
514
499
|
.setCharacteristic(Characteristic.SerialNumber, this.savedInfo.serialNumber ?? this.xboxLiveId)
|
|
515
500
|
.setCharacteristic(Characteristic.FirmwareRevision, this.savedInfo.firmwareRevision ?? 'Firmware Revision')
|
|
516
501
|
.setCharacteristic(Characteristic.ConfiguredName, accessoryName);
|
|
517
|
-
this.allServices.push(this.informationService);
|
|
518
502
|
|
|
519
503
|
//Prepare television service
|
|
520
504
|
if (this.enableDebugMode) this.emit('debug', `Prepare television service`);
|
|
@@ -734,7 +718,12 @@ class XboxDevice extends EventEmitter {
|
|
|
734
718
|
this.emit('warn', `set Power Mode Selection error: ${error}`);
|
|
735
719
|
}
|
|
736
720
|
});
|
|
737
|
-
|
|
721
|
+
|
|
722
|
+
//prepare inputs service
|
|
723
|
+
if (this.enableDebugMode) this.emit('debug', `Prepare inputs service`);
|
|
724
|
+
this.inputsServices = [];
|
|
725
|
+
const inputs = this.getInputsFromDevice ? [...DefaultInputs, ...this.savedInputs] : this.inputs;
|
|
726
|
+
await this.addRemoveOrUpdateInput(inputs, false);
|
|
738
727
|
|
|
739
728
|
//Prepare volume service
|
|
740
729
|
if (this.volumeControl > 0) {
|
|
@@ -803,7 +792,6 @@ class XboxDevice extends EventEmitter {
|
|
|
803
792
|
this.emit('warn', `set Mute error: ${error}`);
|
|
804
793
|
}
|
|
805
794
|
});
|
|
806
|
-
this.allServices.push(this.volumeServiceTvSpeaker);
|
|
807
795
|
|
|
808
796
|
//legacy control
|
|
809
797
|
switch (this.volumeControl) {
|
|
@@ -828,7 +816,6 @@ class XboxDevice extends EventEmitter {
|
|
|
828
816
|
.onSet(async (state) => {
|
|
829
817
|
this.volumeServiceTvSpeaker.setCharacteristic(Characteristic.Mute, !state);
|
|
830
818
|
});
|
|
831
|
-
this.allServices.push(this.volumeServiceLightbulb);
|
|
832
819
|
break;
|
|
833
820
|
case 2: //fan
|
|
834
821
|
const debug1 = this.enableDebugMode ? this.emit('debug', `Prepare volume service fan`) : false;
|
|
@@ -851,7 +838,6 @@ class XboxDevice extends EventEmitter {
|
|
|
851
838
|
.onSet(async (state) => {
|
|
852
839
|
this.volumeServiceTvSpeaker.setCharacteristic(Characteristic.Mute, !state);
|
|
853
840
|
});
|
|
854
|
-
this.allServices.push(this.volumeServiceFan);
|
|
855
841
|
break;
|
|
856
842
|
case 3: // speaker
|
|
857
843
|
const debug2 = this.enableDebugMode ? this.emit('debug', `Prepare volume service speaker`) : false;
|
|
@@ -881,21 +867,10 @@ class XboxDevice extends EventEmitter {
|
|
|
881
867
|
.onSet(async (value) => {
|
|
882
868
|
this.volumeServiceTvSpeaker.setCharacteristic(Characteristic.Volume, value);
|
|
883
869
|
});
|
|
884
|
-
this.allServices.push(this.volumeServiceSpeaker);
|
|
885
870
|
break;
|
|
886
871
|
}
|
|
887
872
|
}
|
|
888
873
|
|
|
889
|
-
//prepare inputs service
|
|
890
|
-
if (this.enableDebugMode) this.emit('debug', `Prepare inputs service`);
|
|
891
|
-
|
|
892
|
-
//check possible inputs count (max 85)
|
|
893
|
-
this.inputsServices = [];
|
|
894
|
-
const inputs = this.getInputsFromDevice ? [...DefaultInputs, ...this.savedInputs] : this.inputs;
|
|
895
|
-
for (const input of inputs) {
|
|
896
|
-
await this.addRemoveOrUpdateInput(input, false);
|
|
897
|
-
}
|
|
898
|
-
|
|
899
874
|
//prepare sensor service
|
|
900
875
|
if (this.sensorPower) {
|
|
901
876
|
if (this.enableDebugMode) this.emit('debug', `Prepare power sensor service`);
|
|
@@ -907,7 +882,6 @@ class XboxDevice extends EventEmitter {
|
|
|
907
882
|
const state = this.power;
|
|
908
883
|
return state;
|
|
909
884
|
});
|
|
910
|
-
this.allServices.push(this.sensorPowerService);
|
|
911
885
|
}
|
|
912
886
|
|
|
913
887
|
if (this.sensorInput) {
|
|
@@ -920,7 +894,6 @@ class XboxDevice extends EventEmitter {
|
|
|
920
894
|
const state = this.power ? this.sensorInputState : false;
|
|
921
895
|
return state;
|
|
922
896
|
});
|
|
923
|
-
this.allServices.push(this.sensorInputService);
|
|
924
897
|
}
|
|
925
898
|
|
|
926
899
|
if (this.sensorScreenSaver) {
|
|
@@ -933,11 +906,10 @@ class XboxDevice extends EventEmitter {
|
|
|
933
906
|
const state = this.power ? this.sensorScreenSaverState : false;
|
|
934
907
|
return state;
|
|
935
908
|
});
|
|
936
|
-
this.allServices.push(this.sensorScreenSaverService);
|
|
937
909
|
}
|
|
938
910
|
|
|
939
911
|
//prepare sonsor service
|
|
940
|
-
const possibleSensorInputsCount = 99 - this.
|
|
912
|
+
const possibleSensorInputsCount = 99 - this.accessory.services.length.length;
|
|
941
913
|
const maxSensorInputsCount = this.sensorsInputsConfiguredCount >= possibleSensorInputsCount ? possibleSensorInputsCount : this.sensorsInputsConfiguredCount;
|
|
942
914
|
if (maxSensorInputsCount > 0) {
|
|
943
915
|
if (this.enableDebugMode) this.emit('debug', `Prepare inputs sensors services`);
|
|
@@ -967,13 +939,12 @@ class XboxDevice extends EventEmitter {
|
|
|
967
939
|
return state;
|
|
968
940
|
});
|
|
969
941
|
this.sensorsInputsServices.push(sensorInputService);
|
|
970
|
-
this.allServices.push(sensorInputService);
|
|
971
942
|
accessory.addService(sensorInputService);
|
|
972
943
|
}
|
|
973
944
|
}
|
|
974
945
|
|
|
975
946
|
//Prepare buttons services
|
|
976
|
-
const possibleButtonsCount = 99 - this.
|
|
947
|
+
const possibleButtonsCount = 99 - this.accessory.services.length.length;
|
|
977
948
|
const maxButtonsCount = this.buttonsConfiguredCount >= possibleButtonsCount ? possibleButtonsCount : this.buttonsConfiguredCount;
|
|
978
949
|
if (maxButtonsCount > 0) {
|
|
979
950
|
if (this.enableDebugMode) this.emit('debug', `Prepare buttons services`);
|
|
@@ -1048,7 +1019,6 @@ class XboxDevice extends EventEmitter {
|
|
|
1048
1019
|
}
|
|
1049
1020
|
});
|
|
1050
1021
|
this.buttonsServices.push(buttonService);
|
|
1051
|
-
this.allServices.push(buttonService);
|
|
1052
1022
|
accessory.addService(buttonService);
|
|
1053
1023
|
}
|
|
1054
1024
|
}
|
|
@@ -1080,8 +1050,8 @@ class XboxDevice extends EventEmitter {
|
|
|
1080
1050
|
//this.power = powerState;
|
|
1081
1051
|
//this.mediaState = playbackState;
|
|
1082
1052
|
})
|
|
1083
|
-
.on('addRemoveOrUpdateInput', async (
|
|
1084
|
-
await this.addRemoveOrUpdateInput(
|
|
1053
|
+
.on('addRemoveOrUpdateInput', async (inputs, remove) => {
|
|
1054
|
+
await this.addRemoveOrUpdateInput(inputs, remove);
|
|
1085
1055
|
})
|
|
1086
1056
|
.on('success', (success) => {
|
|
1087
1057
|
this.emit('success', success);
|