@switchbot/homebridge-switchbot 5.0.0 → 5.1.0-beta.1
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/.github/npm-version-script-esm.js +97 -0
- package/CHANGELOG.md +21 -8
- package/MIGRATION.md +5 -0
- package/README.md +25 -0
- package/config.schema.json +11 -0
- package/dist/SwitchBotHAPPlatform.d.ts.map +1 -1
- package/dist/SwitchBotHAPPlatform.js +33 -4
- package/dist/SwitchBotHAPPlatform.js.map +1 -1
- package/dist/SwitchBotMatterPlatform.d.ts +2 -0
- package/dist/SwitchBotMatterPlatform.d.ts.map +1 -1
- package/dist/SwitchBotMatterPlatform.js +61 -5
- package/dist/SwitchBotMatterPlatform.js.map +1 -1
- package/dist/homebridge-ui/endpoints/config.d.ts.map +1 -1
- package/dist/homebridge-ui/endpoints/config.js +9 -2
- package/dist/homebridge-ui/endpoints/config.js.map +1 -1
- package/dist/homebridge-ui/homebridge-ui/endpoints/config.js +9 -2
- package/dist/homebridge-ui/homebridge-ui/endpoints/config.js.map +1 -1
- package/dist/homebridge-ui/homebridge-ui/utils/config-parser.js +8 -0
- package/dist/homebridge-ui/homebridge-ui/utils/config-parser.js.map +1 -1
- package/dist/homebridge-ui/homebridge-ui/utils/v4-detection.js +23 -0
- package/dist/homebridge-ui/homebridge-ui/utils/v4-detection.js.map +1 -0
- package/dist/homebridge-ui/public/css/styles.css +61 -1
- package/dist/homebridge-ui/public/index.html +25 -2
- package/dist/homebridge-ui/public/js/api.d.ts +16 -0
- package/dist/homebridge-ui/public/js/api.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/api.js +48 -0
- package/dist/homebridge-ui/public/js/api.js.map +1 -1
- package/dist/homebridge-ui/public/js/api.ts +61 -1
- package/dist/homebridge-ui/public/js/app.js +237 -111
- package/dist/homebridge-ui/public/js/app.js.map +4 -4
- package/dist/homebridge-ui/public/js/app.ts +22 -4
- package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/devices.js +6 -0
- package/dist/homebridge-ui/public/js/devices.js.map +1 -1
- package/dist/homebridge-ui/public/js/devices.ts +6 -0
- package/dist/homebridge-ui/public/js/modals.d.ts +1 -0
- package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/modals.js +53 -0
- package/dist/homebridge-ui/public/js/modals.js.map +1 -1
- package/dist/homebridge-ui/public/js/modals.ts +58 -0
- package/dist/homebridge-ui/public/js/render.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/render.js +2 -1
- package/dist/homebridge-ui/public/js/render.js.map +1 -1
- package/dist/homebridge-ui/public/js/render.ts +2 -1
- package/dist/homebridge-ui/settings.js.map +1 -1
- package/dist/homebridge-ui/utils/config-parser.d.ts +8 -0
- package/dist/homebridge-ui/utils/config-parser.d.ts.map +1 -1
- package/dist/homebridge-ui/utils/config-parser.js +8 -0
- package/dist/homebridge-ui/utils/config-parser.js.map +1 -1
- package/dist/homebridge-ui/utils/v4-detection.d.ts +12 -0
- package/dist/homebridge-ui/utils/v4-detection.d.ts.map +1 -0
- package/dist/homebridge-ui/utils/v4-detection.js +23 -0
- package/dist/homebridge-ui/utils/v4-detection.js.map +1 -0
- package/dist/settings.d.ts +11 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js.map +1 -1
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +27 -0
- package/dist/utils.js.map +1 -1
- package/docs/assets/highlight.css +15 -15
- package/docs/assets/icons.js +1 -1
- package/docs/assets/icons.svg +1 -1
- package/docs/assets/main.js +3 -3
- package/docs/index.html +22 -5
- package/docs/variables/default.html +1 -1
- package/package.json +6 -6
- package/scripts/build-ui.js +30 -0
- package/src/SwitchBotHAPPlatform.ts +31 -6
- package/src/SwitchBotMatterPlatform.ts +61 -7
- package/src/homebridge-ui/endpoints/config.ts +10 -2
- package/src/homebridge-ui/public/css/styles.css +61 -1
- package/src/homebridge-ui/public/index.html +23 -0
- package/src/homebridge-ui/public/js/api.ts +61 -1
- package/src/homebridge-ui/public/js/app.ts +22 -4
- package/src/homebridge-ui/public/js/devices.ts +6 -0
- package/src/homebridge-ui/public/js/modals.ts +58 -0
- package/src/homebridge-ui/public/js/render.ts +2 -1
- package/src/homebridge-ui/utils/config-parser.ts +9 -0
- package/src/homebridge-ui/utils/v4-detection.ts +23 -0
- package/src/settings.ts +12 -0
- package/src/utils.ts +31 -0
- package/test/homebridge-ui/config-parser.spec.ts +61 -0
- package/test/platform/matter-integration.spec.ts +50 -0
- package/test/utils/utils.spec.ts +15 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Fetch the list of configured devices from the Homebridge UI API
|
|
2
2
|
import { isValidDeviceType, normalizeDeviceType } from '../../../device-types.js'
|
|
3
|
+
import { isV4Config } from '../../utils/v4-detection.js'
|
|
3
4
|
import './types.js'
|
|
4
5
|
import { uiLog } from './logger.js'
|
|
5
6
|
import { toastError } from './toast.js'
|
|
@@ -22,6 +23,35 @@ export async function fetchDevices(): Promise<any[]> {
|
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Detect whether the current plugin config is in the legacy v4 format.
|
|
28
|
+
*
|
|
29
|
+
* v4 configs stored credentials under a `credentials` sub-object and devices
|
|
30
|
+
* under `options.devices`. v5+ flattened these to `openApiToken`,
|
|
31
|
+
* `openApiSecret`, and a root-level `devices` array.
|
|
32
|
+
*
|
|
33
|
+
* Returns true when a v4 config block is found, false otherwise.
|
|
34
|
+
*/
|
|
35
|
+
export async function detectV4Config(): Promise<boolean> {
|
|
36
|
+
try {
|
|
37
|
+
if (typeof homebridge.getPluginConfig !== 'function') {
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
const configArr = await homebridge.getPluginConfig()
|
|
41
|
+
if (!Array.isArray(configArr)) {
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
const config = configArr.find(isSwitchBotPlatformConfig)
|
|
45
|
+
if (!config || typeof config !== 'object') {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
return isV4Config(config)
|
|
49
|
+
} catch (e) {
|
|
50
|
+
uiLog.warn('Error detecting v4 config:', e)
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
25
55
|
/**
|
|
26
56
|
* Validate and auto-correct device types in the config array before saving.
|
|
27
57
|
* Returns an array of errors for devices that cannot be fixed.
|
|
@@ -189,7 +219,19 @@ export async function addDevice(
|
|
|
189
219
|
deviceId: string,
|
|
190
220
|
name: string,
|
|
191
221
|
type: string,
|
|
192
|
-
options?: {
|
|
222
|
+
options?: {
|
|
223
|
+
address?: string
|
|
224
|
+
model?: string
|
|
225
|
+
rssi?: number
|
|
226
|
+
connectionPreference?: string
|
|
227
|
+
externalPublishProtocol?: string
|
|
228
|
+
room?: string
|
|
229
|
+
encryptionKey?: string
|
|
230
|
+
keyId?: string
|
|
231
|
+
refreshRate?: number
|
|
232
|
+
blePollingEnabled?: boolean
|
|
233
|
+
blePollIntervalMs?: number
|
|
234
|
+
},
|
|
193
235
|
): Promise<any> {
|
|
194
236
|
if (typeof homebridge.getPluginConfig !== 'function' || typeof homebridge.updatePluginConfig !== 'function') {
|
|
195
237
|
throw new TypeError('Homebridge UI API not available')
|
|
@@ -224,6 +266,24 @@ export async function addDevice(
|
|
|
224
266
|
if (options?.keyId) {
|
|
225
267
|
newDevice.keyId = options.keyId
|
|
226
268
|
}
|
|
269
|
+
if (options?.connectionPreference) {
|
|
270
|
+
newDevice.connectionPreference = options.connectionPreference
|
|
271
|
+
}
|
|
272
|
+
if (options?.externalPublishProtocol) {
|
|
273
|
+
newDevice.externalPublishProtocol = options.externalPublishProtocol
|
|
274
|
+
}
|
|
275
|
+
if (options?.room) {
|
|
276
|
+
newDevice.room = options.room
|
|
277
|
+
}
|
|
278
|
+
if (typeof options?.refreshRate === 'number' && Number.isFinite(options.refreshRate)) {
|
|
279
|
+
newDevice.refreshRate = options.refreshRate
|
|
280
|
+
}
|
|
281
|
+
if (typeof options?.blePollingEnabled === 'boolean') {
|
|
282
|
+
newDevice.blePollingEnabled = options.blePollingEnabled
|
|
283
|
+
}
|
|
284
|
+
if (typeof options?.blePollIntervalMs === 'number' && Number.isFinite(options.blePollIntervalMs)) {
|
|
285
|
+
newDevice.blePollIntervalMs = options.blePollIntervalMs
|
|
286
|
+
}
|
|
227
287
|
config.devices.push(newDevice)
|
|
228
288
|
await homebridge.updatePluginConfig(configArr)
|
|
229
289
|
if (typeof homebridge.savePluginConfig === 'function') {
|
|
@@ -8,111 +8,6 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
// src/homebridge-ui/public/js/logger.ts
|
|
12
|
-
var PREFIX, uiLog;
|
|
13
|
-
var init_logger = __esm({
|
|
14
|
-
"src/homebridge-ui/public/js/logger.ts"() {
|
|
15
|
-
"use strict";
|
|
16
|
-
PREFIX = "[SwitchBot UI/html]";
|
|
17
|
-
uiLog = {
|
|
18
|
-
info: (message, ...parameters) => {
|
|
19
|
-
console.log(PREFIX, message, ...parameters);
|
|
20
|
-
},
|
|
21
|
-
warn: (message, ...parameters) => {
|
|
22
|
-
console.warn(PREFIX, message, ...parameters);
|
|
23
|
-
},
|
|
24
|
-
error: (message, ...parameters) => {
|
|
25
|
-
console.error(PREFIX, message, ...parameters);
|
|
26
|
-
},
|
|
27
|
-
debug: (message, ...parameters) => {
|
|
28
|
-
console.debug(PREFIX, message, ...parameters);
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// src/homebridge-ui/public/js/types.ts
|
|
35
|
-
var init_types = __esm({
|
|
36
|
-
"src/homebridge-ui/public/js/types.ts"() {
|
|
37
|
-
"use strict";
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// src/homebridge-ui/public/js/modal.ts
|
|
42
|
-
function callUiMethod(name, ...args) {
|
|
43
|
-
try {
|
|
44
|
-
if (typeof homebridge?.[name] === "function") {
|
|
45
|
-
uiLog.info(`[callUiMethod] Invoking homebridge.${String(name)}()`);
|
|
46
|
-
const fn = homebridge?.[name];
|
|
47
|
-
if (typeof fn === "function") {
|
|
48
|
-
uiLog.info(`[callUiMethod] Invoking homebridge.${String(name)}()`);
|
|
49
|
-
fn.apply(homebridge, args);
|
|
50
|
-
} else {
|
|
51
|
-
uiLog.warn(`[callUiMethod] homebridge[${String(name)}] is not a function.`);
|
|
52
|
-
}
|
|
53
|
-
} else {
|
|
54
|
-
uiLog.warn(`[callUiMethod] homebridge[${String(name)}] is not a function.`);
|
|
55
|
-
}
|
|
56
|
-
} catch (e) {
|
|
57
|
-
uiLog.warn(`Homebridge UI method ${String(name)} failed:`, e);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
function showBusyUi() {
|
|
61
|
-
callUiMethod("disableSaveButton");
|
|
62
|
-
callUiMethod("showSpinner");
|
|
63
|
-
}
|
|
64
|
-
function hideBusyUi() {
|
|
65
|
-
callUiMethod("hideSpinner");
|
|
66
|
-
callUiMethod("enableSaveButton");
|
|
67
|
-
}
|
|
68
|
-
var init_modal = __esm({
|
|
69
|
-
"src/homebridge-ui/public/js/modal.ts"() {
|
|
70
|
-
"use strict";
|
|
71
|
-
init_types();
|
|
72
|
-
init_logger();
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// src/homebridge-ui/public/js/toast.ts
|
|
77
|
-
function showToast(method, message, title = "SwitchBot") {
|
|
78
|
-
try {
|
|
79
|
-
const hb = typeof window !== "undefined" ? window.homebridge : void 0;
|
|
80
|
-
const toast = hb && typeof hb.toast === "object" ? hb.toast : void 0;
|
|
81
|
-
const fn = toast && typeof toast[method] === "function" ? toast[method] : void 0;
|
|
82
|
-
if (fn) {
|
|
83
|
-
try {
|
|
84
|
-
fn(message, title);
|
|
85
|
-
return;
|
|
86
|
-
} catch (err) {
|
|
87
|
-
uiLog.warn(`Toast ${method} threw:`, err);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
uiLog.info(`[Toast:${method}] ${title} - ${message}`);
|
|
91
|
-
} catch (e) {
|
|
92
|
-
uiLog.warn(`Toast ${method} outer error:`, e);
|
|
93
|
-
uiLog.info(`[Toast:${method}] ${title} - ${message}`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
function toastSuccess(message, title) {
|
|
97
|
-
showToast("success", message, title);
|
|
98
|
-
}
|
|
99
|
-
function toastError(message, title) {
|
|
100
|
-
showToast("error", message, title);
|
|
101
|
-
}
|
|
102
|
-
function toastWarning(message, title) {
|
|
103
|
-
showToast("warning", message, title);
|
|
104
|
-
}
|
|
105
|
-
function toastInfo(message, title) {
|
|
106
|
-
showToast("info", message, title);
|
|
107
|
-
}
|
|
108
|
-
var init_toast = __esm({
|
|
109
|
-
"src/homebridge-ui/public/js/toast.ts"() {
|
|
110
|
-
"use strict";
|
|
111
|
-
init_types();
|
|
112
|
-
init_logger();
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
11
|
// src/device-types.js
|
|
117
12
|
function getValidDeviceTypes() {
|
|
118
13
|
const validTypes = /* @__PURE__ */ new Set();
|
|
@@ -354,6 +249,91 @@ var init_device_types = __esm({
|
|
|
354
249
|
}
|
|
355
250
|
});
|
|
356
251
|
|
|
252
|
+
// src/homebridge-ui/utils/v4-detection.ts
|
|
253
|
+
function isV4Config(platform) {
|
|
254
|
+
if (!platform || typeof platform !== "object") {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
const hasCredentials = platform.credentials !== void 0 && typeof platform.credentials === "object";
|
|
258
|
+
const hasOptionsDevices = platform.options !== void 0 && typeof platform.options === "object" && Array.isArray(platform.options.devices);
|
|
259
|
+
return hasCredentials || hasOptionsDevices;
|
|
260
|
+
}
|
|
261
|
+
var init_v4_detection = __esm({
|
|
262
|
+
"src/homebridge-ui/utils/v4-detection.ts"() {
|
|
263
|
+
"use strict";
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// src/homebridge-ui/public/js/types.ts
|
|
268
|
+
var init_types = __esm({
|
|
269
|
+
"src/homebridge-ui/public/js/types.ts"() {
|
|
270
|
+
"use strict";
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// src/homebridge-ui/public/js/logger.ts
|
|
275
|
+
var PREFIX, uiLog;
|
|
276
|
+
var init_logger = __esm({
|
|
277
|
+
"src/homebridge-ui/public/js/logger.ts"() {
|
|
278
|
+
"use strict";
|
|
279
|
+
PREFIX = "[SwitchBot UI/html]";
|
|
280
|
+
uiLog = {
|
|
281
|
+
info: (message, ...parameters) => {
|
|
282
|
+
console.log(PREFIX, message, ...parameters);
|
|
283
|
+
},
|
|
284
|
+
warn: (message, ...parameters) => {
|
|
285
|
+
console.warn(PREFIX, message, ...parameters);
|
|
286
|
+
},
|
|
287
|
+
error: (message, ...parameters) => {
|
|
288
|
+
console.error(PREFIX, message, ...parameters);
|
|
289
|
+
},
|
|
290
|
+
debug: (message, ...parameters) => {
|
|
291
|
+
console.debug(PREFIX, message, ...parameters);
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// src/homebridge-ui/public/js/toast.ts
|
|
298
|
+
function showToast(method, message, title = "SwitchBot") {
|
|
299
|
+
try {
|
|
300
|
+
const hb = typeof window !== "undefined" ? window.homebridge : void 0;
|
|
301
|
+
const toast = hb && typeof hb.toast === "object" ? hb.toast : void 0;
|
|
302
|
+
const fn = toast && typeof toast[method] === "function" ? toast[method] : void 0;
|
|
303
|
+
if (fn) {
|
|
304
|
+
try {
|
|
305
|
+
fn(message, title);
|
|
306
|
+
return;
|
|
307
|
+
} catch (err) {
|
|
308
|
+
uiLog.warn(`Toast ${method} threw:`, err);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
uiLog.info(`[Toast:${method}] ${title} - ${message}`);
|
|
312
|
+
} catch (e) {
|
|
313
|
+
uiLog.warn(`Toast ${method} outer error:`, e);
|
|
314
|
+
uiLog.info(`[Toast:${method}] ${title} - ${message}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function toastSuccess(message, title) {
|
|
318
|
+
showToast("success", message, title);
|
|
319
|
+
}
|
|
320
|
+
function toastError(message, title) {
|
|
321
|
+
showToast("error", message, title);
|
|
322
|
+
}
|
|
323
|
+
function toastWarning(message, title) {
|
|
324
|
+
showToast("warning", message, title);
|
|
325
|
+
}
|
|
326
|
+
function toastInfo(message, title) {
|
|
327
|
+
showToast("info", message, title);
|
|
328
|
+
}
|
|
329
|
+
var init_toast = __esm({
|
|
330
|
+
"src/homebridge-ui/public/js/toast.ts"() {
|
|
331
|
+
"use strict";
|
|
332
|
+
init_types();
|
|
333
|
+
init_logger();
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
|
|
357
337
|
// src/homebridge-ui/public/js/api.ts
|
|
358
338
|
var api_exports = {};
|
|
359
339
|
__export(api_exports, {
|
|
@@ -361,11 +341,12 @@ __export(api_exports, {
|
|
|
361
341
|
addDevicesInBulk: () => addDevicesInBulk,
|
|
362
342
|
deleteAllDevices: () => deleteAllDevices,
|
|
363
343
|
deleteDevice: () => deleteDevice,
|
|
344
|
+
detectV4Config: () => detectV4Config,
|
|
364
345
|
discoverDevices: () => discoverDevices,
|
|
365
346
|
fetchBluetoothStatus: () => fetchBluetoothStatus,
|
|
366
347
|
fetchCredentialStatus: () => fetchCredentialStatus,
|
|
367
348
|
fetchDevices: () => fetchDevices,
|
|
368
|
-
saveCredentials: () =>
|
|
349
|
+
saveCredentials: () => saveCredentials,
|
|
369
350
|
syncParentPluginConfigFromDisk: () => syncParentPluginConfigFromDisk,
|
|
370
351
|
testDeviceConnection: () => testDeviceConnection,
|
|
371
352
|
updateDevice: () => updateDevice,
|
|
@@ -388,6 +369,25 @@ async function fetchDevices() {
|
|
|
388
369
|
return [];
|
|
389
370
|
}
|
|
390
371
|
}
|
|
372
|
+
async function detectV4Config() {
|
|
373
|
+
try {
|
|
374
|
+
if (typeof homebridge.getPluginConfig !== "function") {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
const configArr = await homebridge.getPluginConfig();
|
|
378
|
+
if (!Array.isArray(configArr)) {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
const config = configArr.find(isSwitchBotPlatformConfig);
|
|
382
|
+
if (!config || typeof config !== "object") {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
return isV4Config(config);
|
|
386
|
+
} catch (e) {
|
|
387
|
+
uiLog.warn("Error detecting v4 config:", e);
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
391
|
function validateAndFixDeviceTypes(devices) {
|
|
392
392
|
const errors = [];
|
|
393
393
|
for (const d of devices) {
|
|
@@ -457,7 +457,7 @@ async function fetchCredentialStatus() {
|
|
|
457
457
|
return null;
|
|
458
458
|
}
|
|
459
459
|
}
|
|
460
|
-
async function
|
|
460
|
+
async function saveCredentials(token, secret) {
|
|
461
461
|
uiLog.info("Saving credentials...");
|
|
462
462
|
const resp = await homebridge.request("/credentials", { token, secret });
|
|
463
463
|
uiLog.info("Save response:", resp);
|
|
@@ -532,6 +532,24 @@ async function addDevice(deviceId, name, type, options) {
|
|
|
532
532
|
if (options?.keyId) {
|
|
533
533
|
newDevice.keyId = options.keyId;
|
|
534
534
|
}
|
|
535
|
+
if (options?.connectionPreference) {
|
|
536
|
+
newDevice.connectionPreference = options.connectionPreference;
|
|
537
|
+
}
|
|
538
|
+
if (options?.externalPublishProtocol) {
|
|
539
|
+
newDevice.externalPublishProtocol = options.externalPublishProtocol;
|
|
540
|
+
}
|
|
541
|
+
if (options?.room) {
|
|
542
|
+
newDevice.room = options.room;
|
|
543
|
+
}
|
|
544
|
+
if (typeof options?.refreshRate === "number" && Number.isFinite(options.refreshRate)) {
|
|
545
|
+
newDevice.refreshRate = options.refreshRate;
|
|
546
|
+
}
|
|
547
|
+
if (typeof options?.blePollingEnabled === "boolean") {
|
|
548
|
+
newDevice.blePollingEnabled = options.blePollingEnabled;
|
|
549
|
+
}
|
|
550
|
+
if (typeof options?.blePollIntervalMs === "number" && Number.isFinite(options.blePollIntervalMs)) {
|
|
551
|
+
newDevice.blePollIntervalMs = options.blePollIntervalMs;
|
|
552
|
+
}
|
|
535
553
|
config.devices.push(newDevice);
|
|
536
554
|
await homebridge.updatePluginConfig(configArr);
|
|
537
555
|
if (typeof homebridge.savePluginConfig === "function") {
|
|
@@ -639,12 +657,48 @@ var init_api = __esm({
|
|
|
639
657
|
"src/homebridge-ui/public/js/api.ts"() {
|
|
640
658
|
"use strict";
|
|
641
659
|
init_device_types();
|
|
660
|
+
init_v4_detection();
|
|
642
661
|
init_types();
|
|
643
662
|
init_logger();
|
|
644
663
|
init_toast();
|
|
645
664
|
}
|
|
646
665
|
});
|
|
647
666
|
|
|
667
|
+
// src/homebridge-ui/public/js/modal.ts
|
|
668
|
+
function callUiMethod(name, ...args) {
|
|
669
|
+
try {
|
|
670
|
+
if (typeof homebridge?.[name] === "function") {
|
|
671
|
+
uiLog.info(`[callUiMethod] Invoking homebridge.${String(name)}()`);
|
|
672
|
+
const fn = homebridge?.[name];
|
|
673
|
+
if (typeof fn === "function") {
|
|
674
|
+
uiLog.info(`[callUiMethod] Invoking homebridge.${String(name)}()`);
|
|
675
|
+
fn.apply(homebridge, args);
|
|
676
|
+
} else {
|
|
677
|
+
uiLog.warn(`[callUiMethod] homebridge[${String(name)}] is not a function.`);
|
|
678
|
+
}
|
|
679
|
+
} else {
|
|
680
|
+
uiLog.warn(`[callUiMethod] homebridge[${String(name)}] is not a function.`);
|
|
681
|
+
}
|
|
682
|
+
} catch (e) {
|
|
683
|
+
uiLog.warn(`Homebridge UI method ${String(name)} failed:`, e);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
function showBusyUi() {
|
|
687
|
+
callUiMethod("disableSaveButton");
|
|
688
|
+
callUiMethod("showSpinner");
|
|
689
|
+
}
|
|
690
|
+
function hideBusyUi() {
|
|
691
|
+
callUiMethod("hideSpinner");
|
|
692
|
+
callUiMethod("enableSaveButton");
|
|
693
|
+
}
|
|
694
|
+
var init_modal = __esm({
|
|
695
|
+
"src/homebridge-ui/public/js/modal.ts"() {
|
|
696
|
+
"use strict";
|
|
697
|
+
init_types();
|
|
698
|
+
init_logger();
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
|
|
648
702
|
// src/homebridge-ui/public/js/discovery.ts
|
|
649
703
|
var discovery_exports = {};
|
|
650
704
|
__export(discovery_exports, {
|
|
@@ -1892,6 +1946,27 @@ async function importDiscoveredDevice(device) {
|
|
|
1892
1946
|
opt.selected = (device.connectionPreference || "auto") === val;
|
|
1893
1947
|
connectionPrefSelect.appendChild(opt);
|
|
1894
1948
|
});
|
|
1949
|
+
const externalPublishLabel = document.createElement("label");
|
|
1950
|
+
externalPublishLabel.textContent = "Publish as External Device";
|
|
1951
|
+
externalPublishLabel.style.display = "block";
|
|
1952
|
+
externalPublishLabel.style.marginBottom = "6px";
|
|
1953
|
+
externalPublishLabel.style.fontWeight = "500";
|
|
1954
|
+
externalPublishLabel.style.fontSize = "12px";
|
|
1955
|
+
externalPublishLabel.style.color = "#6b7280";
|
|
1956
|
+
const externalPublishSelect = document.createElement("select");
|
|
1957
|
+
externalPublishSelect.style.width = "100%";
|
|
1958
|
+
externalPublishSelect.style.marginBottom = "12px";
|
|
1959
|
+
externalPublishSelect.style.padding = "8px 10px";
|
|
1960
|
+
externalPublishSelect.style.borderRadius = "6px";
|
|
1961
|
+
externalPublishSelect.style.fontSize = "14px";
|
|
1962
|
+
externalPublishSelect.style.boxSizing = "border-box";
|
|
1963
|
+
["none", "hap", "matter"].forEach((val) => {
|
|
1964
|
+
const opt = document.createElement("option");
|
|
1965
|
+
opt.value = val;
|
|
1966
|
+
opt.text = val === "none" ? "None" : val.toUpperCase();
|
|
1967
|
+
opt.selected = (device.externalPublishProtocol || "none") === val;
|
|
1968
|
+
externalPublishSelect.appendChild(opt);
|
|
1969
|
+
});
|
|
1895
1970
|
const roomLabel = document.createElement("label");
|
|
1896
1971
|
roomLabel.textContent = "Room";
|
|
1897
1972
|
roomLabel.style.display = "block";
|
|
@@ -2023,6 +2098,7 @@ async function importDiscoveredDevice(device) {
|
|
|
2023
2098
|
configDeviceType: typeSelect.value || device.type,
|
|
2024
2099
|
address: macInput.value || void 0,
|
|
2025
2100
|
connectionPreference: connectionPrefSelect.value || void 0,
|
|
2101
|
+
externalPublishProtocol: externalPublishSelect.value || "none",
|
|
2026
2102
|
room: roomInput.value || void 0,
|
|
2027
2103
|
encryptionKey: encryptionKeyInput.value || void 0,
|
|
2028
2104
|
keyId: keyIdInput.value || void 0,
|
|
@@ -2044,6 +2120,8 @@ async function importDiscoveredDevice(device) {
|
|
|
2044
2120
|
contentDiv.appendChild(typeSelect);
|
|
2045
2121
|
contentDiv.appendChild(connectionPrefLabel);
|
|
2046
2122
|
contentDiv.appendChild(connectionPrefSelect);
|
|
2123
|
+
contentDiv.appendChild(externalPublishLabel);
|
|
2124
|
+
contentDiv.appendChild(externalPublishSelect);
|
|
2047
2125
|
contentDiv.appendChild(roomLabel);
|
|
2048
2126
|
contentDiv.appendChild(roomInput);
|
|
2049
2127
|
contentDiv.appendChild(macLabel);
|
|
@@ -2249,6 +2327,27 @@ async function editDevice(device) {
|
|
|
2249
2327
|
opt.selected = (device.connectionPreference || "auto") === val;
|
|
2250
2328
|
connectionPrefSelect.appendChild(opt);
|
|
2251
2329
|
});
|
|
2330
|
+
const externalPublishLabel = document.createElement("label");
|
|
2331
|
+
externalPublishLabel.textContent = "Publish as External Device";
|
|
2332
|
+
externalPublishLabel.style.display = "block";
|
|
2333
|
+
externalPublishLabel.style.marginBottom = "6px";
|
|
2334
|
+
externalPublishLabel.style.fontWeight = "500";
|
|
2335
|
+
externalPublishLabel.style.fontSize = "12px";
|
|
2336
|
+
externalPublishLabel.style.color = "#6b7280";
|
|
2337
|
+
const externalPublishSelect = document.createElement("select");
|
|
2338
|
+
externalPublishSelect.style.width = "100%";
|
|
2339
|
+
externalPublishSelect.style.marginBottom = "12px";
|
|
2340
|
+
externalPublishSelect.style.padding = "8px 10px";
|
|
2341
|
+
externalPublishSelect.style.borderRadius = "6px";
|
|
2342
|
+
externalPublishSelect.style.fontSize = "14px";
|
|
2343
|
+
externalPublishSelect.style.boxSizing = "border-box";
|
|
2344
|
+
["none", "hap", "matter"].forEach((val) => {
|
|
2345
|
+
const opt = document.createElement("option");
|
|
2346
|
+
opt.value = val;
|
|
2347
|
+
opt.text = val === "none" ? "None" : val.toUpperCase();
|
|
2348
|
+
opt.selected = (device.externalPublishProtocol || "none") === val;
|
|
2349
|
+
externalPublishSelect.appendChild(opt);
|
|
2350
|
+
});
|
|
2252
2351
|
const roomLabel = document.createElement("label");
|
|
2253
2352
|
roomLabel.textContent = "Room";
|
|
2254
2353
|
roomLabel.style.display = "block";
|
|
@@ -2337,6 +2436,7 @@ async function editDevice(device) {
|
|
|
2337
2436
|
configDeviceName: nameInput.value || void 0,
|
|
2338
2437
|
configDeviceType: typeSelect.value,
|
|
2339
2438
|
connectionPreference: connectionPrefSelect.value,
|
|
2439
|
+
externalPublishProtocol: externalPublishSelect.value,
|
|
2340
2440
|
room: roomInput.value || void 0,
|
|
2341
2441
|
encryptionKey: encryptionKeyInput.value || void 0,
|
|
2342
2442
|
keyId: keyIdInput.value || void 0,
|
|
@@ -2351,6 +2451,9 @@ async function editDevice(device) {
|
|
|
2351
2451
|
if (params.room !== void 0) {
|
|
2352
2452
|
options.room = params.room;
|
|
2353
2453
|
}
|
|
2454
|
+
if (params.externalPublishProtocol !== void 0) {
|
|
2455
|
+
options.externalPublishProtocol = params.externalPublishProtocol;
|
|
2456
|
+
}
|
|
2354
2457
|
if (params.encryptionKey !== void 0) {
|
|
2355
2458
|
options.encryptionKey = params.encryptionKey;
|
|
2356
2459
|
}
|
|
@@ -2395,6 +2498,8 @@ async function editDevice(device) {
|
|
|
2395
2498
|
contentDiv.appendChild(typeSelect);
|
|
2396
2499
|
contentDiv.appendChild(connectionPrefLabel);
|
|
2397
2500
|
contentDiv.appendChild(connectionPrefSelect);
|
|
2501
|
+
contentDiv.appendChild(externalPublishLabel);
|
|
2502
|
+
contentDiv.appendChild(externalPublishSelect);
|
|
2398
2503
|
contentDiv.appendChild(roomLabel);
|
|
2399
2504
|
contentDiv.appendChild(roomInput);
|
|
2400
2505
|
contentDiv.appendChild(encryptionKeyLabel);
|
|
@@ -3439,8 +3544,9 @@ function renderDeviceList(list) {
|
|
|
3439
3544
|
meta.style.fontSize = "11px";
|
|
3440
3545
|
const typeText = d.type ? `type: ${d.type}` : "";
|
|
3441
3546
|
const connText = d.connectionPreference ? `conn: ${d.connectionPreference}` : "";
|
|
3547
|
+
const extText = d.externalPublishProtocol && d.externalPublishProtocol !== "none" ? `external: ${d.externalPublishProtocol}` : "";
|
|
3442
3548
|
const roomText = d.room ? `room: ${d.room}` : "";
|
|
3443
|
-
meta.textContent = [typeText, connText, roomText].filter(Boolean).join(" | ");
|
|
3549
|
+
meta.textContent = [typeText, connText, extText, roomText].filter(Boolean).join(" | ");
|
|
3444
3550
|
info.appendChild(nameContainer);
|
|
3445
3551
|
info.appendChild(meta);
|
|
3446
3552
|
info.appendChild(expandedDetails);
|
|
@@ -3537,8 +3643,14 @@ async function addDeviceToConfig2(device, options = {}) {
|
|
|
3537
3643
|
address: importValues.address,
|
|
3538
3644
|
model: device.model,
|
|
3539
3645
|
rssi: device.rssi,
|
|
3646
|
+
connectionPreference: importValues.connectionPreference,
|
|
3647
|
+
externalPublishProtocol: importValues.externalPublishProtocol,
|
|
3648
|
+
room: importValues.room,
|
|
3540
3649
|
encryptionKey: importValues.encryptionKey,
|
|
3541
|
-
keyId: importValues.keyId
|
|
3650
|
+
keyId: importValues.keyId,
|
|
3651
|
+
refreshRate: importValues.refreshRate,
|
|
3652
|
+
blePollingEnabled: importValues.blePollingEnabled,
|
|
3653
|
+
blePollIntervalMs: importValues.blePollIntervalMs
|
|
3542
3654
|
});
|
|
3543
3655
|
uiLog.info("Add device response:", resp);
|
|
3544
3656
|
const alreadyExists = !!resp?.alreadyExists;
|
|
@@ -3609,6 +3721,9 @@ var init_devices = __esm({
|
|
|
3609
3721
|
}
|
|
3610
3722
|
});
|
|
3611
3723
|
|
|
3724
|
+
// src/homebridge-ui/public/js/app.ts
|
|
3725
|
+
init_api();
|
|
3726
|
+
|
|
3612
3727
|
// src/homebridge-ui/public/js/credentials.ts
|
|
3613
3728
|
init_logger();
|
|
3614
3729
|
init_modal();
|
|
@@ -3646,7 +3761,7 @@ async function loadCredentialStatus() {
|
|
|
3646
3761
|
uiLog.error("Error loading credentials:", e);
|
|
3647
3762
|
}
|
|
3648
3763
|
}
|
|
3649
|
-
async function
|
|
3764
|
+
async function saveCredentials2() {
|
|
3650
3765
|
const token = document.getElementById("token")?.value;
|
|
3651
3766
|
const secret = document.getElementById("secret")?.value;
|
|
3652
3767
|
const saveStatus = document.getElementById("saveStatus");
|
|
@@ -3706,9 +3821,20 @@ async function saveCredentials() {
|
|
|
3706
3821
|
init_devices();
|
|
3707
3822
|
init_discovery();
|
|
3708
3823
|
window.loadCredentialStatus = loadCredentialStatus;
|
|
3709
|
-
window.saveCredentials =
|
|
3824
|
+
window.saveCredentials = saveCredentials2;
|
|
3710
3825
|
window.discoverDevices = discoverDevices2;
|
|
3826
|
+
async function checkV4Config() {
|
|
3827
|
+
const banner = document.getElementById("v4ConfigWarning");
|
|
3828
|
+
if (!banner) {
|
|
3829
|
+
return;
|
|
3830
|
+
}
|
|
3831
|
+
const isV4 = await detectV4Config();
|
|
3832
|
+
if (isV4) {
|
|
3833
|
+
banner.style.display = "block";
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3711
3836
|
async function init() {
|
|
3837
|
+
await checkV4Config();
|
|
3712
3838
|
await loadCredentialStatus();
|
|
3713
3839
|
await initializeDiscoverySettings();
|
|
3714
3840
|
await loadConfiguredDevices();
|