homebridge-plugin-utils 1.0.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/LICENSE.md ADDED
@@ -0,0 +1,16 @@
1
+ Internet Systems Consortium License
2
+ ===================================
3
+
4
+ Copyright (c) `2017-2024`, `HJD https://github.com/hjdhjd`
5
+
6
+ Permission to use, copy, modify, and/or distribute this software for any purpose
7
+ with or without fee is hereby granted, provided that the above copyright notice
8
+ and this permission notice appear in all copies.
9
+
10
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
11
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
12
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
13
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
16
+ THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,34 @@
1
+ <SPAN ALIGN="CENTER" STYLE="text-align:center">
2
+ <DIV ALIGN="CENTER" STYLE="text-align:center">
3
+
4
+ # Homebridge Plugin Utilities
5
+ [![Downloads](https://img.shields.io/npm/dt/homebridge-plugin-utils?color=%23491F59&logo=icloud&logoColor=%23FFFFFF&style=for-the-badge)](https://www.npmjs.com/package/homebridge-plugin-utils)
6
+ [![Version](https://img.shields.io/npm/v/homebridge-plugin-utils?color=%23491F59&label=Latest%20Version&logo=homebridge&logoColor=%23FFFFFF&style=for-the-badge)](https://www.npmjs.com/package/homebridge-plugin-utils)
7
+ [![Homebridge Discord](https://img.shields.io/discord/432663330281226270?color=%23491F59&label=Discord&logo=discord&logoColor=%23FFFFFF&style=for-the-badge)](https://discord.gg/QXqfHEW)
8
+ </DIV>
9
+ </SPAN>
10
+
11
+ `homebridge-plugin-utils` is a utility library for [Homebridge](https://homebridge.io) [plugins](https://developers.homebridge.io) that aims to provide a set of common core capabilities that can accelerate and streamline plugin development. It's opinionated and largely derived from my other plugins and my desire to increase code reuse and make it easier to provide rich capabilities across all my plugins so that each of my plugins can focus on providing their unique capabilities rather than copying over the same capabilities (feature options, MQTT support, and a rich webUI interface to name a few) time after time.
12
+
13
+ The design decisions are driven by my own needs as I continue to create, evolve, and maintain my plugins but I also wanted to provide these as a resource to others, should it be of interest.
14
+
15
+ ### Features
16
+
17
+ - **Feature options*.* Feature options are a hierarchical configuration system and matching webUI that allows users to set global defaults and override them at a granular level, enabling easier mass-customization of capabilities. For plugins that can potentially enumerate dozens of devices, this comes in quite handy so you don't need to configure each and every device, and instead you can focus on the exceptions.
18
+
19
+ - **Configuration webUI.** This a rich, custom webUI for enumerating all the devices a plugin knows about, and configuring feature options.
20
+
21
+ - **MQTT client.** Building in MQTT client capabilities is made easier through a set of utilities that allow you to easily publish and subscribe to events.
22
+
23
+ - **And more...**
24
+
25
+ ## Documentation
26
+ * Coming in the future.
27
+
28
+ ## Plugin Development Dashboard
29
+ This is mostly of interest to the true developer nerds amongst us.
30
+
31
+ [![License](https://img.shields.io/npm/l/homebridge-plugin-utils?color=%23491F59&logo=open%20source%20initiative&logoColor=%23FFFFFF&style=for-the-badge)](https://github.com/hjdhjd/homebridge-plugin-utils/blob/main/LICENSE.md)
32
+ [![Build Status](https://img.shields.io/github/actions/workflow/status/hjdhjd/homebridge-plugin-utils/ci.yml?branch=main&color=%23491F59&logo=github-actions&logoColor=%23FFFFFF&style=for-the-badge)](https://github.com/hjdhjd/homebridge-plugin-utils/actions?query=workflow%3A%22Continuous+Integration%22)
33
+ [![Dependencies](https://img.shields.io/librariesio/release/npm/homebridge-plugin-utils?color=%23491F59&logo=dependabot&style=for-the-badge)](https://libraries.io/npm/homebridge-plugin-utils)
34
+ [![GitHub commits since latest release (by SemVer)](https://img.shields.io/github/commits-since/hjdhjd/homebridge-plugin-utils/latest?color=%23491F59&logo=github&sort=semver&style=for-the-badge)](https://github.com/hjdhjd/homebridge-plugin-utils/commits/main)
@@ -0,0 +1,82 @@
1
+ /* Copyright(C) 2017-2024, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * eslint-rules.mjs: Opinionated default linting rules for Homebridge plugins.
4
+ */
5
+ import stylistic from "@stylistic/eslint-plugin";
6
+ import tsEslint from "@typescript-eslint/eslint-plugin";
7
+
8
+ // ESlint plugins to use.
9
+ const eslintPlugins = {
10
+
11
+ "@stylistic": stylistic,
12
+ "@typescript-eslint": tsEslint
13
+ };
14
+
15
+ // TypeScript-specific rules.
16
+ const eslintTsRules = {
17
+
18
+ ...tsEslint.configs.strictTypeChecked,
19
+ ...tsEslint.configs.stylisticTypeChecked,
20
+ "@typescript-eslint/explicit-function-return-type": "warn",
21
+ "@typescript-eslint/explicit-module-boundary-types": "warn",
22
+ "@typescript-eslint/no-explicit-any": "warn",
23
+ "@typescript-eslint/no-floating-promises": ["warn", { "ignoreIIFE": true }],
24
+ "@typescript-eslint/no-non-null-assertion": "warn",
25
+ "no-dupe-class-members": "off",
26
+ "no-undef": "off",
27
+ "no-unused-vars": "off"
28
+ };
29
+
30
+ // JavaScript-specific rules.
31
+ const eslintJsRules = {
32
+
33
+ ...tsEslint.configs.disableTypeChecked,
34
+ "@typescript-eslint/no-floating-promises": "off"
35
+ };
36
+
37
+ // Rules that exist across both JavaScript and TypeScript files.
38
+ const eslintCommonRules = {
39
+
40
+ ...tsEslint.configs.eslintRecommended,
41
+ "@stylistic/brace-style": "error",
42
+ "@stylistic/comma-dangle": "error",
43
+ "@stylistic/generator-star-spacing": "error",
44
+ "@stylistic/implicit-arrow-linebreak": "error",
45
+ "@stylistic/indent": ["warn", 2, { "SwitchCase": 1 }],
46
+ "@stylistic/keyword-spacing": ["error",
47
+ { "overrides": { "catch": { "after": false }, "for": { "after": false }, "if": { "after": false }, "switch": { "after": false}, "while": { "after": false } } }],
48
+ "@stylistic/linebreak-style": ["warn", "unix"],
49
+ "@stylistic/lines-between-class-members": ["warn", "always", { "exceptAfterSingleLine": true }],
50
+ "@stylistic/max-len": ["warn", 170],
51
+ "@stylistic/no-tabs": "error",
52
+ "@stylistic/no-trailing-spaces": "error",
53
+ "@stylistic/semi": ["warn", "always"],
54
+ "@stylistic/space-before-function-paren": ["error", { "anonymous": "never", "asyncArrow": "always", "named": "never" }],
55
+ "@stylistic/space-in-parens": "error",
56
+ "@stylistic/space-infix-ops": "error",
57
+ "@stylistic/space-unary-ops": "error",
58
+ "@typescript-eslint/no-this-alias": "warn",
59
+ "camelcase": "warn",
60
+ "curly": ["warn", "all"],
61
+ "dot-notation": "warn",
62
+ "eqeqeq": "warn",
63
+ "no-await-in-loop": "warn",
64
+ "no-console": "warn",
65
+ "prefer-arrow-callback": "warn",
66
+ "quotes": ["warn", "double", { "avoidEscape": true }],
67
+ "sort-imports": "warn",
68
+ "sort-keys": "warn",
69
+ "sort-vars": "warn"
70
+ };
71
+
72
+ export default {
73
+
74
+ plugins: eslintPlugins,
75
+ rules: {
76
+
77
+ common: eslintCommonRules,
78
+ js: eslintJsRules,
79
+ ts: eslintTsRules
80
+ }
81
+ };
82
+
@@ -0,0 +1,45 @@
1
+ /* Copyright(C) 2017-2024, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * tsconfig.json: Default TypeScript transpiler options for ESM Homebridge plugins.
4
+ */
5
+ {
6
+ "compilerOptions": {
7
+
8
+ "allowSyntheticDefaultImports": true,
9
+ "declaration": true,
10
+ "esModuleInterop": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+
13
+ "lib": [
14
+
15
+ "DOM",
16
+ "ES2022"
17
+ ],
18
+
19
+ "module": "ES2022",
20
+ "moduleResolution":"node",
21
+ "sourceMap": true,
22
+ "strict": true,
23
+ "target": "ES2022"
24
+ }
25
+ }
26
+ /* When creating your own tsconfig.json, use the following as a starting point to inherit these defaults:
27
+ *
28
+ * {
29
+ * "compilerOptions": {
30
+ *
31
+ * "outDir": "dist",
32
+ * "rootDir": "src"
33
+ * },
34
+ *
35
+ * "extends": "./build/tsconfig.json",
36
+ *
37
+ * "include": [
38
+ *
39
+ * "build",
40
+ * "eslint.config.mjs",
41
+ * "src",
42
+ * "ui"
43
+ * ]
44
+ * }
45
+ */
@@ -0,0 +1,177 @@
1
+ export interface FeatureOptionEntry {
2
+ default: boolean;
3
+ defaultValue?: number;
4
+ description: string;
5
+ group?: string;
6
+ name: string;
7
+ }
8
+ export interface FeatureCategoryEntry {
9
+ description: string;
10
+ name: string;
11
+ validFor: string[];
12
+ }
13
+ type OptionScope = "controller" | "device" | "global" | "none";
14
+ export declare class FeatureOptions {
15
+ private _categories;
16
+ private _configuredOptions;
17
+ private _groups;
18
+ private _options;
19
+ defaultReturnValue: boolean;
20
+ private defaults;
21
+ private valueOptions;
22
+ constructor(categories: FeatureCategoryEntry[], options: {
23
+ [index: string]: FeatureOptionEntry[];
24
+ }, configuredOptions: string[]);
25
+ color(option: string, device?: string): string;
26
+ /**
27
+ * Return the default value for an option.
28
+ *
29
+ * @param option - Feature option to check.
30
+ *
31
+ * @returns Returns true or false, depending on the option default.
32
+ */
33
+ defaultValue(option: string): boolean;
34
+ /**
35
+ * Return whether the option explicitly exists in the list of configured options.
36
+ *
37
+ * @param option - Feature option to check.
38
+ * @param id - Optional device or controller scope identifier to check.
39
+ *
40
+ * @returns Returns true if the option has been explicitly configured, false otherwise.
41
+ */
42
+ exists(option: string, id?: string): boolean;
43
+ /**
44
+ * Return a fully formed feature option string.
45
+ *
46
+ * @param category - Feature option category entry or category name string.
47
+ * @param option - Feature option entry of option name string.
48
+ *
49
+ * @returns Returns a fully formed feature option in the form of `category.option`.
50
+ */
51
+ expandOption(category: FeatureCategoryEntry | string, option: FeatureOptionEntry | string): string;
52
+ /**
53
+ * Parse a floating point feature option value.
54
+ *
55
+ * @param value - Value to parse.
56
+ *
57
+ * @returns Returns a floating point number from a string, or `undefined` if it couldn't be parsed.
58
+ */
59
+ getFloat(value: string | undefined): number | undefined;
60
+ /**
61
+ * Parse an integer feature option value.
62
+ *
63
+ * @param value - Value to parse.
64
+ *
65
+ * @returns Returns an integer from a string, or `undefined` if it couldn't be parsed.
66
+ */
67
+ getInteger(value: string | undefined): number | undefined;
68
+ /**
69
+ * Return whether an option has been set in either the device or controller scope context.
70
+ *
71
+ * @param option - Feature option to check.
72
+ *
73
+ * @returns Returns true if the option is set at the device or controller level and false otherwise.
74
+ */
75
+ isScopeDevice(option: string, device: string): boolean;
76
+ /**
77
+ * Return whether an option has been set in the global scope context.
78
+ *
79
+ * @param option - Feature option to check.
80
+ *
81
+ * @returns Returns true if the option is set globally and false otherwise.
82
+ */
83
+ isScopeGlobal(option: string): boolean;
84
+ /**
85
+ * Return whether an option is value-centric or not.
86
+ *
87
+ * @param option - Feature option entry or string to check.
88
+ *
89
+ * @returns Returns true if it is a value-centric option and false otherwise.
90
+ */
91
+ isValue(option: string): boolean;
92
+ /**
93
+ * Return the scope hierarchy location of an option.
94
+ *
95
+ * @param option - Feature option to check.
96
+ * @param device - Optional device scope identifier.
97
+ * @param controller - Optional controller scope identifier.
98
+ *
99
+ * @returns Returns an object containing the location in the scope hierarchy of an `option` as well as the current value associated with the option.
100
+ */
101
+ scope(option: string, device?: string, controller?: string): OptionScope;
102
+ /**
103
+ * Return the current state of a feature option, traversing the scope hierarchy.
104
+ *
105
+ * @param option - Feature option to check.
106
+ * @param device - Optional device scope identifier.
107
+ * @param controller - Optional controller scope identifier.
108
+ *
109
+ * @returns Returns true if the option is enabled, and false otherwise.
110
+ */
111
+ test(option: string, device?: string, controller?: string): boolean;
112
+ /**
113
+ * Return the value associated with a value-centric feature option, traversing the scope hierarchy.
114
+ *
115
+ * @param option - Feature option to check.
116
+ * @param device - Optional device scope identifier.
117
+ * @param controller - Optional controller scope identifier.
118
+ *
119
+ * @returns Returns the current value associated with `option` or `undefined` if none.
120
+ */
121
+ value(option: string, device?: string, controller?: string): string | undefined;
122
+ /**
123
+ * Return the list of available feature option categories.
124
+ *
125
+ * @returns Returns the current list of available feature option categories.
126
+ */
127
+ get categories(): FeatureCategoryEntry[];
128
+ /**
129
+ * Set the list of available feature option categories.
130
+ *
131
+ * @param options - Array of available feature options.
132
+ */
133
+ set categories(category: FeatureCategoryEntry[]);
134
+ /**
135
+ * Return the list of currently configured feature options.
136
+ *
137
+ * @returns Returns the currently configured list of feature options.
138
+ */
139
+ get configuredOptions(): string[];
140
+ /**
141
+ * Set the list of currently configured feature options.
142
+ *
143
+ * @param options - Array of configured feature options.
144
+ */
145
+ set configuredOptions(options: string[]);
146
+ /**
147
+ * Return the list of available feature option groups.
148
+ *
149
+ * @returns Returns the current list of available feature option groups.
150
+ */
151
+ get groups(): {
152
+ [index: string]: string[];
153
+ };
154
+ /**
155
+ * Return the list of available feature options.
156
+ *
157
+ * @returns Returns the current list of available feature options.
158
+ */
159
+ get options(): {
160
+ [index: string]: FeatureOptionEntry[];
161
+ };
162
+ /**
163
+ * Set the list of available feature options.
164
+ *
165
+ * @param options - Array of available feature options.
166
+ */
167
+ set options(options: {
168
+ [index: string]: FeatureOptionEntry[];
169
+ });
170
+ private generateDefaults;
171
+ private getOptionInfo;
172
+ private isOptionEnabled;
173
+ private optionRegex;
174
+ private parseOptionNumeric;
175
+ private valueRegex;
176
+ }
177
+ export {};