From dfd7d33f03fe5aa8489464c2cd57cac1f6006d3f Mon Sep 17 00:00:00 2001
From: Grzegorz <grzegorz914@icloud.com>
Date: Fri, 29 Dec 2023 18:32:54 +0100
Subject: [PATCH] release 2.11.0

---
 CHANGELOG.md       |  6 +++++
 README.md          |  1 +
 config.schema.json | 30 +++++++++++++++++++++++
 package.json       |  2 +-
 sample-config.json |  1 +
 src/xboxdevice.js  | 60 +++++++++++++++++++++++++---------------------
 6 files changed, 72 insertions(+), 28 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5ade1d3..d08755d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### NOTE!!!
 ## After update to 2.x.x the plugin settings (xboxLiveId) need to be updated.
 
+## [2.11.0] - (29.12.2023)
+## Changes
+- added possibility to select display inputs order, possible by `None`, `Alphabetically Name`, `Alphabetically Reference`
+- config schema updated
+- cleanup
+
 ## [2.10.0] - (26.12.2023)
 ## After update to this version the plugin properties are changed and console must be authorized and settings need to be corrected.
 ## Changes
diff --git a/README.md b/README.md
index b03e6ed..a76c473 100644
--- a/README.md
+++ b/README.md
@@ -80,6 +80,7 @@ Homebridge plugin for Microsoft game Consoles. Tested with Xbox One X/S and Xbox
 | `filterApps` | If enabled, Apps will be hidden and not displayed in the inputs list, only available if `webApiControl` enabled. |
 | `filterSystemApps` | If enabled, System Apps (Accessory, Microsoft Store, Television) will be hidden and not displayed in the inputs list, only available if `webApiControl` enabled. |
 | `filterDlc` | If enabled, Dlc will be hidden and not displayed in the inputs list, only available if `webApiControl` enabled. |
+| `inputsDisplayOrder` | Here select display order of the inputs list, `None`, `Alphabetically Name`, `Alphabetically Reference`. |
 | `inputs.name` | Here set *Input Name* which You want expose to the *Homebridge/HomeKit*, `Screensaver`, `Television`, `TV Settings`, `Dashboard`, `Accessory`, `Settings`, `Network Troubleshooter`, `Microsoft Store`, `Xbox Guide` are created by default. |
 | `inputs.reference` | Required to identify current running app. |
 | `inputs.oneStoreProductId` | Required to switch apps. |
diff --git a/config.schema.json b/config.schema.json
index e087c77..3fdc085 100644
--- a/config.schema.json
+++ b/config.schema.json
@@ -80,6 +80,35 @@
 							"description": "If enabled, DLC will not be displayed on the list of inputs.",
 							"required": false
 						},
