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.
- package/homebridge-ui/public/index.html +131 -120
- package/homebridge-ui/server.js +45 -48
- package/package.json +3 -3
|
@@ -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
|
-
|
|
72
|
-
const devicesCount = pluginConfig[0].devices.length;
|
|
69
|
+
this.configButtonState = false;
|
|
73
70
|
this.deviceIndex = 0;
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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
|
-
|
|
110
|
-
|
|
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
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
//
|
|
135
|
-
|
|
154
|
+
// Clear token button logic
|
|
155
|
+
$("clearTokenButton").addEventListener("click", async () => {
|
|
136
156
|
homebridge.showSpinner();
|
|
137
|
-
|
|
138
|
-
|
|
157
|
+
$("info").textContent = "Start clearing token...";
|
|
158
|
+
$("info").style.color = "yellow";
|
|
139
159
|
|
|
140
160
|
try {
|
|
141
|
-
const host =
|
|
142
|
-
await homebridge.request('/clearToken', { host
|
|
161
|
+
const host = devices[this.deviceIndex].host;
|
|
162
|
+
await homebridge.request('/clearToken', { host });
|
|
143
163
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
164
|
+
Object.assign(devices[this.deviceIndex], {
|
|
165
|
+
webApiToken: '',
|
|
166
|
+
webApiControl: false
|
|
167
|
+
});
|
|
147
168
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
//
|
|
169
|
-
|
|
187
|
+
// Start authorization logic
|
|
188
|
+
$("startAuthorizationButton").addEventListener("click", async () => {
|
|
170
189
|
homebridge.showSpinner();
|
|
171
|
-
|
|
172
|
-
|
|
190
|
+
$("info").textContent = "Starting authorization...";
|
|
191
|
+
$("info").style.color = "yellow";
|
|
173
192
|
|
|
174
193
|
try {
|
|
175
|
-
const
|
|
176
|
-
const webApiClientId =
|
|
177
|
-
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
217
|
+
const timerId = setInterval(() => {
|
|
205
218
|
if (timeLeft <= 0) {
|
|
206
219
|
open(info);
|
|
207
220
|
clearInterval(timerId);
|
|
208
|
-
|
|
209
|
-
|
|
221
|
+
$("info").innerText = "Now paste the *code* into *Web API Token* and press *Activate Console*.";
|
|
222
|
+
$("info").style.color = "yellow";
|
|
210
223
|
} else {
|
|
211
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
})();
|
package/homebridge-ui/server.js
CHANGED
|
@@ -16,75 +16,72 @@ class PluginUiServer extends HomebridgePluginUiServer {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
async clearToken(payload) {
|
|
19
|
-
const
|
|
20
|
-
const tokensFile = `${this.homebridgeStoragePath}/xboxTv/authToken_${
|
|
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
|
|
26
|
-
}
|
|
25
|
+
tokensFile
|
|
26
|
+
};
|
|
27
|
+
|
|
27
28
|
const authentication = new Authentication(authConfig);
|
|
28
29
|
|
|
29
30
|
try {
|
|
30
|
-
const
|
|
31
|
-
|
|
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
|
|
39
|
-
}
|
|
40
|
-
}
|
|
35
|
+
throw new Error(`Clear token error: ${error?.message ?? error}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
41
38
|
|
|
42
39
|
async startAuthorization(payload) {
|
|
43
|
-
const
|
|
44
|
-
const
|
|
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
|
|
51
|
-
}
|
|
46
|
+
tokensFile
|
|
47
|
+
};
|
|
48
|
+
|
|
52
49
|
const authentication = new Authentication(authConfig);
|
|
53
|
-
|
|
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.
|
|
58
|
-
|
|
59
|
-
info:
|
|
60
|
-
status:
|
|
74
|
+
const oauth2URI = await authentication.generateAuthorizationUrl();
|
|
75
|
+
return {
|
|
76
|
+
info: oauth2URI,
|
|
77
|
+
status: 1 // Needs user authorization
|
|
61
78
|
};
|
|
62
79
|
} catch (error) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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.
|
|
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": "^
|
|
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.
|
|
46
|
+
"axios": "^1.10.0"
|
|
47
47
|
},
|
|
48
48
|
"keywords": [
|
|
49
49
|
"homebridge",
|