homebridge-xbox-tv 3.6.3 → 3.6.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.
@@ -57,7 +57,6 @@
57
57
 
58
58
  <script>
59
59
  (async () => {
60
- //get the plugin config array
61
60
  const pluginConfig = await homebridge.getPluginConfig();
62
61
 
63
62
  if (!pluginConfig.length) {
@@ -66,176 +65,188 @@
66
65
  homebridge.showSchemaForm();
67
66
  return;
68
67
  }
69
- this.configButtonState = false;
70
68
 
71
- //get devices count
72
- const devicesCount = pluginConfig[0].devices.length;
69
+ this.configButtonState = false;
73
70
  this.deviceIndex = 0;
74
- for (let i = 0; i < devicesCount; i++) {
75
- //create buttons
76
- const button = document.createElement("button");
77
- button.setAttribute("type", "button");
78
- button.setAttribute("id", `button${i}`);
79
- button.setAttribute("class", "btn btn-primary");
80
- button.style.textTransform = 'none';
81
- button.innerText = pluginConfig[0].devices[i].name;
82
- document.getElementById("consoleButton").appendChild(button);
83
-
84
- //get actuall value on console select
85
- document.getElementById(`button${i}`).addEventListener('click', async () => {
86
- for (let j = 0; j < devicesCount; j++) {
87
- const setRemoveAtribute = j === i ? document.getElementById(`button${j}`).setAttribute("class", "btn btn-primary") : document.getElementById(`button${j}`).setAttribute("class", "btn btn-secondary");
88
- }
71
+ const devices = pluginConfig[0].devices || [];
72
+ const devicesCount = devices.length;
89
73
 
90
- document.getElementById('deviceName').innerHTML = pluginConfig[0].devices[i].name || '';
91
- document.getElementById('deviceHost').value = pluginConfig[0].devices[i].host || '';
92
- document.getElementById('deviceLiveId').value = pluginConfig[0].devices[i].xboxLiveId || '';
93
- document.getElementById('deviceToken').value = pluginConfig[0].devices[i].webApiToken || '';
94
- document.getElementById('deviceWebApiControl').checked = pluginConfig[0].devices[i].webApiControl || false;
74
+ // Helper to get DOM elements
75
+ const $ = id => document.getElementById(id);
95
76
 
96
- const tokenLength = (pluginConfig[0].devices[i].webApiToken).length || 0;
97
- const setButtonName = tokenLength <= 10 ? document.getElementById('startAuthorizationButton').innerText = "Start authorization" : document.getElementById('startAuthorizationButton').innerText = "Check state";
98
- const setCheckboxState = tokenLength <= 10 ? document.getElementById('deviceWebApiControl').checked = false : false;
99
- const setCheckboxMode = tokenLength <= 10 ? document.getElementById('deviceWebApiControl').disabled = true : document.getElementById('deviceWebApiControl').disabled = false;
100
- const setWebApiControlMode = tokenLength <= 10 ? pluginConfig[0].devices[i].webApiControl = false : false;
77
+ // Helper to set button classes
78
+ const setButtonClass = (activeIndex) => {
79
+ for (let j = 0; j < devicesCount; j++) {
80
+ $(`button${j}`).className = j === activeIndex ? "btn btn-primary" : "btn btn-secondary";
81
+ }
82
+ };
101
83
 
102
- await homebridge.updatePluginConfig(pluginConfig)
84
+ // Helper to update the device form
85
+ const updateDeviceForm = (device) => {
86
+ $('deviceName').innerHTML = device.name || '';
87
+ $('deviceHost').value = device.host || '';
88
+ $('deviceLiveId').value = device.xboxLiveId || '';
89
+ $('deviceToken').value = device.webApiToken || '';
90
+ $('deviceWebApiControl').checked = device.webApiControl || false;
91
+
92
+ const tokenLength = device.webApiToken?.length || 0;
93
+ $('startAuthorizationButton').innerText = tokenLength <= 10 ? "Start authorization" : "Check state";
94
+ $('deviceWebApiControl').disabled = tokenLength <= 10;
95
+ if (tokenLength <= 10) {
96
+ $('deviceWebApiControl').checked = false;
97
+ device.webApiControl = false;
98
+ }
99
+ };
100
+
101
+ // Create buttons for each device
102
+ devices.forEach((device, i) => {
103
+ const button = document.createElement("button");
104
+ button.type = "button";
105
+ button.id = `button${i}`;
106
+ button.className = "btn btn-primary";
107
+ button.style.textTransform = "none";
108
+ button.innerText = device.name;
109
+
110
+ $("consoleButton").appendChild(button);
111
+
112
+ button.addEventListener("click", async () => {
113
+ setButtonClass(i);
114
+ updateDeviceForm(devices[i]);
103
115
  this.deviceIndex = i;
116
+ await homebridge.updatePluginConfig(pluginConfig);
104
117
  });
105
- const click = i === devicesCount - 1 ? document.getElementById(`button0`).click() : false;
106
- const update = i === devicesCount - 1 ? await homebridge.updatePluginConfig(pluginConfig) : false;
107
- };
108
118
 
109
- //load authorizationManager form
110
- document.getElementById('authorizationManager').style.display = 'block';
119
+ // Auto-select the first device
120
+ if (i === devicesCount - 1) {
121
+ $("button0").click();
122
+ }
123
+ });
124
+
125
+ // Show the authorization form
126
+ $("authorizationManager").style.display = "block";
111
127
 
112
- //watch for changes to the config button
113
- document.getElementById('configButton').addEventListener('click', async () => {
114
- const showHideSettings = this.configButtonState ? homebridge.hideSchemaForm() : homebridge.showSchemaForm();
115
- const setRemoveAtribute = this.configButtonState ? document.getElementById(`configButton`).setAttribute("class", "btn btn-secondary") : document.getElementById(`configButton`).setAttribute("class", "btn btn-primary");
128
+ // Config button toggle
129
+ $("configButton").addEventListener("click", () => {
130
+ this.configButtonState ? homebridge.hideSchemaForm() : homebridge.showSchemaForm();
131
+ $("configButton").className = this.configButtonState ? "btn btn-secondary" : "btn btn-primary";
116
132
  this.configButtonState = !this.configButtonState;
117
133
  });
118
134
 
119
- //watch for changes to the form
120
- document.getElementById('configForm').addEventListener('input', async () => {
121
- pluginConfig[0].devices[this.deviceIndex].host = document.querySelector('#deviceHost').value;
122
- pluginConfig[0].devices[this.deviceIndex].xboxLiveId = document.querySelector('#deviceLiveId').value;
123
- pluginConfig[0].devices[this.deviceIndex].webApiToken = document.querySelector('#deviceToken').value;
124
- pluginConfig[0].devices[this.deviceIndex].webApiControl = document.querySelector('#deviceWebApiControl').checked;
135
+ // Update config on form input
136
+ $("configForm").addEventListener("input", async () => {
137
+ const currentDevice = devices[this.deviceIndex];
125
138
 
126
- const tokenLength1 = (document.querySelector('#deviceToken').value).length || 0;
127
- const setButtonName = tokenLength1 <= 10 ? document.getElementById('startAuthorizationButton').innerText = "Start authorization" : false;
128
- const setButtonMode = tokenLength1 <= 10 ? document.getElementById('startAuthorizationButton').removeAttribute('disabled') : false;
139
+ currentDevice.host = $('deviceHost').value;
140
+ currentDevice.xboxLiveId = $('deviceLiveId').value;
141
+ currentDevice.webApiToken = $('deviceToken').value;
142
+ currentDevice.webApiControl = $('deviceWebApiControl').checked;
143
+
144
+ const tokenLength = currentDevice.webApiToken?.length || 0;
145
+ $('startAuthorizationButton').innerText = tokenLength <= 10 ? "Start authorization" : "Check state";
146
+ if (tokenLength <= 10) {
147
+ $('startAuthorizationButton').removeAttribute('disabled');
148
+ }
129
149
 
130
150
  await homebridge.updatePluginConfig(pluginConfig);
131
151
  await homebridge.savePluginConfig(pluginConfig);
132
152
  });
133
153
 
134
- //clear token file
135
- document.getElementById('clearTokenButton').addEventListener('click', async () => {
154
+ // Clear token button logic
155
+ $("clearTokenButton").addEventListener("click", async () => {
136
156
  homebridge.showSpinner();
137
- document.getElementById('info').innerHTML = 'Start clearing token...';
138
- document.getElementById('info').style.color = 'yellow';
157
+ $("info").textContent = "Start clearing token...";
158
+ $("info").style.color = "yellow";
139
159
 
140
160
  try {
141
- const host = pluginConfig[0].devices[this.deviceIndex].host;
142
- await homebridge.request('/clearToken', { host: host });
161
+ const host = devices[this.deviceIndex].host;
162
+ await homebridge.request('/clearToken', { host });
143
163
 
144
- //update value
145
- pluginConfig[0].devices[this.deviceIndex].webApiToken = '';
146
- pluginConfig[0].devices[this.deviceIndex].webApiControl = false;
164
+ Object.assign(devices[this.deviceIndex], {
165
+ webApiToken: '',
166
+ webApiControl: false
167
+ });
147
168
 
148
- document.getElementById('info').innerHTML = 'Web api token cleared, now You can start new authorization process.';
149
- document.getElementById('info').style.color = 'green';
150
- document.getElementById('deviceToken').value = '';
151
- document.getElementById('startAuthorizationButton').removeAttribute("disabled");
152
- document.getElementById('startAuthorizationButton').innerText = "Start authorization";
153
- document.getElementById('deviceWebApiControl').checked = false;
154
- document.getElementById('deviceWebApiControl').disabled = true;
169
+ updateDeviceForm(devices[this.deviceIndex]);
170
+
171
+ $("info").textContent = "Web API token cleared, now you can start a new authorization process.";
172
+ $("info").style.color = "green";
173
+ $("startAuthorizationButton").removeAttribute("disabled");
155
174
 
156
175
  await homebridge.updatePluginConfig(pluginConfig);
157
176
  await homebridge.savePluginConfig(pluginConfig);
158
-
159
- homebridge.hideSpinner();
160
177
  } catch (error) {
161
- document.getElementById('info').innerHTML = 'Clear web api token error.';
162
- document.getElementById('info').style.color = 'yellow';
163
- document.getElementById('info1').innerHTML = `Error: ${error}`;
164
- document.getElementById('info1').style.color = 'red';
178
+ $("info").textContent = "Clear Web API token error.";
179
+ $("info").style.color = "yellow";
180
+ $("info1").textContent = `Error: ${error}`;
181
+ $("info1").style.color = "red";
182
+ } finally {
183
+ homebridge.hideSpinner();
165
184
  }
166
185
  });
167
186
 
168
- //watch for click on the Start authorization button
169
- document.getElementById('startAuthorizationButton').addEventListener('click', async () => {
187
+ // Start authorization logic
188
+ $("startAuthorizationButton").addEventListener("click", async () => {
170
189
  homebridge.showSpinner();
171
- document.getElementById('info').innerHTML = 'Starting authorization...';
172
- document.getElementById('info').style.color = 'yellow';
190
+ $("info").textContent = "Starting authorization...";
191
+ $("info").style.color = "yellow";
173
192
 
174
193
  try {
175
- const host = pluginConfig[0].devices[this.deviceIndex].host;
176
- const webApiClientId = pluginConfig[0].devices[this.deviceIndex].webApiClientId;
177
- const webApiClientSecret = pluginConfig[0].devices[this.deviceIndex].webApiClientSecret;
178
- const webApiToken = pluginConfig[0].devices[this.deviceIndex].webApiToken;
179
-
180
- const payload = {
181
- host: host,
182
- webApiClientId: webApiClientId,
183
- webApiClientSecret: webApiClientSecret,
184
- webApiToken: webApiToken
185
- };
186
- const response = await homebridge.request('/startAuthorization', payload);
187
- const info = response.info;
188
- const status = response.status;
194
+ const device = devices[this.deviceIndex];
195
+ const { host, webApiClientId, webApiClientSecret, webApiToken } = device;
196
+
197
+ const response = await homebridge.request('/startAuthorization', {
198
+ host, webApiClientId, webApiClientSecret, webApiToken
199
+ });
200
+
201
+ const { info, status } = response;
189
202
 
190
203
  switch (status) {
191
- case 0:
192
- document.getElementById('info').innerHTML = info;
193
- document.getElementById('info').style.color = 'green';
194
- document.getElementById('startAuthorizationButton').innerText = "Check state";
195
- document.getElementById('deviceWebApiControl').disabled = false;
204
+ case 0: // Authorized
205
+ $("info").textContent = info;
206
+ $("info").style.color = "green";
207
+ $("startAuthorizationButton").innerText = "Check state";
208
+ $("deviceWebApiControl").disabled = false;
196
209
  break;
197
- case 1:
198
- document.getElementById('startAuthorizationButton').innerText = "Activate console";
199
- document.getElementById('deviceWebApiControl').checked = false;
200
- document.getElementById('deviceWebApiControl').disabled = true;
201
- pluginConfig[0].devices[this.deviceIndex].webApiControl = false;
210
+ case 1: // Needs user interaction
211
+ $("startAuthorizationButton").innerText = "Activate console";
212
+ $("deviceWebApiControl").checked = false;
213
+ $("deviceWebApiControl").disabled = true;
214
+ device.webApiControl = false;
202
215
 
203
216
  let timeLeft = 15;
204
- let timerId = setInterval(() => {
217
+ const timerId = setInterval(() => {
205
218
  if (timeLeft <= 0) {
206
219
  open(info);
207
220
  clearInterval(timerId);
208
- document.getElementById('info').innerHTML = `Now paste the *code* to the *Web Api Token* and press *Activate Console*.`;
209
- document.getElementById('info').style.color = 'yellow';
221
+ $("info").innerText = "Now paste the *code* into *Web API Token* and press *Activate Console*.";
222
+ $("info").style.color = "yellow";
210
223
  } else {
211
- document.getElementById('info').innerHTML = `After ${timeLeft} sec. on the opened page, sign in to your Xbox Live account and give permission for Xbox TV. Next from the response URI copy the part after *?code=* and return to Authorization Manager.`;
224
+ $("info").innerText = `After ${timeLeft} sec, sign in to Xbox Live and authorize. Then copy the code after ?code= and return here.`;
225
+ $("info").style.color = "yellow";
212
226
  timeLeft--;
213
- document.getElementById('info').style.color = 'yellow';
214
227
  }
215
228
  }, 1000);
216
229
  break;
217
- case 2:
218
- document.getElementById('info').innerHTML = info;
219
- document.getElementById('info').style.color = 'green';
220
- document.getElementById('startAuthorizationButton').innerText = "Check state";
221
- document.getElementById('deviceWebApiControl').disabled = false;
222
- document.getElementById('deviceWebApiControl').checked = true;
223
- pluginConfig[0].devices[this.deviceIndex].webApiControl = true;
224
-
225
- await homebridge.updatePluginConfig(pluginConfig)
230
+ case 2: // Successfully authorized
231
+ $("info").textContent = info;
232
+ $("info").style.color = "green";
233
+ $("startAuthorizationButton").innerText = "Check state";
234
+ $("deviceWebApiControl").disabled = false;
235
+ $("deviceWebApiControl").checked = true;
236
+ device.webApiControl = true;
237
+
238
+ await homebridge.updatePluginConfig(pluginConfig);
226
239
  await homebridge.savePluginConfig(pluginConfig);
227
240
  break;
228
- };
229
-
230
- homebridge.hideSpinner();
241
+ }
231
242
  } catch (error) {
232
- document.getElementById('info').innerHTML = 'Check Your credentials data and try again.';
233
- document.getElementById('info').style.color = 'yellow';
234
- document.getElementById('info1').innerHTML = `Error: ${error}`;
235
- document.getElementById('info1').style.color = 'red';
243
+ $("info").textContent = "Check your credential data and try again.";
244
+ $("info").style.color = "yellow";
245
+ $("info1").textContent = `Error: ${error}`;
246
+ $("info1").style.color = "red";
236
247
  } finally {
237
248
  homebridge.hideSpinner();
238
- };
249
+ }
239
250
  });
240
251
 
241
252
  })();
@@ -16,75 +16,72 @@ class PluginUiServer extends HomebridgePluginUiServer {
16
16
  };
17
17
 
18
18
  async clearToken(payload) {
19
- const host = payload.host;
20
- const tokensFile = `${this.homebridgeStoragePath}/xboxTv/authToken_${host.split('.').join('')}`;
19
+ const hostKey = payload.host.replace(/\./g, '');
20
+ const tokensFile = `${this.homebridgeStoragePath}/xboxTv/authToken_${hostKey}`;
21
21
 
22
22
  const authConfig = {
23
23
  webApiClientId: payload.webApiClientId,
24
24
  webApiClientSecret: payload.webApiClientSecret,
25
- tokensFile: tokensFile
26
- }
25
+ tokensFile
26
+ };
27
+
27
28
  const authentication = new Authentication(authConfig);
28
29
 
29
30
  try {
30
- const tokens = {
31
- oauth: {},
32
- user: {},
33
- xsts: {}
34
- };
35
- await authentication.saveData(tokensFile, tokens);
31
+ const emptyTokens = { oauth: {}, user: {}, xsts: {} };
32
+ await authentication.saveData(tokensFile, emptyTokens);
36
33
  return true;
37
34
  } catch (error) {
38
- throw new Error(`Clear token error: ${error.message ?? error}.`);
39
- };
40
- };
35
+ throw new Error(`Clear token error: ${error?.message ?? error}`);
36
+ }
37
+ }
41
38
 
42
39
  async startAuthorization(payload) {
43
- const host = payload.host;
44
- const webApiToken = payload.webApiToken;
45
- const tokensFile = `${this.homebridgeStoragePath}/xboxTv/authToken_${host.split('.').join('')}`;
40
+ const hostKey = payload.host.replace(/\./g, '');
41
+ const tokensFile = `${this.homebridgeStoragePath}/xboxTv/authToken_${hostKey}`;
46
42
 
47
43
  const authConfig = {
48
44
  webApiClientId: payload.webApiClientId,
49
45
  webApiClientSecret: payload.webApiClientSecret,
50
- tokensFile: tokensFile
51
- }
46
+ tokensFile
47
+ };
48
+
52
49
  const authentication = new Authentication(authConfig);
53
- let data = {};
50
+ const webApiToken = payload.webApiToken;
54
51
 
55
52
  try {
53
+ // Case: Console already authorized
54
+ await authentication.checkAuthorization();
55
+ return {
56
+ info: 'Console authorized and activated. To start a new process, please clear the Web API Token first.',
57
+ status: 0 // Authorized
58
+ };
59
+ } catch {
60
+ if (webApiToken) {
61
+ try {
62
+ await authentication.accessToken(webApiToken);
63
+ return {
64
+ info: 'Activation successful! Now restart the plugin and have fun!',
65
+ status: 2 // Token activated
66
+ };
67
+ } catch (error) {
68
+ throw new Error(`Activation console error: ${error?.message ?? error}`);
69
+ }
70
+ }
71
+
72
+ // No token, generate auth URL
56
73
  try {
57
- await authentication.checkAuthorization();
58
- data = {
59
- info: 'Console authorized and activated. To start new process please clear Web API Token first.',
60
- status: 0
74
+ const oauth2URI = await authentication.generateAuthorizationUrl();
75
+ return {
76
+ info: oauth2URI,
77
+ status: 1 // Needs user authorization
61
78
  };
62
79
  } catch (error) {
63
- if (webApiToken) {
64
- try {
65
- await authentication.accessToken(webApiToken);
66
- data = {
67
- info: 'Activation console successfull, now restart plugin and have fun!!!',
68
- status: 2
69
- };
70
- } catch (error) {
71
- throw new Error(`Activation console error: ${error.message ?? error}.`);
72
- };
73
- } else {
74
- const oauth2URI = await authentication.generateAuthorizationUrl();
75
- data = {
76
- info: oauth2URI,
77
- status: 1
78
- };
79
- };
80
- };
81
-
82
- return data;
83
- } catch (error) {
84
- throw new Error(`Authorization manager error: ${error.message ?? error}.`);
85
- };
86
- };
87
- };
80
+ throw new Error(`Failed to generate authorization URL: ${error?.message ?? error}`);
81
+ }
82
+ }
83
+ }
84
+ }
88
85
 
89
86
  (() => {
90
87
  return new PluginUiServer();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "Xbox TV",
3
3
  "name": "homebridge-xbox-tv",
4
- "version": "3.6.3",
4
+ "version": "3.6.5",
5
5
  "description": "Homebridge plugin to control Xbox game consoles.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
@@ -32,7 +32,7 @@
32
32
  ],
33
33
  "engines": {
34
34
  "homebridge": "^1.9.0 || ^2.0.0 || ^2.0.0-beta.29",
35
- "node": "^18.20.4 || ^20.15.1 || ^22.7.0 || ^23.2.0 || ^24.0.0"
35
+ "node": "^20 || ^22 || ^24"
36
36
  },
37
37
  "dependencies": {
38
38
  "@homebridge/plugin-ui-utils": "^2.1.0",
@@ -43,7 +43,7 @@
43
43
  "uuid": "^11.1.0",
44
44
  "ping": "^0.4.4",
45
45
  "express": "^5.1.0",
46
- "axios": "^1.9.0"
46
+ "axios": "^1.10.0"
47
47
  },
48
48
  "keywords": [
49
49
  "homebridge",