homebridge-plugin-utils 1.10.2 → 1.11.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/dist/featureoptions.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Nullable } from "./util.js";
|
|
1
2
|
export interface FeatureOptionEntry {
|
|
2
3
|
default: boolean;
|
|
3
4
|
defaultValue?: number | string;
|
|
@@ -65,9 +66,9 @@ export declare class FeatureOptions {
|
|
|
65
66
|
* @param device - Optional device scope identifier.
|
|
66
67
|
* @param controller - Optional controller scope identifier.
|
|
67
68
|
*
|
|
68
|
-
* @returns Returns the value of a value-centric option as a floating point number
|
|
69
|
+
* @returns Returns the value of a value-centric option as a floating point number, `undefined` if it doesn't exist or couldn't be parsed, and `null` if disabled.
|
|
69
70
|
*/
|
|
70
|
-
getFloat(option: string, device?: string, controller?: string): number | undefined
|
|
71
|
+
getFloat(option: string, device?: string, controller?: string): Nullable<number | undefined>;
|
|
71
72
|
/**
|
|
72
73
|
* Parse an integer feature option value.
|
|
73
74
|
*
|
|
@@ -75,9 +76,9 @@ export declare class FeatureOptions {
|
|
|
75
76
|
* @param device - Optional device scope identifier.
|
|
76
77
|
* @param controller - Optional controller scope identifier.
|
|
77
78
|
*
|
|
78
|
-
* @returns Returns the value of a value-centric option as an integer
|
|
79
|
+
* @returns Returns the value of a value-centric option as an integer, `undefined` if it doesn't exist or couldn't be parsed, and `null` if disabled.
|
|
79
80
|
*/
|
|
80
|
-
getInteger(option: string, device?: string, controller?: string): number | undefined
|
|
81
|
+
getInteger(option: string, device?: string, controller?: string): Nullable<number | undefined>;
|
|
81
82
|
/**
|
|
82
83
|
* Return whether an option has been set in either the device or controller scope context.
|
|
83
84
|
*
|
|
@@ -129,9 +130,10 @@ export declare class FeatureOptions {
|
|
|
129
130
|
* @param device - Optional device scope identifier.
|
|
130
131
|
* @param controller - Optional controller scope identifier.
|
|
131
132
|
*
|
|
132
|
-
* @returns Returns the current value associated with `option`
|
|
133
|
+
* @returns Returns the current value associated with `option` if the feature option is enabled, `null` if disabled (or not a value-centric feature option), or
|
|
134
|
+
* `undefined` if it's not specified.
|
|
133
135
|
*/
|
|
134
|
-
value(option: string, device?: string, controller?: string): string | undefined
|
|
136
|
+
value(option: string, device?: string, controller?: string): Nullable<string | undefined>;
|
|
135
137
|
/**
|
|
136
138
|
* Return the list of available feature option categories.
|
|
137
139
|
*
|
package/dist/featureoptions.js
CHANGED
|
@@ -50,16 +50,8 @@ export class FeatureOptions {
|
|
|
50
50
|
* @returns Returns true or false, depending on the option default.
|
|
51
51
|
*/
|
|
52
52
|
defaultValue(option) {
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
return this.defaultReturnValue;
|
|
56
|
-
}
|
|
57
|
-
const value = this.defaults[option.toLowerCase()];
|
|
58
|
-
// If it's unknown to us, assume it's true.
|
|
59
|
-
if (value === undefined) {
|
|
60
|
-
return this.defaultReturnValue;
|
|
61
|
-
}
|
|
62
|
-
return value;
|
|
53
|
+
// If it's unknown to us, return the default return value.
|
|
54
|
+
return this.defaults[option.toLowerCase()] ?? this.defaultReturnValue;
|
|
63
55
|
}
|
|
64
56
|
/**
|
|
65
57
|
* Return whether the option explicitly exists in the list of configured options.
|
|
@@ -96,7 +88,7 @@ export class FeatureOptions {
|
|
|
96
88
|
* @param device - Optional device scope identifier.
|
|
97
89
|
* @param controller - Optional controller scope identifier.
|
|
98
90
|
*
|
|
99
|
-
* @returns Returns the value of a value-centric option as a floating point number
|
|
91
|
+
* @returns Returns the value of a value-centric option as a floating point number, `undefined` if it doesn't exist or couldn't be parsed, and `null` if disabled.
|
|
100
92
|
*/
|
|
101
93
|
getFloat(option, device, controller) {
|
|
102
94
|
// Parse the number and return the value.
|
|
@@ -109,7 +101,7 @@ export class FeatureOptions {
|
|
|
109
101
|
* @param device - Optional device scope identifier.
|
|
110
102
|
* @param controller - Optional controller scope identifier.
|
|
111
103
|
*
|
|
112
|
-
* @returns Returns the value of a value-centric option as an integer
|
|
104
|
+
* @returns Returns the value of a value-centric option as an integer, `undefined` if it doesn't exist or couldn't be parsed, and `null` if disabled.
|
|
113
105
|
*/
|
|
114
106
|
getInteger(option, device, controller) {
|
|
115
107
|
// Parse the number and return the value.
|
|
@@ -147,7 +139,10 @@ export class FeatureOptions {
|
|
|
147
139
|
* @returns Returns true if it is a value-centric option and false otherwise.
|
|
148
140
|
*/
|
|
149
141
|
isValue(option) {
|
|
150
|
-
|
|
142
|
+
if (!option) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
return option.toLowerCase() in this.valueOptions;
|
|
151
146
|
}
|
|
152
147
|
/**
|
|
153
148
|
* Return the scope hierarchy location of an option.
|
|
@@ -180,20 +175,24 @@ export class FeatureOptions {
|
|
|
180
175
|
* @param device - Optional device scope identifier.
|
|
181
176
|
* @param controller - Optional controller scope identifier.
|
|
182
177
|
*
|
|
183
|
-
* @returns Returns the current value associated with `option`
|
|
178
|
+
* @returns Returns the current value associated with `option` if the feature option is enabled, `null` if disabled (or not a value-centric feature option), or
|
|
179
|
+
* `undefined` if it's not specified.
|
|
184
180
|
*/
|
|
185
181
|
value(option, device, controller) {
|
|
186
182
|
// If this isn't a value-centric feature option, we're done.
|
|
187
183
|
if (!this.isValue(option)) {
|
|
188
|
-
return
|
|
184
|
+
return null;
|
|
189
185
|
}
|
|
186
|
+
// Normalize the option.
|
|
187
|
+
option = option.toLowerCase();
|
|
190
188
|
const getValue = (checkOption, checkId) => {
|
|
191
189
|
const regex = this.valueRegex(checkOption, checkId);
|
|
192
190
|
// Get the option value, if we have one.
|
|
193
191
|
for (const entry of this.configuredOptions) {
|
|
194
192
|
const regexMatch = regex.exec(entry);
|
|
195
193
|
if (regexMatch) {
|
|
196
|
-
return
|
|
194
|
+
// If the option is enabled, return the value. Otherwise, we have nothing.
|
|
195
|
+
return (regexMatch[1].toLowerCase() === "enable") ? regexMatch[2] : null;
|
|
197
196
|
}
|
|
198
197
|
}
|
|
199
198
|
return undefined;
|
|
@@ -201,6 +200,10 @@ export class FeatureOptions {
|
|
|
201
200
|
// Check to see if we have a device-level value first.
|
|
202
201
|
if (device) {
|
|
203
202
|
const value = getValue(option, device);
|
|
203
|
+
// The option must been explicitly disabled.
|
|
204
|
+
if (value === null) {
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
204
207
|
if (value) {
|
|
205
208
|
return value;
|
|
206
209
|
}
|
|
@@ -208,12 +211,25 @@ export class FeatureOptions {
|
|
|
208
211
|
// Now check to see if we have an controller-level value.
|
|
209
212
|
if (controller) {
|
|
210
213
|
const value = getValue(option, controller);
|
|
214
|
+
// The option's been explicitly disabled.
|
|
215
|
+
if (value === null) {
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
211
218
|
if (value) {
|
|
212
219
|
return value;
|
|
213
220
|
}
|
|
214
221
|
}
|
|
215
222
|
// Finally, we check for a global-level value.
|
|
216
|
-
|
|
223
|
+
const value = getValue(option);
|
|
224
|
+
if (value) {
|
|
225
|
+
return value;
|
|
226
|
+
}
|
|
227
|
+
// The option's been explicitly disabled or is disabled by default.
|
|
228
|
+
if ((value === null) || !this.defaultValue(option)) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
// Return the enabled value, or the default value if we've got nothing explicitly configured.
|
|
232
|
+
return value ?? ((this.valueOptions[option] === undefined) ? undefined : this.valueOptions[option]?.toString());
|
|
217
233
|
}
|
|
218
234
|
/**
|
|
219
235
|
* Return the list of available feature option categories.
|
|
@@ -290,7 +306,9 @@ export class FeatureOptions {
|
|
|
290
306
|
// Index the default value.
|
|
291
307
|
this.defaults[entry.toLowerCase()] = option.default;
|
|
292
308
|
// Track value-centric options.
|
|
293
|
-
|
|
309
|
+
if ("defaultValue" in option) {
|
|
310
|
+
this.valueOptions[entry.toLowerCase()] = option.defaultValue;
|
|
311
|
+
}
|
|
294
312
|
// Cross reference the feature option group it belongs to, if any.
|
|
295
313
|
if (option.group !== undefined) {
|
|
296
314
|
const expandedGroup = category.name + (option.group.length ? ("." + option.group) : "");
|
|
@@ -336,11 +354,7 @@ export class FeatureOptions {
|
|
|
336
354
|
// Utility to test whether an option is set in a given scope.
|
|
337
355
|
// We return true if an option is enabled, false for disabled, undefined otherwise. For value-centric options, we return true if a value exists.
|
|
338
356
|
isOptionEnabled(option, id) {
|
|
339
|
-
|
|
340
|
-
if (this.isValue(option)) {
|
|
341
|
-
return this.exists(option, id);
|
|
342
|
-
}
|
|
343
|
-
const regex = this.optionRegex(option, id);
|
|
357
|
+
const regex = this.isValue(option) ? this.valueRegex(option, id) : this.optionRegex(option, id);
|
|
344
358
|
// Get the option value, if we have one.
|
|
345
359
|
for (const entry of this.configuredOptions) {
|
|
346
360
|
const regexMatch = regex.exec(entry);
|
|
@@ -358,9 +372,9 @@ export class FeatureOptions {
|
|
|
358
372
|
}
|
|
359
373
|
// Utility function to parse and return a numeric configuration parameter.
|
|
360
374
|
parseOptionNumeric(option, convert) {
|
|
361
|
-
//
|
|
362
|
-
if (option
|
|
363
|
-
return undefined;
|
|
375
|
+
// If the option is disabled or we don't have it configured -- we're done.
|
|
376
|
+
if (!option) {
|
|
377
|
+
return (option === null) ? null : undefined;
|
|
364
378
|
}
|
|
365
379
|
// Convert it to a number, if needed.
|
|
366
380
|
const convertedValue = convert(option);
|
|
@@ -374,7 +388,7 @@ export class FeatureOptions {
|
|
|
374
388
|
// Regular expression test for value-centric feature options.
|
|
375
389
|
valueRegex(option, id) {
|
|
376
390
|
// This regular expression is a bit more intricate than you might think it should be due to the need to ensure we capture values at the very end of the option.
|
|
377
|
-
return new RegExp("^Enable\\." + option.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + (!id ? "" : "\\." + id) + "
|
|
391
|
+
return new RegExp("^(Disable|Enable)\\." + option.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + (!id ? "" : "\\." + id) + "(?:\\.([^\\.]+))?$", "gi");
|
|
378
392
|
}
|
|
379
393
|
}
|
|
380
394
|
//# sourceMappingURL=featureoptions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"featureoptions.js","sourceRoot":"","sources":["../src/featureoptions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"featureoptions.js","sourceRoot":"","sources":["../src/featureoptions.ts"],"names":[],"mappings":"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"}
|
|
@@ -50,16 +50,8 @@ export class FeatureOptions {
|
|
|
50
50
|
* @returns Returns true or false, depending on the option default.
|
|
51
51
|
*/
|
|
52
52
|
defaultValue(option) {
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
return this.defaultReturnValue;
|
|
56
|
-
}
|
|
57
|
-
const value = this.defaults[option.toLowerCase()];
|
|
58
|
-
// If it's unknown to us, assume it's true.
|
|
59
|
-
if (value === undefined) {
|
|
60
|
-
return this.defaultReturnValue;
|
|
61
|
-
}
|
|
62
|
-
return value;
|
|
53
|
+
// If it's unknown to us, return the default return value.
|
|
54
|
+
return this.defaults[option.toLowerCase()] ?? this.defaultReturnValue;
|
|
63
55
|
}
|
|
64
56
|
/**
|
|
65
57
|
* Return whether the option explicitly exists in the list of configured options.
|
|
@@ -96,7 +88,7 @@ export class FeatureOptions {
|
|
|
96
88
|
* @param device - Optional device scope identifier.
|
|
97
89
|
* @param controller - Optional controller scope identifier.
|
|
98
90
|
*
|
|
99
|
-
* @returns Returns the value of a value-centric option as a floating point number
|
|
91
|
+
* @returns Returns the value of a value-centric option as a floating point number, `undefined` if it doesn't exist or couldn't be parsed, and `null` if disabled.
|
|
100
92
|
*/
|
|
101
93
|
getFloat(option, device, controller) {
|
|
102
94
|
// Parse the number and return the value.
|
|
@@ -109,7 +101,7 @@ export class FeatureOptions {
|
|
|
109
101
|
* @param device - Optional device scope identifier.
|
|
110
102
|
* @param controller - Optional controller scope identifier.
|
|
111
103
|
*
|
|
112
|
-
* @returns Returns the value of a value-centric option as an integer
|
|
104
|
+
* @returns Returns the value of a value-centric option as an integer, `undefined` if it doesn't exist or couldn't be parsed, and `null` if disabled.
|
|
113
105
|
*/
|
|
114
106
|
getInteger(option, device, controller) {
|
|
115
107
|
// Parse the number and return the value.
|
|
@@ -147,7 +139,10 @@ export class FeatureOptions {
|
|
|
147
139
|
* @returns Returns true if it is a value-centric option and false otherwise.
|
|
148
140
|
*/
|
|
149
141
|
isValue(option) {
|
|
150
|
-
|
|
142
|
+
if (!option) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
return option.toLowerCase() in this.valueOptions;
|
|
151
146
|
}
|
|
152
147
|
/**
|
|
153
148
|
* Return the scope hierarchy location of an option.
|
|
@@ -180,20 +175,24 @@ export class FeatureOptions {
|
|
|
180
175
|
* @param device - Optional device scope identifier.
|
|
181
176
|
* @param controller - Optional controller scope identifier.
|
|
182
177
|
*
|
|
183
|
-
* @returns Returns the current value associated with `option`
|
|
178
|
+
* @returns Returns the current value associated with `option` if the feature option is enabled, `null` if disabled (or not a value-centric feature option), or
|
|
179
|
+
* `undefined` if it's not specified.
|
|
184
180
|
*/
|
|
185
181
|
value(option, device, controller) {
|
|
186
182
|
// If this isn't a value-centric feature option, we're done.
|
|
187
183
|
if (!this.isValue(option)) {
|
|
188
|
-
return
|
|
184
|
+
return null;
|
|
189
185
|
}
|
|
186
|
+
// Normalize the option.
|
|
187
|
+
option = option.toLowerCase();
|
|
190
188
|
const getValue = (checkOption, checkId) => {
|
|
191
189
|
const regex = this.valueRegex(checkOption, checkId);
|
|
192
190
|
// Get the option value, if we have one.
|
|
193
191
|
for (const entry of this.configuredOptions) {
|
|
194
192
|
const regexMatch = regex.exec(entry);
|
|
195
193
|
if (regexMatch) {
|
|
196
|
-
return
|
|
194
|
+
// If the option is enabled, return the value. Otherwise, we have nothing.
|
|
195
|
+
return (regexMatch[1].toLowerCase() === "enable") ? regexMatch[2] : null;
|
|
197
196
|
}
|
|
198
197
|
}
|
|
199
198
|
return undefined;
|
|
@@ -201,6 +200,10 @@ export class FeatureOptions {
|
|
|
201
200
|
// Check to see if we have a device-level value first.
|
|
202
201
|
if (device) {
|
|
203
202
|
const value = getValue(option, device);
|
|
203
|
+
// The option must been explicitly disabled.
|
|
204
|
+
if (value === null) {
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
204
207
|
if (value) {
|
|
205
208
|
return value;
|
|
206
209
|
}
|
|
@@ -208,12 +211,25 @@ export class FeatureOptions {
|
|
|
208
211
|
// Now check to see if we have an controller-level value.
|
|
209
212
|
if (controller) {
|
|
210
213
|
const value = getValue(option, controller);
|
|
214
|
+
// The option's been explicitly disabled.
|
|
215
|
+
if (value === null) {
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
211
218
|
if (value) {
|
|
212
219
|
return value;
|
|
213
220
|
}
|
|
214
221
|
}
|
|
215
222
|
// Finally, we check for a global-level value.
|
|
216
|
-
|
|
223
|
+
const value = getValue(option);
|
|
224
|
+
if (value) {
|
|
225
|
+
return value;
|
|
226
|
+
}
|
|
227
|
+
// The option's been explicitly disabled or is disabled by default.
|
|
228
|
+
if ((value === null) || !this.defaultValue(option)) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
// Return the enabled value, or the default value if we've got nothing explicitly configured.
|
|
232
|
+
return value ?? ((this.valueOptions[option] === undefined) ? undefined : this.valueOptions[option]?.toString());
|
|
217
233
|
}
|
|
218
234
|
/**
|
|
219
235
|
* Return the list of available feature option categories.
|
|
@@ -290,7 +306,9 @@ export class FeatureOptions {
|
|
|
290
306
|
// Index the default value.
|
|
291
307
|
this.defaults[entry.toLowerCase()] = option.default;
|
|
292
308
|
// Track value-centric options.
|
|
293
|
-
|
|
309
|
+
if ("defaultValue" in option) {
|
|
310
|
+
this.valueOptions[entry.toLowerCase()] = option.defaultValue;
|
|
311
|
+
}
|
|
294
312
|
// Cross reference the feature option group it belongs to, if any.
|
|
295
313
|
if (option.group !== undefined) {
|
|
296
314
|
const expandedGroup = category.name + (option.group.length ? ("." + option.group) : "");
|
|
@@ -336,11 +354,7 @@ export class FeatureOptions {
|
|
|
336
354
|
// Utility to test whether an option is set in a given scope.
|
|
337
355
|
// We return true if an option is enabled, false for disabled, undefined otherwise. For value-centric options, we return true if a value exists.
|
|
338
356
|
isOptionEnabled(option, id) {
|
|
339
|
-
|
|
340
|
-
if (this.isValue(option)) {
|
|
341
|
-
return this.exists(option, id);
|
|
342
|
-
}
|
|
343
|
-
const regex = this.optionRegex(option, id);
|
|
357
|
+
const regex = this.isValue(option) ? this.valueRegex(option, id) : this.optionRegex(option, id);
|
|
344
358
|
// Get the option value, if we have one.
|
|
345
359
|
for (const entry of this.configuredOptions) {
|
|
346
360
|
const regexMatch = regex.exec(entry);
|
|
@@ -358,9 +372,9 @@ export class FeatureOptions {
|
|
|
358
372
|
}
|
|
359
373
|
// Utility function to parse and return a numeric configuration parameter.
|
|
360
374
|
parseOptionNumeric(option, convert) {
|
|
361
|
-
//
|
|
362
|
-
if (option
|
|
363
|
-
return undefined;
|
|
375
|
+
// If the option is disabled or we don't have it configured -- we're done.
|
|
376
|
+
if (!option) {
|
|
377
|
+
return (option === null) ? null : undefined;
|
|
364
378
|
}
|
|
365
379
|
// Convert it to a number, if needed.
|
|
366
380
|
const convertedValue = convert(option);
|
|
@@ -374,7 +388,7 @@ export class FeatureOptions {
|
|
|
374
388
|
// Regular expression test for value-centric feature options.
|
|
375
389
|
valueRegex(option, id) {
|
|
376
390
|
// This regular expression is a bit more intricate than you might think it should be due to the need to ensure we capture values at the very end of the option.
|
|
377
|
-
return new RegExp("^Enable\\." + option.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + (!id ? "" : "\\." + id) + "
|
|
391
|
+
return new RegExp("^(Disable|Enable)\\." + option.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + (!id ? "" : "\\." + id) + "(?:\\.([^\\.]+))?$", "gi");
|
|
378
392
|
}
|
|
379
393
|
}
|
|
380
394
|
//# sourceMappingURL=featureoptions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"featureoptions.js","sourceRoot":"","sources":["../src/featureoptions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"featureoptions.js","sourceRoot":"","sources":["../src/featureoptions.ts"],"names":[],"mappings":"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"}
|
|
@@ -312,7 +312,7 @@ export class webUiFeatureOptions {
|
|
|
312
312
|
}
|
|
313
313
|
|
|
314
314
|
// The first entry returned by getDevices() must always be the controller.
|
|
315
|
-
this.#controller = this.#devices[0]?.
|
|
315
|
+
this.#controller = this.#devices[0]?.serialNumber ?? null;
|
|
316
316
|
}
|
|
317
317
|
|
|
318
318
|
// Make the UI visible.
|
|
@@ -329,7 +329,7 @@ export class webUiFeatureOptions {
|
|
|
329
329
|
this.#sidebar.showDevices(controller, this.#devices);
|
|
330
330
|
|
|
331
331
|
// Display the feature options to the user.
|
|
332
|
-
this.showDeviceOptions(controller ? this.#devices[0].
|
|
332
|
+
this.showDeviceOptions(controller ? this.#devices[0].serialNumber : "Global Options");
|
|
333
333
|
|
|
334
334
|
// All done. Let the user interact with us.
|
|
335
335
|
homebridge.hideSpinner();
|
|
@@ -345,7 +345,7 @@ export class webUiFeatureOptions {
|
|
|
345
345
|
webUiEntry.parentElement.classList.add("bg-info", "text-white") : webUiEntry.parentElement.classList.remove("bg-info", "text-white"));
|
|
346
346
|
|
|
347
347
|
// Populate the device information info pane.
|
|
348
|
-
const currentDevice = this.#devices.find(device => device.
|
|
348
|
+
const currentDevice = this.#devices.find(device => device.serialNumber === deviceId);
|
|
349
349
|
|
|
350
350
|
// Populate the details view. If there's no device specified, the context is considered global and we hide the device details view.
|
|
351
351
|
if(!currentDevice) {
|
|
@@ -428,13 +428,13 @@ export class webUiFeatureOptions {
|
|
|
428
428
|
checkbox.readOnly = false;
|
|
429
429
|
checkbox.id = featureOption;
|
|
430
430
|
checkbox.name = featureOption;
|
|
431
|
-
checkbox.value = featureOption + (!currentDevice ? "" : ("." + currentDevice.
|
|
431
|
+
checkbox.value = featureOption + (!currentDevice ? "" : ("." + currentDevice.serialNumber));
|
|
432
432
|
|
|
433
433
|
let initialValue = undefined;
|
|
434
434
|
let initialScope;
|
|
435
435
|
|
|
436
436
|
// Determine our initial option scope to show the user what's been set.
|
|
437
|
-
switch(initialScope = this.#featureOptions.scope(featureOption, currentDevice?.
|
|
437
|
+
switch(initialScope = this.#featureOptions.scope(featureOption, currentDevice?.serialNumber, this.#controller)) {
|
|
438
438
|
|
|
439
439
|
case "global":
|
|
440
440
|
case "controller":
|
|
@@ -442,13 +442,11 @@ export class webUiFeatureOptions {
|
|
|
442
442
|
// If we're looking at the global scope, show the option value. Otherwise, we show that we're inheriting a value from the scope above.
|
|
443
443
|
if(!currentDevice) {
|
|
444
444
|
|
|
445
|
+
checkbox.checked = this.#featureOptions.test(featureOption);
|
|
446
|
+
|
|
445
447
|
if(this.#featureOptions.isValue(featureOption)) {
|
|
446
448
|
|
|
447
|
-
checkbox.checked = this.#featureOptions.exists(featureOption);
|
|
448
449
|
initialValue = this.#featureOptions.value(checkbox.id);
|
|
449
|
-
} else {
|
|
450
|
-
|
|
451
|
-
checkbox.checked = this.#featureOptions.test(featureOption);
|
|
452
450
|
}
|
|
453
451
|
|
|
454
452
|
if(checkbox.checked) {
|
|
@@ -472,13 +470,11 @@ export class webUiFeatureOptions {
|
|
|
472
470
|
case "none":
|
|
473
471
|
default:
|
|
474
472
|
|
|
475
|
-
|
|
473
|
+
checkbox.checked = this.#featureOptions.test(featureOption, currentDevice?.serialNumber);
|
|
476
474
|
|
|
477
|
-
|
|
478
|
-
initialValue = this.#featureOptions.value(checkbox.id, currentDevice?.serial);
|
|
479
|
-
} else {
|
|
475
|
+
if(this.#featureOptions.isValue(featureOption)) {
|
|
480
476
|
|
|
481
|
-
|
|
477
|
+
initialValue = this.#featureOptions.value(checkbox.id, currentDevice?.serialNumber);
|
|
482
478
|
}
|
|
483
479
|
|
|
484
480
|
break;
|
|
@@ -515,32 +511,7 @@ export class webUiFeatureOptions {
|
|
|
515
511
|
inputValue.readOnly = !checkbox.checked;
|
|
516
512
|
|
|
517
513
|
// Add or remove the setting from our configuration when we've changed our state.
|
|
518
|
-
inputValue.addEventListener("change",
|
|
519
|
-
|
|
520
|
-
// Find the option in our list and delete it if it exists.
|
|
521
|
-
const optionRegex = new RegExp("^(?:Enable|Disable)\\." + checkbox.id + (!currentDevice ? "" : ("\\." + currentDevice.serial)) + "\\.[^\\.]+$", "gi");
|
|
522
|
-
const newOptions = this.#featureOptions.configuredOptions.filter(entry => !optionRegex.test(entry));
|
|
523
|
-
|
|
524
|
-
if(checkbox.checked) {
|
|
525
|
-
|
|
526
|
-
newOptions.push("Enable." + checkbox.value + "." + inputValue.value);
|
|
527
|
-
} else if(checkbox.indeterminate) {
|
|
528
|
-
|
|
529
|
-
// If we're in an indeterminate state, we need to traverse the tree to get the upstream value we're inheriting.
|
|
530
|
-
inputValue.value = (currentDevice?.serial !== this.#controller) ?
|
|
531
|
-
(this.#featureOptions.value(checkbox.id, this.#controller) ?? this.#featureOptions.value(checkbox.id)) :
|
|
532
|
-
(this.#featureOptions.value(checkbox.id) ?? option.defaultValue);
|
|
533
|
-
} else {
|
|
534
|
-
|
|
535
|
-
inputValue.value = option.defaultValue;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// Update our configuration in Homebridge.
|
|
539
|
-
this.currentConfig[0].options = newOptions;
|
|
540
|
-
this.#featureOptions.configuredOptions = newOptions;
|
|
541
|
-
await homebridge.updatePluginConfig(this.currentConfig);
|
|
542
|
-
});
|
|
543
|
-
|
|
514
|
+
inputValue.addEventListener("change", () => checkbox.dispatchEvent(new Event("change")));
|
|
544
515
|
tdInput.appendChild(inputValue);
|
|
545
516
|
trX.appendChild(tdInput);
|
|
546
517
|
}
|
|
@@ -553,7 +524,7 @@ export class webUiFeatureOptions {
|
|
|
553
524
|
labelDescription.classList.add("user-select-none", "my-0", "py-0");
|
|
554
525
|
|
|
555
526
|
// Highlight options for the user that are different than our defaults.
|
|
556
|
-
const scopeColor = this.#featureOptions.color(featureOption, currentDevice?.
|
|
527
|
+
const scopeColor = this.#featureOptions.color(featureOption, currentDevice?.serialNumber, this.#controller);
|
|
557
528
|
|
|
558
529
|
if(scopeColor) {
|
|
559
530
|
|
|
@@ -564,19 +535,21 @@ export class webUiFeatureOptions {
|
|
|
564
535
|
checkbox.addEventListener("change", async () => {
|
|
565
536
|
|
|
566
537
|
// Find the option in our list and delete it if it exists.
|
|
567
|
-
const optionRegex = new RegExp("^(?:Enable|Disable)\\." + checkbox.id + (!currentDevice ? "" : ("\\." + currentDevice.
|
|
538
|
+
const optionRegex = new RegExp("^(?:Enable|Disable)\\." + checkbox.id + (!currentDevice ? "" : ("\\." + currentDevice.serialNumber)) +
|
|
539
|
+
"(?:\\.([^\\.]+))?$", "gi");
|
|
540
|
+
|
|
568
541
|
const newOptions = this.#featureOptions.configuredOptions.filter(entry => !optionRegex.test(entry));
|
|
569
542
|
|
|
570
543
|
// Figure out if we've got the option set upstream.
|
|
571
544
|
let upstreamOption = false;
|
|
572
545
|
|
|
573
546
|
// We explicitly want to check for the scope of the feature option above where we are now, so we can appropriately determine what we should show.
|
|
574
|
-
switch(this.#featureOptions.scope(checkbox.id, (currentDevice && (currentDevice.
|
|
547
|
+
switch(this.#featureOptions.scope(checkbox.id, (currentDevice && (currentDevice.serialNumber !== this.#controller)) ? this.#controller : undefined)) {
|
|
575
548
|
|
|
576
549
|
case "device":
|
|
577
550
|
case "controller":
|
|
578
551
|
|
|
579
|
-
if(currentDevice.
|
|
552
|
+
if(currentDevice.serialNumber !== this.#controller) {
|
|
580
553
|
|
|
581
554
|
upstreamOption = true;
|
|
582
555
|
}
|
|
@@ -597,30 +570,50 @@ export class webUiFeatureOptions {
|
|
|
597
570
|
break;
|
|
598
571
|
}
|
|
599
572
|
|
|
600
|
-
//
|
|
601
|
-
|
|
602
|
-
|
|
573
|
+
// Always do this for value options.
|
|
574
|
+
if(this.#featureOptions.isValue(featureOption) && inputValue) {
|
|
575
|
+
|
|
576
|
+
upstreamOption = true;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// We're currently in an indetermindate state and transitioning to an unchecked state.
|
|
580
|
+
if(checkbox.readOnly) {
|
|
603
581
|
|
|
604
|
-
//
|
|
605
|
-
//
|
|
582
|
+
// The user wants to change the state to unchecked. We need this because a checkbox can be in both an unchecked and indeterminate simultaneously, so we use
|
|
583
|
+
// the readOnly property to let us know that we've just cycled from an indeterminate state.
|
|
606
584
|
checkbox.checked = checkbox.readOnly = false;
|
|
585
|
+
|
|
586
|
+
// If we have a value-centric feature option, we show the default value when we're in an indeterminate state.
|
|
587
|
+
if(this.#featureOptions.isValue(featureOption) && inputValue) {
|
|
588
|
+
|
|
589
|
+
// If we're unchecked, clear out the value and make it read only. We show the system default for reference.
|
|
590
|
+
inputValue.value = option.defaultValue;
|
|
591
|
+
inputValue.readOnly = true;
|
|
592
|
+
}
|
|
607
593
|
} else if(!checkbox.checked) {
|
|
608
594
|
|
|
595
|
+
// We're currently in a unchecked state and transitioning to an indeterminate state.
|
|
596
|
+
|
|
609
597
|
// If we have an upstream option configured, we reveal a third state to show inheritance of that option and allow the user to select it.
|
|
610
598
|
if(upstreamOption) {
|
|
611
599
|
|
|
612
600
|
// We want to set the readOnly property as well, since it will survive a user interaction when they click the checkbox to clear out the
|
|
613
601
|
// indeterminate state. This allows us to effectively cycle between three states.
|
|
614
602
|
checkbox.readOnly = checkbox.indeterminate = true;
|
|
615
|
-
}
|
|
616
603
|
|
|
617
|
-
|
|
604
|
+
if(this.#featureOptions.isValue(featureOption) && inputValue) {
|
|
618
605
|
|
|
619
|
-
|
|
606
|
+
// If we're in an indeterminate state, we need to traverse the tree to get the upstream value we're inheriting.
|
|
607
|
+
inputValue.value = (currentDevice?.serialNumber !== this.#controller) ?
|
|
608
|
+
(this.#featureOptions.value(checkbox.id, this.#controller) ?? this.#featureOptions.value(checkbox.id)) :
|
|
609
|
+
(this.#featureOptions.value(checkbox.id) ?? option.defaultValue);
|
|
610
|
+
|
|
611
|
+
inputValue.readOnly = true;
|
|
612
|
+
}
|
|
620
613
|
}
|
|
621
614
|
} else if(checkbox.checked) {
|
|
622
615
|
|
|
623
|
-
// We'
|
|
616
|
+
// We're currently in a checked state and transitioning to an unchecked state.
|
|
624
617
|
checkbox.readOnly = checkbox.indeterminate = false;
|
|
625
618
|
|
|
626
619
|
if(this.#featureOptions.isValue(featureOption) && inputValue) {
|
|
@@ -629,36 +622,29 @@ export class webUiFeatureOptions {
|
|
|
629
622
|
}
|
|
630
623
|
}
|
|
631
624
|
|
|
632
|
-
// The
|
|
633
|
-
|
|
625
|
+
// The feature option is different from the default - highlight it for the user, accounting for the scope hierarchy, and add it to our configuration. We
|
|
626
|
+
// provide a visual queue to the user, highlighting to indicate that a non-default option has been set.
|
|
627
|
+
if(!checkbox.indeterminate && ((checkbox.checked !== option.default) ||
|
|
628
|
+
(this.#featureOptions.isValue(featureOption) && inputValue && (inputValue.value !== option.defaultValue)) || upstreamOption)) {
|
|
634
629
|
|
|
635
630
|
labelDescription.classList.add("text-info");
|
|
636
|
-
newOptions.push((checkbox.checked ? "Enable." : "Disable.") + checkbox.value
|
|
631
|
+
newOptions.push((checkbox.checked ? "Enable." : "Disable.") + checkbox.value +
|
|
632
|
+
(this.#featureOptions.isValue(featureOption) && checkbox.checked ? ("." + inputValue.value) : ""));
|
|
637
633
|
} else {
|
|
638
634
|
|
|
639
635
|
// We've reset to the defaults, remove our highlighting.
|
|
640
636
|
labelDescription.classList.remove("text-info");
|
|
641
637
|
}
|
|
642
638
|
|
|
643
|
-
// Update our Homebridge
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
const changeEvent = new Event("change");
|
|
648
|
-
|
|
649
|
-
inputValue.dispatchEvent(changeEvent);
|
|
650
|
-
} else {
|
|
651
|
-
|
|
652
|
-
// Update our configuration in Homebridge.
|
|
653
|
-
this.currentConfig[0].options = newOptions;
|
|
654
|
-
this.#featureOptions.configuredOptions = newOptions;
|
|
655
|
-
await homebridge.updatePluginConfig(this.currentConfig);
|
|
656
|
-
}
|
|
639
|
+
// Update our configuration in Homebridge.
|
|
640
|
+
this.currentConfig[0].options = newOptions;
|
|
641
|
+
this.#featureOptions.configuredOptions = newOptions;
|
|
642
|
+
await homebridge.updatePluginConfig(this.currentConfig);
|
|
657
643
|
|
|
658
644
|
// If we've reset to defaults, make sure our color coding for scope is reflected.
|
|
659
645
|
if((checkbox.checked === option.default) || checkbox.indeterminate) {
|
|
660
646
|
|
|
661
|
-
const scopeColor = this.#featureOptions.color(featureOption, currentDevice?.
|
|
647
|
+
const scopeColor = this.#featureOptions.color(featureOption, currentDevice?.serialNumber, this.#controller);
|
|
662
648
|
|
|
663
649
|
if(scopeColor) {
|
|
664
650
|
|
|
@@ -669,7 +655,7 @@ export class webUiFeatureOptions {
|
|
|
669
655
|
// Adjust visibility of other feature options that depend on us.
|
|
670
656
|
if(this.#featureOptions.groups[checkbox.id]) {
|
|
671
657
|
|
|
672
|
-
const entryVisibility = this.#featureOptions.test(featureOption, currentDevice?.
|
|
658
|
+
const entryVisibility = this.#featureOptions.test(featureOption, currentDevice?.serialNumber) ? "" : "none";
|
|
673
659
|
|
|
674
660
|
// Lookup each feature option setting and set the visibility accordingly.
|
|
675
661
|
for(const entry of this.#featureOptions.groups[checkbox.id]) {
|
|
@@ -692,7 +678,7 @@ export class webUiFeatureOptions {
|
|
|
692
678
|
trX.appendChild(tdLabel);
|
|
693
679
|
|
|
694
680
|
// Adjust the visibility of the feature option, if it's logically grouped.
|
|
695
|
-
if((option.group !== undefined) && !this.#featureOptions.test(category.name + (option.group.length ? ("." + option.group) : ""), currentDevice?.
|
|
681
|
+
if((option.group !== undefined) && !this.#featureOptions.test(category.name + (option.group.length ? ("." + option.group) : ""), currentDevice?.serialNumber)) {
|
|
696
682
|
|
|
697
683
|
trX.style.display = "none";
|
|
698
684
|
} else {
|
|
@@ -738,7 +724,7 @@ export class webUiFeatureOptions {
|
|
|
738
724
|
|
|
739
725
|
// Display our device details.
|
|
740
726
|
deviceFirmware.innerHTML = device.firmwareVersion;
|
|
741
|
-
deviceSerial.innerHTML = device.
|
|
727
|
+
deviceSerial.innerHTML = device.serialNumber;
|
|
742
728
|
}
|
|
743
729
|
|
|
744
730
|
// Default method for enumerating the device list in the sidebar.
|
|
@@ -785,12 +771,12 @@ export class webUiFeatureOptions {
|
|
|
785
771
|
|
|
786
772
|
const label = document.createElement("label");
|
|
787
773
|
|
|
788
|
-
label.name = device.
|
|
774
|
+
label.name = device.serialNumber;
|
|
789
775
|
label.appendChild(document.createTextNode(device.name ?? "Unknown"));
|
|
790
776
|
label.style.cursor = "pointer";
|
|
791
777
|
label.classList.add("mx-2", "my-0", "p-0", "w-100");
|
|
792
778
|
|
|
793
|
-
label.addEventListener("click", () => this.showDeviceOptions(device.
|
|
779
|
+
label.addEventListener("click", () => this.showDeviceOptions(device.serialNumber));
|
|
794
780
|
|
|
795
781
|
// Add the device label to our cell.
|
|
796
782
|
tdDevice.appendChild(label);
|
|
@@ -814,10 +800,14 @@ export class webUiFeatureOptions {
|
|
|
814
800
|
// Filter out only the components we're interested in.
|
|
815
801
|
devices = devices.map(device => ({
|
|
816
802
|
|
|
817
|
-
|
|
803
|
+
firmwareRevision: (device.services.find(service => service.constructorName ===
|
|
818
804
|
"AccessoryInformation")?.characteristics.find(characteristic => characteristic.constructorName === "FirmwareRevision")?.value ?? ""),
|
|
805
|
+
manufacturer: (device.services.find(service => service.constructorName ===
|
|
806
|
+
"AccessoryInformation")?.characteristics.find(characteristic => characteristic.constructorName === "Manufacturer")?.value ?? ""),
|
|
807
|
+
model: (device.services.find(service => service.constructorName ===
|
|
808
|
+
"AccessoryInformation")?.characteristics.find(characteristic => characteristic.constructorName === "Model")?.value ?? ""),
|
|
819
809
|
name: device.displayName,
|
|
820
|
-
|
|
810
|
+
serialNumber: (device.services.find(service => service.constructorName ===
|
|
821
811
|
"AccessoryInformation")?.characteristics.find(characteristic => characteristic.constructorName === "SerialNumber")?.value ?? "")
|
|
822
812
|
}));
|
|
823
813
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-plugin-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"displayName": "Homebridge Plugin Utilities",
|
|
5
5
|
"description": "Opinionated utilities to provide common capabilities and create rich configuration webUI experiences for Homebridge plugins.",
|
|
6
6
|
"author": {
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
},
|
|
40
40
|
"main": "dist/index.js",
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@stylistic/eslint-plugin": "2.
|
|
43
|
-
"@types/node": "22.
|
|
44
|
-
"eslint": "^9.
|
|
42
|
+
"@stylistic/eslint-plugin": "2.11.0",
|
|
43
|
+
"@types/node": "22.10.1",
|
|
44
|
+
"eslint": "^9.16.0",
|
|
45
45
|
"homebridge": "1.8.4",
|
|
46
46
|
"shx": "0.3.4",
|
|
47
|
-
"typescript": "5.
|
|
48
|
-
"typescript-eslint": "^8.
|
|
47
|
+
"typescript": "5.7.2",
|
|
48
|
+
"typescript-eslint": "^8.17.0"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"mqtt": "5.10.
|
|
51
|
+
"mqtt": "5.10.3"
|
|
52
52
|
}
|
|
53
53
|
}
|