homebridge-lib 6.3.7 → 6.3.8

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/lib/Colour.js DELETED
@@ -1,323 +0,0 @@
1
- // homebridge-lib/lib/Colour.js
2
- //
3
- // Library for Homebridge plugins.
4
- // Copyright © 2016-2023 Erik Baauw. All rights reserved.
5
-
6
- 'use strict'
7
-
8
- // Return point in color gamut closest to p.
9
- function closestInGamut (p, gamut) {
10
- // Return cross product of two points.
11
- function crossProduct (p1, p2) {
12
- return p1.x * p2.y - p1.y * p2.x
13
- }
14
-
15
- // Return distance between two points.
16
- function distance (p1, p2) {
17
- const dx = p1.x - p2.x
18
- const dy = p1.y - p2.y
19
- return Math.sqrt(dx * dx + dy * dy)
20
- }
21
-
22
- // Return point on line a,b closest to p.
23
- function closest (a, b, p) {
24
- const ap = { x: p.x - a.x, y: p.y - a.y }
25
- const ab = { x: b.x - a.x, y: b.y - a.y }
26
- let t = (ap.x * ab.x + ap.y * ab.y) / (ab.x * ab.x + ab.y * ab.y)
27
- t = t < 0.0 ? 0.0 : t > 1.0 ? 1.0 : t
28
- return { x: a.x + t * ab.x, y: a.y + t * ab.y }
29
- }
30
-
31
- const r = { x: gamut.r[0], y: gamut.r[1] }
32
- const g = { x: gamut.g[0], y: gamut.g[1] }
33
- const b = { x: gamut.b[0], y: gamut.b[1] }
34
- const v1 = { x: g.x - r.x, y: g.y - r.y }
35
- const v2 = { x: b.x - r.x, y: b.y - r.y }
36
- const v = crossProduct(v1, v2)
37
- const q = { x: p.x - r.x, y: p.y - r.y }
38
- const s = crossProduct(q, v2) / v
39
- const t = crossProduct(v1, q) / v
40
- if (s >= 0.0 && t >= 0.0 && s + t <= 1.0) {
41
- return p
42
- }
43
- const pRG = closest(r, g, p)
44
- const pGB = closest(g, b, p)
45
- const pBR = closest(b, r, p)
46
- const dRG = distance(p, pRG)
47
- const dGB = distance(p, pGB)
48
- const dBR = distance(p, pBR)
49
- let min = dRG
50
- p = pRG
51
- if (dGB < min) {
52
- min = dGB
53
- p = pGB
54
- }
55
- if (dBR < min) {
56
- p = pBR
57
- }
58
- return p
59
- }
60
-
61
- /** Colour conversions.
62
- * @class
63
- * @hideconstructor
64
- */
65
- class Colour {
66
- /** [sRGB](https://en.wikipedia.org/wiki/SRGB) colour in
67
- * [HSV](https://en.wikipedia.org/wiki/HSL_and_HSV).
68
- * @typedef
69
- * @property {integer} h - Hue, between 0˚ and 360˚.
70
- * @property {integer} s - Saturation, between 0% and 100%.
71
- * @property {integer} v - Value, between 0% and 100%.
72
- */
73
- static get HSV () {}
74
-
75
- /** [sRGB](https://en.wikipedia.org/wiki/SRGB) colour in
76
- * [RGB color model](https://en.wikipedia.org/wiki/RGB_color_model).
77
- * @typedef
78
- * @property {number} r - Red, between 0.0 and 1.0.
79
- * @property {number} g - Green, between 0.0 and 1.0.
80
- * @property {number} b - Blue, between 0.0 and 1.0.
81
- */
82
- static get RGB () {}
83
-
84
- /** Convert {@link Colour.HSV HSV} to {@link Colour.RGB RGB}.
85
- *
86
- * See [HSL and HSV](https://en.wikipedia.org/wiki/HSL_and_HSV).
87
- * @param {integer} h - Hue, between 0˚ and 360˚.
88
- * @param {integer} s - Saturation, between 0% and 100%.
89
- * @param {integer} [v=100] - Value, between 0% and 100%.
90
- * @return {RGB} rgb - The corresponding {@link Colour.RGB RGB} value.
91
- */
92
- static hsvToRgb (h, s, v = 100) {
93
- h /= 60.0
94
- s /= 100.0
95
- v /= 100.0
96
- const C = v * s
97
- const m = v - C
98
- let x = (h % 2) - 1.0
99
- if (x < 0) {
100
- x = -x
101
- }
102
- x = C * (1.0 - x)
103
- let r, g, b
104
- switch (Math.floor(h) % 6) {
105
- case 0: r = C + m; g = x + m; b = m; break
106
- case 1: r = x + m; g = C + m; b = m; break
107
- case 2: r = m; g = C + m; b = x + m; break
108
- case 3: r = m; g = x + m; b = C + m; break
109
- case 4: r = x + m; g = m; b = C + m; break
110
- case 5: r = C + m; g = m; b = x + m; break
111
- }
112
- return { r, g, b }
113
- }
114
-
115
- /** Convert {@link Colour.RGB RGB} to {@link Colour.HSV HSV}.
116
- *
117
- * See [HSL and HSV](https://en.wikipedia.org/wiki/HSL_and_HSV).
118
- * @param {number} r - Red, between 0.0 and 1.0.
119
- * @param {number} g - Green, between 0.0 and 1.0.
120
- * @param {number} b - Blue, between 0.0 and 1.0.
121
- * @return {HSV} hsv - The corresponding {@link Colour.HSV HSV} value.
122
- */
123
- static rgbToHsv (r, g, b) {
124
- const M = Math.max(r, g, b)
125
- const m = Math.min(r, g, b)
126
- const C = M - m
127
- const S = (M === 0.0) ? 0.0 : C / M
128
- let H
129
- switch (M) {
130
- case m:
131
- H = 0.0
132
- break
133
- case r:
134
- H = (g - b) / C
135
- if (H < 0) {
136
- H += 6.0
137
- }
138
- break
139
- case g:
140
- H = (b - r) / C
141
- H += 2.0
142
- break
143
- case b:
144
- H = (r - g) / C
145
- H += 4.0
146
- break
147
- }
148
- return {
149
- h: Math.round(H * 60.0),
150
- s: Math.round(S * 100.0),
151
- v: Math.round(M * 100.0)
152
- }
153
- }
154
-
155
- /** Colour [gamut](https://en.wikipedia.org/wiki/Gamut).
156
- * @typedef
157
- * @property {number[]} r - `xy` coordinates for red,
158
- * x, y between 0.0000 and 1.0000.
159
- * @property {number[]} g - `xy` coordinates for green,
160
- * x, y between 0.0000 and 1.0000..
161
- * @property {number[]} b - `xy` coordinates for blue,
162
- * x, y between 0.0000 and 1.0000.
163
- */
164
- static get Gamut () {}
165
-
166
- /** Default gamut.
167
- * @type {Gamut}
168
- * @readonly
169
- */
170
- static get defaultGamut () {
171
- // Safe default gamut taking into account:
172
- // - The maximum value for CurrentX and CurrentY, 65279 (0xfeff),
173
- // as defined by the ZCL spec;
174
- // - A potential division by zero error for CurrentY, when translating the
175
- // xy values back to hue/sat.
176
- return {
177
- r: [0.9961, 0.0001],
178
- g: [0, 0.9961],
179
- b: [0, 0.0001]
180
- }
181
- }
182
-
183
- /** Transform [sRGB](https://en.wikipedia.org/wiki/SRGB)
184
- * {@link Colour.HSV HSV} to
185
- * [CIE 1931](https://en.wikipedia.org/wiki/CIE_1931_color_space) `xy`.
186
- *
187
- * See [Hue developer portal](https://developers.meethue.com/develop/application-design-guidance/color-conversion-formulas-rgb-to-xy-and-back/).
188
- * @param {integer} h - Hue, between 0˚ and 360˚.
189
- * @param {integer} s - Saturation, between 0% and 100%.
190
- * @param {Gamut} [gamut=defaultGamut] - The gamut supported by the light.
191
- * @return {number[]} xy - The closest matching CIE 1931 colour,
192
- * x, y between 0.0000 and 1.0000.
193
- */
194
- static hsvToXy (h, s, gamut = Colour.defaultGamut) {
195
- // Gamma correction (inverse sRGB Companding).
196
- function invCompand (v) {
197
- return v > 0.04045 ? Math.pow((v + 0.055) / (1.0 + 0.055), 2.4) : v / 12.92
198
- }
199
-
200
- let { r, g, b } = Colour.hsvToRgb(h, s)
201
-
202
- // RGB to XYZ to xyY
203
- r = invCompand(r)
204
- g = invCompand(g)
205
- b = invCompand(b)
206
- const X = r * 0.664511 + g * 0.154324 + b * 0.162028
207
- const Y = r * 0.283881 + g * 0.668433 + b * 0.047685
208
- const Z = r * 0.000088 + g * 0.072310 + b * 0.986039
209
- const sum = X + Y + Z
210
- const p = sum === 0.0 ? { x: 0.0, y: 0.0 } : { x: X / sum, y: Y / sum }
211
- const q = closestInGamut(p, gamut)
212
- return [Math.round(q.x * 10000) / 10000, Math.round(q.y * 10000) / 10000]
213
- }
214
-
215
- /** Transform [CIE 1931](https://en.wikipedia.org/wiki/CIE_1931_color_space)
216
- * `xy` to [sRGB](https://en.wikipedia.org/wiki/SRGB)
217
- * {@link Colour.HSV HSV}.
218
- *
219
- * See [Hue developer portal](https://developers.meethue.com/develop/application-design-guidance/color-conversion-formulas-rgb-to-xy-and-back/).
220
- * @param {number[]} xy - The CIE 1931 xy colour,
221
- * x, y between 0.0000 and 1.0000.
222
- * @param {Gamut} [gamut=defaultGamut] - The gamut supported by the light.
223
- * @return {HSV} hsv - The closest matching sRGB colour.
224
- */
225
- static xyToHsv (xy, gamut = Colour.defaultGamut) {
226
- // Inverse Gamma correction (sRGB Companding).
227
- function compand (v) {
228
- return v <= 0.0031308
229
- ? 12.92 * v
230
- : (1.0 + 0.055) * Math.pow(v, (1.0 / 2.4)) - 0.055
231
- }
232
-
233
- // Correction for negative values is missing from Philips' documentation.
234
- function correctNegative () {
235
- const m = Math.min(r, g, b)
236
- if (m < 0.0) {
237
- r -= m
238
- g -= m
239
- b -= m
240
- }
241
- }
242
-
243
- function rescale () {
244
- const M = Math.max(r, g, b)
245
- if (M > 1.0) {
246
- r /= M
247
- g /= M
248
- b /= M
249
- }
250
- }
251
-
252
- // xyY to XYZ to RGB
253
- const p = closestInGamut({ x: xy[0], y: xy[1] }, gamut)
254
- const x = p.x
255
- const y = p.y === 0.0 ? 0.000001 : p.y
256
- const z = 1.0 - x - y
257
- const Y = 1.0
258
- const X = (Y / y) * x
259
- const Z = (Y / y) * z
260
- let r = X * 1.656492 + Y * -0.354851 + Z * -0.255038
261
- let g = X * -0.707196 + Y * 1.655397 + Z * 0.036152
262
- let b = X * 0.051713 + Y * -0.121364 + Z * 1.011530
263
- correctNegative()
264
- rescale()
265
- r = compand(r)
266
- g = compand(g)
267
- b = compand(b)
268
- rescale()
269
- return Colour.rgbToHsv(r, g, b)
270
- }
271
-
272
- /** Transform
273
- * [colour temperature](https://en.wikipedia.org/wiki/Color_temperature) to
274
- * [CIE 1931](https://en.wikipedia.org/wiki/CIE_1931_color_space) `xy`.
275
- *
276
- * Source: [deCONZ REST API plugin](https://github.com/dresden-elektronik/deconz-rest-plugin/blob/master/colorspace.cpp).
277
- * The results don't match exactly the `xy` values as returned by a Hue
278
- * LCT015 light, but seem to be close enough.
279
- * @param {integer} ct - The colour temperature
280
- * in [mired](https://en.wikipedia.org/wiki/Mired).
281
- * @return {number[]} xy - The closest matching CIE 1931 colour,
282
- * x, y between 0.0000 and 1.0000.
283
- */
284
- static ctToXy (ct) {
285
- const kelvin = 1000000 / ct
286
- let x, y
287
-
288
- if (kelvin < 4000) {
289
- x = 11790 +
290
- 57520658 / kelvin +
291
- -15358885888 / kelvin / kelvin +
292
- -17440695910400 / kelvin / kelvin / kelvin
293
- } else {
294
- x = 15754 +
295
- 14590587 / kelvin +
296
- 138086835814 / kelvin / kelvin +
297
- -198301902438400 / kelvin / kelvin / kelvin
298
- }
299
- if (kelvin < 2222) {
300
- y = -3312 +
301
- 35808 * x / 0x10000 +
302
- -22087 * x * x / 0x100000000 +
303
- -18126 * x * x * x / 0x1000000000000
304
- } else if (kelvin < 4000) {
305
- y = -2744 +
306
- 34265 * x / 0x10000 +
307
- -22514 * x * x / 0x100000000 +
308
- -15645 * x * x * x / 0x1000000000000
309
- } else {
310
- y = -6062 +
311
- 61458 * x / 0x10000 +
312
- -96229 * x * x / 0x100000000 +
313
- 50491 * x * x * x / 0x1000000000000
314
- }
315
- y *= 4
316
- x /= 0xFFFF
317
- y /= 0xFFFF
318
-
319
- return [Math.round(x * 10000) / 10000, Math.round(y * 10000) / 10000]
320
- }
321
- }
322
-
323
- module.exports = Colour
@@ -1,311 +0,0 @@
1
- // homebridge-lib/lib/CommandLineParser.js
2
- //
3
- // Library for Homebridge plugins.
4
- // Copyright © 2017-2023 Erik Baauw. All rights reserved.
5
-
6
- // TODO:
7
- // - Change parameters to (params, callback) with:
8
- // - params.shortKey
9
- // - params.longKey
10
- // - params.key
11
- // - params.optional
12
- // - params.minumum
13
- // - params.helpText
14
-
15
- 'use strict'
16
-
17
- const homebridgeLib = require('../index')
18
-
19
- const chalk = require('chalk')
20
-
21
- const packageJson = require('../package.json')
22
-
23
- // Force colors when output is re-directed.
24
- chalk.enabled = true
25
- chalk.level = 1
26
-
27
- /** Usage error.
28
- * @hideconstructor
29
- * @extends Error
30
- * @memberof CommandLineParser
31
- */
32
- class UsageError extends Error {}
33
-
34
- /** Parser and validator for command-line arguments.
35
- */
36
- class CommandLineParser {
37
- static get UsageError () { return UsageError }
38
-
39
- /** Create a new parser instance.
40
- * @params {string} pkgJson - The contents of `package.json` to retrieve
41
- * the version and homepage for the command-line tool.
42
- */
43
- constructor (pkgJson = packageJson) {
44
- this._callbacks = {
45
- flags: {},
46
- options: {},
47
- parameters: [],
48
- remaining: null
49
- }
50
- this._packageJson = pkgJson
51
- }
52
-
53
- _toShort (value) {
54
- if (value == null) {
55
- return null
56
- }
57
- if (typeof value !== 'string' || value.length !== 1) {
58
- throw new TypeError(`${value}: invalid short key`)
59
- }
60
- if (this._callbacks.flags[value] != null) {
61
- throw new SyntaxError(`${value}: duplicate short key`)
62
- }
63
- return value
64
- }
65
-
66
- _toLong (value) {
67
- if (value == null) {
68
- return null
69
- }
70
- if (typeof value !== 'string' || value.length === 1) {
71
- throw new TypeError(`${value}: invalid long key`)
72
- }
73
- if (this._callbacks.options[value] != null) {
74
- throw new SyntaxError(`${value}: duplicate long key`)
75
- }
76
- return value
77
- }
78
-
79
- /** Add a flag to print help text and exit.
80
- *
81
- * See {@link CommandLineParser#flag flag()}.
82
- *
83
- * For now, the help text needs to be specified explicitly.
84
- * <br>TODO: Generate helpText automatically from
85
- * {@link CommandLineParser#flag flag()},
86
- * {@link CommandLineParser#option option()},
87
- * {@link CommandLineParser#parameter parameter()}, and
88
- * {@link CommandLineParser#remaining remaining()}
89
- * @param {string} shortKey - The short key (e.g. `h` for `-h`).
90
- * @param {string} longKey - The long key (e.g. `help` for `--help`).
91
- * @param {string} helpText - The help text.
92
- * @return {CommandLineParser} this - For chaining.
93
- */
94
- help (shortKey, longKey, helpText) {
95
- helpText = homebridgeLib.OptionParser.toString('helpText', helpText, true)
96
- this.flag(shortKey, longKey, () => {
97
- console.log(helpText)
98
- console.log(`
99
- See ${this._packageJson.homepage.split('#')[0]} for more info.
100
- (${this._packageJson.name} v${this._packageJson.version}, node ${process.version})`
101
- )
102
- process.exit(0)
103
- })
104
- return this
105
- }
106
-
107
- /** Add a flag to print the version and exit.
108
- *
109
- * See {@link CommandLineParser#flag flag()}.
110
- *
111
- * @param {string} shortKey - The short key (e.g. `V` for `-V`).
112
- * @param {string} longKey - The long key (e.g. `version` for `--version`).
113
- * @return {CommandLineParser} this - For chaining.
114
- */
115
- version (shortKey, longKey) {
116
- this.flag(shortKey, longKey, () => {
117
- console.log(this._packageJson.version)
118
- process.exit(0)
119
- })
120
- return this
121
- }
122
-
123
- /** Add a callback for a flag.
124
- *
125
- * A flag is an optional command-line parameter, identified by a short key
126
- * (a single character, like `-v`), or by a long key (a word, like
127
- * `--verbose`).
128
- *
129
- * @param {string} shortKey - The short key (e.g. `v` for `-v`).
130
- * @param {string} longKey - The long key (e.g. `verbose` for `--verbose`).
131
- * @param {function} callback - The callback function.<br>
132
- * The function will be called when the flag is present, with the
133
- * following parameters:
134
- *
135
- * Name | Type | Attributes | Description
136
- * ---- | ---- | ---------- | -----------
137
- * `key` | string | | The key.
138
- * @return {CommandLineParser} this - For chaining.
139
- */
140
- flag (shortKey, longKey, callback) {
141
- shortKey = this._toShort(shortKey)
142
- longKey = this._toLong(longKey)
143
- callback = homebridgeLib.OptionParser.toFunction('callback', callback)
144
- if (shortKey != null) {
145
- this._callbacks.flags[shortKey] = callback
146
- }
147
- if (longKey != null) {
148
- this._callbacks.flags[longKey] = callback
149
- }
150
- return this
151
- }
152
-
153
- /** Add a callback for an option.
154
- *
155
- * An option is an optional command-line paramater that takes a value.
156
- * The option is identified by a short key (a single character, like `-t`),
157
- * or by a long key (a word, like `--timeout`).
158
- * The value can specified in the next or in the same command-line parameter:
159
- * `-t5` `--timeout=5`, `-t 5`, or `--timeout 5`
160
- *
161
- * @param {string} shortKey - The short key (e.g. `t` for `-t`).
162
- * @param {string} longKey - The long key (e.g. `timeout` for `--timeout`).
163
- * @param {function} callback - The callback function.<br>
164
- * The function will be called when the option is present, with the
165
- * following parameters:
166
- *
167
- * Name | Type | Attributes | Description
168
- * ---- | ---- | ---------- | -----------
169
- * `value` | string | | The value.
170
- * `key` | string | | The (short or long) key.
171
- * @return {CommandLineParser} this - For chaining.
172
- */
173
- option (shortKey, longKey, callback) {
174
- shortKey = this._toShort(shortKey)
175
- longKey = this._toLong(longKey)
176
- callback = homebridgeLib.OptionParser.toFunction('callback', callback)
177
- if (shortKey != null) {
178
- this._callbacks.options[shortKey] = callback
179
- }
180
- if (longKey != null) {
181
- this._callbacks.options[longKey] = callback
182
- }
183
- return this
184
- }
185
-
186
- /** Add a callback for a positional parameter.
187
- *
188
- * A positional paramater is a mandatory command-line parameter.
189
- * It is specified as a single value, e.g. `get`
190
- *
191
- * @param {string} key - The parameter key (e.g. `command`).
192
- * @param {function} callback - The callback function.<br>
193
- * The function will be called with the following parameters:
194
- *
195
- * Name | Type | Attributes | Description
196
- * ---- | ---- | ---------- | -----------
197
- * `value` | string | | The parameter value.
198
- * `key` | string | | The parameter key.
199
- * @return {CommandLineParser} this - For chaining.
200
- */
201
- parameter (key, callback, optional = false) {
202
- key = homebridgeLib.OptionParser.toString('key', key, true)
203
- callback = homebridgeLib.OptionParser.toFunction('callback', callback)
204
- this._callbacks.parameters.push({ key, callback, optional })
205
- return this
206
- }
207
-
208
- // * @param {string} key - The name of the remaining parameters (e.g.
209
- // * `file` for `[`_file_` ...]`).
210
- /** Add a callback for the remaining parameters.
211
- *
212
- * The remaining parameters are any additional commmand-line parameters,
213
- * after the positional paramers, typically indicated as `[file ...]`.
214
- * @param {function} callback - The callback function.<br>
215
- * This function will be called with the following paramters:
216
- *
217
- * Name | Type | Attributes | Description
218
- * ---- | ---- | ---------- | -----------
219
- * `values` | string[] | | A list of values of the remaining parameters.
220
- * @return {CommandLineParser} this - For chaining.
221
- */
222
- remaining (/* key, */ callback) {
223
- callback = homebridgeLib.OptionParser.toFunction('callback', callback)
224
- this._callbacks.remaining = callback
225
- return this
226
- }
227
-
228
- /** Parse the command-line parameters.
229
- *
230
- * @throws {UsageError} In case of invalid command-line paramters.
231
- */
232
- parse (wordList = process.argv.slice(2)) {
233
- // process.argv[0]: node executable, process.argv[1]: javascript file
234
- wordList = homebridgeLib.OptionParser.toArray('wordList', wordList)
235
- let wordIndex = 0
236
- let charIndex
237
-
238
- function handleWord (word, long) {
239
- const key = long ? word.split('=')[0] : word[0]
240
- const option = (long ? '--' : '-') + key
241
- let value = long ? word.split('=')[1] : null
242
- let callback = this._callbacks.flags[key]
243
- if (callback) {
244
- if (value != null) {
245
- throw new UsageError(`${option}: option doesn't allow an argument`)
246
- }
247
- callback(option)
248
- return long
249
- }
250
- callback = this._callbacks.options[key]
251
- if (callback) {
252
- value = long ? word.split('=')[1] : word.substring(1)
253
- if (value) {
254
- charIndex = word.length
255
- } else {
256
- if (wordIndex >= wordList.length) {
257
- throw new UsageError(`${option}: option requires an argument`)
258
- }
259
- value = wordList[wordIndex++]
260
- }
261
- callback(value, option)
262
- return long
263
- }
264
- throw new UsageError(`${option}: unknown option`)
265
- }
266
-
267
- // Parse flags and options.
268
- while (wordIndex < wordList.length) {
269
- const word = wordList[wordIndex++]
270
- if (word[0] !== '-' || word === '-') {
271
- wordIndex -= 1
272
- break
273
- }
274
- if (word === '--') {
275
- break
276
- }
277
- if (word[1] === '-') {
278
- handleWord.call(this, word.substring(2), true)
279
- continue
280
- }
281
- charIndex = 1
282
- while (charIndex < word.length) {
283
- if (handleWord.call(this, word.substring(charIndex++), false)) {
284
- break
285
- }
286
- }
287
- }
288
- // Parse parameters.
289
- for (const p of this._callbacks.parameters) {
290
- if (wordIndex >= wordList.length) {
291
- if (!p.optional) {
292
- throw new UsageError(`parameter ${p.key} missing`)
293
- }
294
- break
295
- }
296
- const parameter = wordList[wordIndex++]
297
- p.callback(parameter)
298
- }
299
- const remaining = wordList.slice(wordIndex, wordList.length)
300
- const callback = this._callbacks.remaining
301
- if (callback) {
302
- callback(remaining)
303
- return
304
- }
305
- if (remaining.length > 0) {
306
- throw new UsageError('too many parameters')
307
- }
308
- }
309
- }
310
-
311
- module.exports = CommandLineParser