homebridge-xbox-tv 3.7.3 → 3.7.4-beta.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "Xbox TV",
3
3
  "name": "homebridge-xbox-tv",
4
- "version": "3.7.3",
4
+ "version": "3.7.4-beta.0",
5
5
  "description": "Homebridge plugin to control Xbox game consoles.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
@@ -250,14 +250,14 @@ class XboxWebApi extends EventEmitter {
250
250
  'contentType': contentType
251
251
  }
252
252
  inputs.push(input);
253
+
254
+ //emit app
255
+ this.emit('addRemoveOrUpdateInput', input, false);
253
256
  }
254
257
 
255
258
  //save inputs
256
259
  await this.saveData(this.inputsFile, inputs);
257
260
 
258
- //emit apps
259
- this.emit('addRemoveOrUpdateInput', inputs, false);
260
-
261
261
  //emit restFul and mqtt
262
262
  this.emit('restFul', 'apps', apps);
263
263
  this.emit('mqtt', 'Apps', apps);
package/src/xboxdevice.js CHANGED
@@ -481,6 +481,129 @@ class XboxDevice extends EventEmitter {
481
481
  }
482
482
  }
483
483
 
484
+ async addRemoveOrUpdateInput(input, remove = false) {
485
+ try {
486
+ // Safety: no services or too many inputs (only block on add)
487
+ if (!this.inputsServices || (this.inputsServices.length >= 85 && !remove)) return;
488
+
489
+ // --- FILTER APPS ---
490
+ const contentType = input.contentType;
491
+ const filterGames = this.filterGames && contentType === 'Game';
492
+ const filterApps = this.filterApps && contentType === 'App';
493
+ const filterSystemApps = this.filterSystemApps && contentType === 'systemApp';
494
+ const filterDlc = this.filterDlc && contentType === 'Dlc';
495
+ if (filterGames || filterApps || filterSystemApps || filterDlc) return;
496
+
497
+ // Input reference
498
+ const inputReference = input.reference;
499
+
500
+ // --- REMOVE ---
501
+ if (remove) {
502
+ const svc = this.inputsServices.find(s => s.reference === inputReference);
503
+ if (svc) {
504
+ if (this.enableDebugMode) this.emit('debug', `Removing input: ${input.name} (${inputReference})`);
505
+ this.accessory.removeService(svc);
506
+ this.inputsServices = this.inputsServices.filter(s => s.reference !== inputReference);
507
+ await this.displayOrder();
508
+ return true;
509
+ }
510
+ if (this.enableDebugMode) this.emit('debug', `Remove failed (not found): ${input.name} (${inputReference})`);
511
+ return false;
512
+ }
513
+
514
+ // --- ADD OR UPDATE ---
515
+ let inputService = this.inputsServices.find(s => s.reference === inputReference);
516
+
517
+ const savedName = this.savedInputsNames[inputReference] ?? input.name;
518
+ const sanitizedName = await this.sanitizeString(savedName);
519
+ const inputMode = input.mode;
520
+ const inputVisibility = this.savedInputsTargetVisibility[inputReference] ?? 0;
521
+
522
+ if (inputService) {
523
+ // === UPDATE EXISTING ===
524
+ inputService.name = sanitizedName;
525
+ inputService.visibility = inputVisibility;
526
+
527
+ inputService
528
+ .updateCharacteristic(Characteristic.Name, sanitizedName)
529
+ .updateCharacteristic(Characteristic.ConfiguredName, sanitizedName)
530
+ .updateCharacteristic(Characteristic.TargetVisibilityState, inputVisibility)
531
+ .updateCharacteristic(Characteristic.CurrentVisibilityState, inputVisibility);
532
+
533
+ if (this.enableDebugMode) this.emit('debug', `Updated input: ${input.name} (${inputReference})`);
534
+ } else {
535
+ // === CREATE NEW ===
536
+ const identifier = this.inputsServices.length + 1;
537
+ inputService = this.accessory.addService(Service.InputSource, sanitizedName, `Input ${identifier}`);
538
+
539
+ // Custom props
540
+ inputService.identifier = identifier;
541
+ inputService.reference = inputReference;
542
+ inputService.name = sanitizedName;
543
+ inputService.mode = inputMode;
544
+ inputService.visibility = inputVisibility;
545
+
546
+ inputService
547
+ .setCharacteristic(Characteristic.Identifier, identifier)
548
+ .setCharacteristic(Characteristic.Name, sanitizedName)
549
+ .setCharacteristic(Characteristic.ConfiguredName, sanitizedName)
550
+ .setCharacteristic(Characteristic.IsConfigured, 1)
551
+ .setCharacteristic(Characteristic.InputSourceType, inputMode) // 0=HDMI-like Input, 1=Tuner/Channel
552
+ .setCharacteristic(Characteristic.CurrentVisibilityState, inputVisibility)
553
+ .setCharacteristic(Characteristic.TargetVisibilityState, inputVisibility);
554
+
555
+ // --- ConfiguredName rename persistence ---
556
+ inputService.getCharacteristic(Characteristic.ConfiguredName)
557
+ .onSet(async (value) => {
558
+ try {
559
+ inputService.name = value;
560
+ this.savedInputsNames[inputReference] = value;
561
+ await this.saveData(this.inputsNamesFile, this.savedInputsNames);
562
+
563
+ if (this.enableDebugMode) {
564
+ this.emit('debug', `Saved Input Name: ${value}, Reference: ${inputReference}`);
565
+ }
566
+
567
+ // keep in sync
568
+ const index = this.inputsServices.findIndex(s => s.reference === inputReference);
569
+ if (index !== -1) this.inputsServices[index].name = value;
570
+
571
+ await this.displayOrder();
572
+ } catch (error) {
573
+ this.emit('warn', `Save Input Name error: ${error}`);
574
+ }
575
+ });
576
+
577
+ // --- TargetVisibility persistence ---
578
+ inputService.getCharacteristic(Characteristic.TargetVisibilityState)
579
+ .onSet(async (state) => {
580
+ try {
581
+ inputService.visibility = state;
582
+ this.savedInputsTargetVisibility[inputReference] = state;
583
+ await this.saveData(this.inputsTargetVisibilityFile, this.savedInputsTargetVisibility);
584
+
585
+ if (this.enableDebugMode) {
586
+ this.emit('debug', `Saved Input: ${input.name}, Target Visibility: ${state ? 'HIDDEN' : 'SHOWN'}`);
587
+ }
588
+ } catch (error) {
589
+ this.emit('warn', `Save Target Visibility error: ${error}`);
590
+ }
591
+ });
592
+
593
+ this.inputsServices.push(inputService);
594
+ this.televisionService.addLinkedService(inputService);
595
+
596
+ if (this.enableDebugMode) this.emit('debug', `Added new input: ${input.name} (${inputReference})`);
597
+ }
598
+
599
+ // Normalize identifiers and order
600
+ await this.displayOrder();
601
+ return true;
602
+ } catch (error) {
603
+ throw new Error(`Add/Update input error: ${error}`);
604
+ }
605
+ }
606
+
484
607
  //Prepare accessory