+						"inputsDisplayOrder": {
+							"title": "Inputs Display Order",
+							"type": "integer",
+							"minimum": 0,
+							"maximum": 2,
+							"default": 0,
+							"description": "Here select display order of the inputs list.",
+							"oneOf": [
+							  {
+								"title": "None",
+								"enum": [
+								  0
+								]
+							  },
+							  {
+								"title": "Alphabetically Name",
+								"enum": [
+								  1
+								]
+							  },
+							  {
+								"title": "Alphabetically Reference",
+								"enum": [
+								  2
+								]
+							  }
+							],
+							"required": true
+						  },
 						"inputs": {
 							"type": "array",
 							"items": {
@@ -779,6 +808,7 @@
 						"devices[].filterApps",
 						"devices[].filterSystemApps",
 						"devices[].filterDlc",
+						"devices[].inputsDisplayOrder",
 						{
 							"key": "devices[].inputs",
 							"type": "tabarray",
diff --git a/package.json b/package.json
index 5c2d688..136a38e 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "displayName": "Xbox TV",
   "name": "homebridge-xbox-tv",
-  "version": "2.10.3",
+  "version": "2.11.0",
   "description": "Homebridge plugin to control Xbox game consoles.",
   "license": "MIT",
   "author": "grzegorz914",
diff --git a/sample-config.json b/sample-config.json
index d1dce93..6b1194c 100644
--- a/sample-config.json
+++ b/sample-config.json
@@ -27,6 +27,7 @@
 					"filterApps": false,
 					"filterSystemApps": false,
 					"filterDlc": false,
+					"inputsDisplayOrder": 0,
 					"inputs": [
 						{
 							"name": "A Way Out",
diff --git a/src/xboxdevice.js b/src/xboxdevice.js
index b577dba..efa7c99 100644
--- a/src/xboxdevice.js
+++ b/src/xboxdevice.js
@@ -62,20 +62,16 @@ class XboxDevice extends EventEmitter {
 
         //add configured inputs to the default inputs
         this.inputs = [...CONSTANTS.DefaultInputs, ...this.inputs];
-        this.displayOrder = []
+        this.inputsConfigured = [];
+        this.displayOrder = [];
 
         //setup variables
         this.restFulConnected = false;
         this.mqttConnected = false;
 
         this.allServices = [];
-        this.inputsName = [];
-        this.inputsReference = [];
-        this.inputsOneStoreProductId = [];
-
         this.sensorInputsServices = [];
-        this.sensorInputsReference = [];
-        this.sensorInputsDisplayType = [];
+        this.sensorInputs = [];
         this.buttonsServices = [];
 
         this.power = false;
@@ -278,7 +274,7 @@ class XboxDevice extends EventEmitter {
                 }
             })
             .on('stateChanged', (power, volume, mute, mediaState, titleId, reference) => {
-                const inputIdentifier = this.inputsReference.includes(reference) ? this.inputsReference.findIndex(index => index === reference) : this.inputsReference.includes(titleId) ? this.inputsReference.findIndex(index => index === titleId) : undefined;
+                const inputIdentifier = this.inputsConfigured.findIndex(index => index.reference === reference) + 1 ?? this.inputsConfigured.findIndex(index => index.titleId === titleId) + 1;
 
                 //update characteristics
                 if (this.televisionService) {
@@ -286,7 +282,7 @@ class XboxDevice extends EventEmitter {
                         .updateCharacteristic(Characteristic.Active, power)
                 };
 
-                if (this.televisionService && inputIdentifier !== undefined) {
+                if (this.televisionService && inputIdentifier !== 0) {
                     this.televisionService
                         .updateCharacteristic(Characteristic.ActiveIdentifier, inputIdentifier);
                     this.inputIdentifier = inputIdentifier;
@@ -313,7 +309,7 @@ class XboxDevice extends EventEmitter {
                         .updateCharacteristic(Characteristic.ContactSensorState, power)
                 }
 
-                if (this.sensorInputService && inputIdentifier !== undefined) {
+                if (this.sensorInputService && inputIdentifier !== 0) {
                     const state = power ? (this.inputIdentifier !== inputIdentifier) : false;
                     this.sensorInputService
                         .updateCharacteristic(Characteristic.ContactSensorState, state)
@@ -331,8 +327,8 @@ class XboxDevice extends EventEmitter {
                 if (this.sensorInputsServices) {
                     const servicesCount = this.sensorInputsServices.length;
                     for (let i = 0; i < servicesCount; i++) {
-                        const state = power ? (this.sensorInputsReference[i] === reference) : false;
-                        const displayType = this.sensorInputsDisplayType[i];
+                        const state = power ? (this.sensorInputs[i].reference === reference) : false;
+                        const displayType = this.sensorInputs[i].displayType;
                         const characteristicType = [Characteristic.MotionDetected, Characteristic.OccupancyDetected, Characteristic.ContactSensorState][displayType];
                         this.sensorInputsServices[i]
                             .updateCharacteristic(characteristicType, state);
@@ -496,17 +492,17 @@ class XboxDevice extends EventEmitter {
                 this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier)
                     .onGet(async () => {
                         const inputIdentifier = this.inputIdentifier;
-                        const inputOneStoreProductId = this.inputsOneStoreProductId[inputIdentifier];
-                        const inputReference = this.inputsReference[inputIdentifier];
-                        const inputName = this.inputsName[inputIdentifier];
+                        const inputOneStoreProductId = this.inputsConfigured[inputIdentifier].oneStoreProductId;
+                        const inputReference = this.inputsConfigured[inputIdentifier].reference;
+                        const inputName = this.inputsConfigured[inputIdentifier].name;
                         const logInfo = this.disableLogInfo ? false : this.emit('message', `Input: ${inputName}, Reference: ${inputReference}, Product Id: ${inputOneStoreProductId}`);
                         return inputIdentifier;
                     })
                     .onSet(async (inputIdentifier) => {
                         try {
-                            const inputOneStoreProductId = this.inputsOneStoreProductId[inputIdentifier];
-                            const inputReference = this.inputsReference[inputIdentifier];
-                            const inputName = this.inputsName[inputIdentifier];
+                            const inputOneStoreProductId = this.inputsConfigured[inputIdentifier].oneStoreProductId;
+                            const inputReference = this.inputsConfigured[inputIdentifier].reference;
+                            const inputName = this.inputsConfigured[inputIdentifier].name;
 
                             let channelName;
                             let command;
@@ -765,7 +761,19 @@ class XboxDevice extends EventEmitter {
                 }
 
                 //check possible inputs and possible inputs count (max 80)
-                const inputs = filteredInputsArr;
+                let inputs = filteredInputsArr;
+                switch (this.inputsDisplayOrder) {
+                    case 0:
+                        inputs = inputs
+                        break;
+                    case 1:
+                        inputs.sort((a, b) => a.name.localeCompare(b.name));
+                        break;
+                    case 2:
+                        inputs.sort((a, b) => a.reference.localeCompare(b.reference));
+                        break;
+                }
+
                 const inputsCount = inputs.length;
                 const possibleInputsCount = 90 - this.allServices.length;
                 const maxInputsCount = inputsCount >= possibleInputsCount ? possibleInputsCount : inputsCount;
@@ -797,7 +805,7 @@ class XboxDevice extends EventEmitter {
                     if (inputReference && inputName) {
                         const inputService = new Service.InputSource(`${inputName} ${i}`, `Input ${i}`);
                         inputService
-                            .setCharacteristic(Characteristic.Identifier, i)
+                            .setCharacteristic(Characteristic.Identifier, i + 1)
                             .setCharacteristic(Characteristic.Name, inputName)
                             .setCharacteristic(Characteristic.InputSourceType, inputType)
                             .setCharacteristic(Characteristic.IsConfigured, isConfigured)
@@ -808,7 +816,7 @@ class XboxDevice extends EventEmitter {
                                 return inputName;
                             })
                             .onSet(async (value) => {
-                                const valueExist = value === this.savedInputsNames[inputReference];
+                                const valueExist = inputName;
                                 if (valueExist) {
                                     return;
                                 }
@@ -832,7 +840,7 @@ class XboxDevice extends EventEmitter {
                                 return targetVisibility;
                             })
                             .onSet(async (state) => {
-                                const stateExist = state === this.savedInputsTargetVisibility[inputReference];
+                                const stateExist = state === currentVisibility;
                                 if (stateExist) {
                                     return;
                                 };
@@ -851,9 +859,7 @@ class XboxDevice extends EventEmitter {
                             });
 
                         this.displayOrder.push(i + 1);
-                        this.inputsOneStoreProductId.push(inputOneStoreProductId);
-                        this.inputsReference.push(inputReference);
-                        this.inputsName.push(inputName);
+                        this.inputsConfigured.push(input);
 
                         this.televisionService.addLinkedService(inputService);
                         this.allServices.push(inputService);
@@ -863,6 +869,7 @@ class XboxDevice extends EventEmitter {
 
                     };
                 }
+                this.televisionService.setCharacteristic(Characteristic.DisplayOrder, Encode(1, this.displayOrder).toString('base64'));
 
                 //Prepare volume service
                 if (this.volumeControl >= 0) {
@@ -997,8 +1004,7 @@ class XboxDevice extends EventEmitter {
                                         return state;
                                     });
 
-                                this.sensorInputsReference.push(sensorInputReference);
-                                this.sensorInputsDisplayType.push(sensorInputDisplayType);
+                                this.sensorInputs.push(sensorInput);
                                 this.sensorInputsServices.push(sensorInputService);
                                 this.allServices.push(sensorInputService);
                                 accessory.addService(this.sensorInputsServices[i]);