485
608
  async prepareAccessory() {
486
609
  try {
@@ -723,7 +846,9 @@ class XboxDevice extends EventEmitter {
723
846
  if (this.enableDebugMode) this.emit('debug', `Prepare inputs service`);
724
847
  this.inputsServices = [];
725
848
  const inputs = this.getInputsFromDevice ? [...DefaultInputs, ...this.savedInputs] : this.inputs;
726
- await this.addRemoveOrUpdateInput(inputs, false);
849
+ for (const input of inputs) {
850
+ await this.addRemoveOrUpdateInput(input, false);
851
+ };
727
852
 
728
853
  //Prepare volume service
729
854
  if (this.volumeControl > 0) {
@@ -1050,8 +1175,8 @@ class XboxDevice extends EventEmitter {
1050
1175
  //this.power = powerState;
1051
1176
  //this.mediaState = playbackState;
1052
1177
  })
1053
- .on('addRemoveOrUpdateInput', async (inputs, remove) => {
1054
- await this.addRemoveOrUpdateInput(inputs, remove);
1178
+ .on('addRemoveOrUpdateInput', async (input, remove) => {
1179
+ await this.addRemoveOrUpdateInput(input, remove);
1055
1180
  })
1056
1181
  .on('success', (success) => {
1057
1182
  this.emit('success', success);