homebridge-plejd 1.6.1 → 1.7.0-beta

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.
@@ -0,0 +1,125 @@
1
+ import { Logger } from "homebridge/lib/logger";
2
+ import { PlejdCommand, PlejdService } from "../src/plejdService";
3
+ import { PLEJD_WRITE_TIMEOUT } from "../src/constants";
4
+
5
+ describe("PlejdService updateState", () => {
6
+ let service: PlejdService;
7
+
8
+ beforeEach(() => {
9
+ service = new PlejdService(
10
+ {
11
+ devices: [],
12
+ cryptoKey: Buffer.from("FooBar", "utf8"),
13
+ },
14
+ Logger.withPrefix("PlejdServiceTests"),
15
+ () => {},
16
+ );
17
+ });
18
+
19
+ describe("Turn off scenarios", () => {
20
+ it.each([
21
+ ["isOn=false, brightness=undefined", false, undefined],
22
+ ["isOn=false, brightness=0", false, 0],
23
+ ["isOn=true, brightness=0", true, 0],
24
+ ["isOn=true, brightness=undefined", true, undefined],
25
+ ])("%s should queue a turn off command", async (_, isOn, brightness) => {
26
+ const deviceId = 42;
27
+ await service.updateState(deviceId, isOn, {
28
+ targetBrightness: brightness,
29
+ });
30
+
31
+ const queue = service.readQueue();
32
+ expect(queue.length).toBe(1);
33
+
34
+ const command = queue[0].toString("hex");
35
+ const expected =
36
+ deviceId.toString(16).padStart(2, "0") +
37
+ PlejdCommand.RequestNoResponse +
38
+ PlejdCommand.OnOffState +
39
+ "00";
40
+
41
+ expect(command).toBe(expected);
42
+ });
43
+ });
44
+
45
+ describe("Brightness transition scenarios", () => {
46
+ it("should return if no brightness change is made", async () => {
47
+ const deviceId = 42;
48
+ const brightness = 50; // 50% brightness
49
+ const transitionMS = 500;
50
+ await service.updateState(deviceId, true, {
51
+ currentBrightness: brightness,
52
+ targetBrightness: brightness,
53
+ transitionMS: transitionMS,
54
+ });
55
+ const queue = service.readQueue();
56
+ expect(queue.length).toBe(0);
57
+ });
58
+
59
+ it("should queue multiple brightness commands for a transition", async () => {
60
+ const deviceId = 42;
61
+ const brightness = 50; // 50% brightness
62
+ const transitionMS = 500;
63
+
64
+ await service.updateState(deviceId, true, {
65
+ targetBrightness: brightness,
66
+ transitionMS: transitionMS,
67
+ });
68
+
69
+ const queue = service.readQueue();
70
+
71
+ // Calculate expected number of steps
72
+ const expectedSteps = Math.round(transitionMS / PLEJD_WRITE_TIMEOUT);
73
+ expect(queue.length).toBe(expectedSteps);
74
+
75
+ // Check that all commands are brightness commands
76
+ queue.forEach((item) => {
77
+ const command = item.toString("hex");
78
+ expect(command.substring(2, 6)).toBe(PlejdCommand.RequestNoResponse);
79
+ expect(command.substring(6, 10)).toBe(PlejdCommand.Brightness);
80
+ expect(command.substring(10, 14)).toBe("0100"); // isOn = true
81
+ });
82
+
83
+ // Check that the brightness is distributed correctly
84
+ const newBrightness = Math.round(2.55 * brightness);
85
+ const brightnessStep = Math.round(newBrightness / expectedSteps);
86
+
87
+ queue.forEach((item) => {
88
+ const command = item.toString("hex");
89
+ const brightnessHex = command.substring(14, 18);
90
+ const brightnessValue = parseInt(brightnessHex, 16);
91
+ expect(brightnessValue).toBe(brightnessStep);
92
+ });
93
+ });
94
+
95
+ it("should handle a 1-second transition with default parameters", async () => {
96
+ const deviceId = 42;
97
+ const brightness = 100;
98
+
99
+ await service.updateState(deviceId, true, {
100
+ targetBrightness: brightness,
101
+ transitionMS: 1000,
102
+ });
103
+ const queue = service.readQueue();
104
+
105
+ // With default 1000ms and assuming PLEJD_WRITE_TIMEOUT = 100
106
+ const expectedSteps = 10;
107
+ expect(queue.length).toBe(expectedSteps);
108
+ });
109
+
110
+ it("should handle a 0-ms transition with default parameters", async () => {
111
+ const deviceId = 42;
112
+ const brightness = 100;
113
+
114
+ await service.updateState(deviceId, true, {
115
+ targetBrightness: brightness,
116
+ });
117
+
118
+ const queue = service.readQueue();
119
+
120
+ // With default 1000ms and assuming PLEJD_WRITE_TIMEOUT = 100
121
+ const expectedSteps = 1;
122
+ expect(queue.length).toBe(expectedSteps);
123
+ });
124
+ });
125
+ });
@@ -0,0 +1,286 @@
1
+ import {
2
+ isDimmable,
3
+ isAddon,
4
+ delay,
5
+ race,
6
+ plejdChallageResp as plejdChalResp,
7
+ plejdEncodeDecode,
8
+ } from "../src/utils";
9
+
10
+ describe("plejdChalResp", () => {
11
+ it("should produce consistent output for same key and challenge", () => {
12
+ const key = Buffer.from("0123456789abcdef0123456789abcdef", "hex");
13
+ const challenge = Buffer.from("fedcba9876543210fedcba9876543210", "hex");
14
+
15
+ const result1 = plejdChalResp(key, challenge);
16
+ const result2 = plejdChalResp(key, challenge);
17
+
18
+ expect(result1).toEqual(result2);
19
+ });
20
+
21
+ it("should produce different outputs for different challenges", () => {
22
+ const key = Buffer.from("0123456789abcdef0123456789abcdef", "hex");
23
+ const challenge1 = Buffer.from("1111111111111111111111111111111", "hex");
24
+ const challenge2 = Buffer.from("2222222222222222222222222222222", "hex");
25
+
26
+ const result1 = plejdChalResp(key, challenge1);
27
+ const result2 = plejdChalResp(key, challenge2);
28
+
29
+ expect(result1).not.toEqual(result2);
30
+ });
31
+
32
+ it("should produce different outputs for different keys", () => {
33
+ const key1 = Buffer.from("1111111111111111111111111111111", "hex");
34
+ const key2 = Buffer.from("2222222222222222222222222222222", "hex");
35
+ const challenge = Buffer.from("fedcba9876543210fedcba9876543210", "hex");
36
+
37
+ const result1 = plejdChalResp(key1, challenge);
38
+ const result2 = plejdChalResp(key2, challenge);
39
+
40
+ expect(result1).not.toEqual(result2);
41
+ });
42
+
43
+ it("should return 16-byte buffer", () => {
44
+ const key = Buffer.from("0123456789abcdef0123456789abcdef", "hex");
45
+ const challenge = Buffer.from("fedcba9876543210fedcba9876543210", "hex");
46
+
47
+ const result = plejdChalResp(key, challenge);
48
+
49
+ expect(result).toBeInstanceOf(Buffer);
50
+ expect(result.length).toBe(16);
51
+ });
52
+
53
+ it("should handle known test vector", () => {
54
+ // Test with zero key and challenge for predictable result
55
+ const key = Buffer.alloc(16, 0);
56
+ const challenge = Buffer.alloc(16, 0);
57
+
58
+ const result = plejdChalResp(key, challenge);
59
+
60
+ expect(result.length).toBe(16);
61
+ expect(result).toBeInstanceOf(Buffer);
62
+ });
63
+ });
64
+
65
+ describe("plejdEncodeDecode", () => {
66
+ const testKey = Buffer.from("0123456789abcdef0123456789abcdef", "hex");
67
+ const testAddress = Buffer.from("001122334455", "hex");
68
+
69
+ it("should produce consistent output for same inputs", () => {
70
+ const data = Buffer.from("hello world", "ascii");
71
+
72
+ const result1 = plejdEncodeDecode(testKey, testAddress, data);
73
+ const result2 = plejdEncodeDecode(testKey, testAddress, data);
74
+
75
+ expect(result1).toEqual(result2);
76
+ });
77
+
78
+ it("should be reversible (encode/decode)", () => {
79
+ const originalData = Buffer.from("test message", "ascii");
80
+
81
+ const encoded = plejdEncodeDecode(testKey, testAddress, originalData);
82
+ const decoded = plejdEncodeDecode(testKey, testAddress, encoded);
83
+
84
+ expect(decoded).toEqual(originalData);
85
+ });
86
+
87
+ it("should handle empty data", () => {
88
+ const emptyData = Buffer.alloc(0);
89
+
90
+ const result = plejdEncodeDecode(testKey, testAddress, emptyData);
91
+
92
+ expect(result).toEqual(Buffer.alloc(0));
93
+ });
94
+
95
+ it("should handle single byte", () => {
96
+ const data = Buffer.from([65]); // 'A'
97
+
98
+ const result = plejdEncodeDecode(testKey, testAddress, data);
99
+
100
+ expect(result.length).toBe(1);
101
+ });
102
+
103
+ it("should produce different output for different keys", () => {
104
+ const data = Buffer.from("test", "ascii");
105
+ const key2 = Buffer.from("fedcba9876543210fedcba9876543210", "hex");
106
+
107
+ const result1 = plejdEncodeDecode(testKey, testAddress, data);
108
+ const result2 = plejdEncodeDecode(key2, testAddress, data);
109
+
110
+ expect(result1).not.toEqual(result2);
111
+ });
112
+
113
+ it("should produce different output for different addresses", () => {
114
+ const data = Buffer.from("test", "ascii");
115
+ const address2 = Buffer.from("aabbccddeeff", "hex");
116
+
117
+ const result1 = plejdEncodeDecode(testKey, testAddress, data);
118
+ const result2 = plejdEncodeDecode(testKey, address2, data);
119
+
120
+ expect(result1).not.toEqual(result2);
121
+ });
122
+
123
+ it("should handle data longer than keystream (16 bytes)", () => {
124
+ const longData = Buffer.from(
125
+ "this is a very long message that exceeds sixteen bytes",
126
+ "ascii",
127
+ );
128
+
129
+ const encoded = plejdEncodeDecode(testKey, testAddress, longData);
130
+ const decoded = plejdEncodeDecode(testKey, testAddress, encoded);
131
+
132
+ expect(decoded).toEqual(longData);
133
+ expect(encoded.length).toBe(longData.length);
134
+ });
135
+
136
+ it("should handle binary data", () => {
137
+ const binaryData = Buffer.from([0x00, 0xff, 0x80, 0x7f, 0x01, 0xfe]);
138
+
139
+ const encoded = plejdEncodeDecode(testKey, testAddress, binaryData);
140
+ const decoded = plejdEncodeDecode(testKey, testAddress, encoded);
141
+
142
+ expect(decoded).toEqual(binaryData);
143
+ });
144
+
145
+ it("should create proper keystream buffer from address", () => {
146
+ // Use proper 6-byte MAC address format
147
+ const address = Buffer.from("001122334455", "hex"); // 6 bytes
148
+ const data = Buffer.from([0x00]); // XOR with 0 reveals keystream
149
+
150
+ const result = plejdEncodeDecode(testKey, address, data);
151
+
152
+ // Should return the first byte of the keystream
153
+ expect(result.length).toBe(1);
154
+ });
155
+
156
+ it("should repeat keystream every 16 bytes", () => {
157
+ // Create data that will reveal the keystream pattern
158
+ const data = Buffer.alloc(32, 0); // 32 zeros
159
+
160
+ const result = plejdEncodeDecode(testKey, testAddress, data);
161
+
162
+ // Bytes 0 and 16 should be the same (keystream repeats)
163
+ expect(result[0]).toBe(result[16]);
164
+ expect(result[15]).toBe(result[31]);
165
+ });
166
+ });
167
+
168
+ describe("delay", () => {
169
+ it("should resolve after specified milliseconds", async () => {
170
+ const start = Date.now();
171
+
172
+ await delay(100);
173
+
174
+ const elapsed = Date.now() - start;
175
+ expect(elapsed).toBeGreaterThanOrEqual(95); // Allow some tolerance
176
+ expect(elapsed).toBeLessThan(150);
177
+ });
178
+
179
+ it("should resolve immediately for 0ms delay", async () => {
180
+ const start = Date.now();
181
+
182
+ await delay(0);
183
+
184
+ const elapsed = Date.now() - start;
185
+ expect(elapsed).toBeLessThan(10);
186
+ });
187
+ });
188
+
189
+ describe("race", () => {
190
+ it("should resolve when operation completes before timeout", async () => {
191
+ const operation = async () => {
192
+ await delay(100);
193
+ return "success";
194
+ };
195
+
196
+ const result = await race(operation, 200);
197
+
198
+ expect(result).toBe("success");
199
+ });
200
+
201
+ it("should reject with timeout error when operation takes too long", async () => {
202
+ const operation = async () => {
203
+ await delay(200);
204
+ return "success";
205
+ };
206
+
207
+ await expect(race(operation, 100)).rejects.toThrow("BLE operation timeout");
208
+ });
209
+
210
+ it("should reject when operation throws before timeout", async () => {
211
+ const operation = async () => {
212
+ throw new Error("operation failed");
213
+ };
214
+
215
+ await expect(race(operation, 100)).rejects.toThrow("operation failed");
216
+ });
217
+
218
+ it("should handle operation that returns different types", async () => {
219
+ const numberOperation = async () => 42;
220
+ const objectOperation = async () => ({ key: "value" });
221
+
222
+ const numberResult = await race(numberOperation, 100);
223
+ const objectResult = await race(objectOperation, 100);
224
+
225
+ expect(numberResult).toBe(42);
226
+ expect(objectResult).toEqual({ key: "value" });
227
+ });
228
+ });
229
+
230
+ describe("Device Type Detection", () => {
231
+ describe("isDimmable", () => {
232
+ const testCases = [
233
+ { model: "DIM-01", expected: true },
234
+ { model: "DIM-02", expected: true },
235
+ { model: "LED-10", expected: true },
236
+ { model: "DIM-01-2P", expected: true },
237
+ { model: "LED-75", expected: true },
238
+ { model: "DIM-02-LC 2024 Q2", expected: true },
239
+ { model: "Dim 2.2.1 Release Candidate", expected: true },
240
+ { model: "RTR-01", expected: false },
241
+ { model: "WPH-01", expected: false },
242
+ { model: "UNKNOWN-MODEL", expected: false },
243
+ ];
244
+
245
+ testCases.forEach(({ model, expected }) => {
246
+ it(`should identify ${model} as ${expected ? "dimmable" : "non-dimmable"}`, () => {
247
+ expect(isDimmable(model)).toBe(expected);
248
+ });
249
+ });
250
+
251
+ // Test with real-world data
252
+ const realWorldDimmers = [
253
+ "DIM-02-LC 2024 Q2",
254
+ "Dim 2.2.1 Release Candidate",
255
+ "DIM-02",
256
+ "DIM-01",
257
+ ];
258
+
259
+ realWorldDimmers.forEach((model) => {
260
+ it(`should identify real-world model ${model} as dimmable`, () => {
261
+ expect(isDimmable(model)).toBe(true);
262
+ });
263
+ });
264
+ });
265
+
266
+ describe("isAddon", () => {
267
+ const testCases = [
268
+ { model: "RTR-01", expected: true },
269
+ { model: "WPH-01", expected: true },
270
+ { model: "WRT-01", expected: true },
271
+ { model: "MNT-01", expected: true },
272
+ { model: "MNT-02", expected: true },
273
+ { model: "GWY-01", expected: true },
274
+ { model: "BAT-01", expected: true },
275
+ { model: "EXT-01", expected: true },
276
+ { model: "DIM-01", expected: false },
277
+ { model: "UNKNOWN-MODEL", expected: false },
278
+ ];
279
+
280
+ testCases.forEach(({ model, expected }) => {
281
+ it(`should identify ${model} as ${expected ? "addon" : "non-addon"}`, () => {
282
+ expect(isAddon(model)).toBe(expected);
283
+ });
284
+ });
285
+ });
286
+ });
@@ -1,3 +0,0 @@
1
- export declare const plejdChalResp: (key: Buffer, chal: Buffer) => Buffer;
2
- export declare const plejdEncodeDecode: (key: Buffer, addressBuffer: Buffer, data: Buffer) => Buffer;
3
- export declare const reverseBuffer: (src: Buffer) => Buffer;
@@ -1,40 +0,0 @@
1
- import { createCipheriv, createHash } from 'crypto';
2
- export const plejdChalResp = (key, chal) => {
3
- const intermediate = createHash('sha256').update(xor(key, chal)).digest();
4
- const part1 = intermediate.slice(0, 16);
5
- const part2 = intermediate.slice(16);
6
- return xor(part1, part2);
7
- };
8
- export const plejdEncodeDecode = (key, addressBuffer, data) => {
9
- const buf = Buffer.concat([
10
- addressBuffer,
11
- addressBuffer,
12
- addressBuffer.subarray(0, 4),
13
- ]);
14
- const cipher = createCipheriv('aes-128-ecb', key, '');
15
- cipher.setAutoPadding(false);
16
- let ct = cipher.update(buf).toString('hex');
17
- ct += cipher.final().toString('hex');
18
- const ctBuff = Buffer.from(ct, 'hex');
19
- let output = '';
20
- for (let i = 0, length = data.length; i < length; i++) {
21
- output += String.fromCharCode(data[i] ^ ctBuff[i % 16]);
22
- }
23
- return Buffer.from(output, 'ascii');
24
- };
25
- export const reverseBuffer = (src) => {
26
- const buffer = Buffer.allocUnsafe(src.length);
27
- for (let i = 0, j = src.length - 1; i <= j; ++i, --j) {
28
- buffer[i] = src[j];
29
- buffer[j] = src[i];
30
- }
31
- return buffer;
32
- };
33
- const xor = (first, second) => {
34
- const result = Buffer.alloc(first.length);
35
- for (let i = 0; i < first.length; i++) {
36
- result[i] = first[i] ^ second[i];
37
- }
38
- return result;
39
- };
40
- //# sourceMappingURL=plejdUtils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"plejdUtils.js","sourceRoot":"","sources":["../src/plejdUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,GAAW,EAAE,IAAY,EAAE,EAAE;IACzD,MAAM,YAAY,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAE1E,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAErC,OAAO,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC3B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,GAAW,EACX,aAAqB,EACrB,IAAY,EACJ,EAAE;IACV,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;QACxB,aAAa;QACb,aAAa;QACb,aAAa,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC;KAC7B,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACtD,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAE7B,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5C,EAAE,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAEtC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtD,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,GAAW,EAAU,EAAE;IACnD,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAE9C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;QACrD,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACnB,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IACrB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,MAAc,EAAU,EAAE;IACpD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC"}
package/dist/settings.js DELETED
@@ -1,47 +0,0 @@
1
- /**
2
- * This is the name of the platform that users will use to register the plugin in the Homebridge config.json
3
- */
4
- export const PLATFORM_NAME = "Plejd";
5
- /**
6
- * This must match the name of your plugin as defined the package.json
7
- */
8
- export const PLUGIN_NAME = "homebridge-plejd";
9
- export const PLEJD_WRITE_TIMEOUT = 200;
10
- export const PLEJD_PING_TIMEOUT = 3000;
11
- export const isDimmable = (type) => {
12
- const normalizedType = type.toLowerCase();
13
- // Check if the model starts with any of the basic dimmer types
14
- return PLEJD_LIGHTS.some((light) => normalizedType.startsWith(light.toLowerCase()) ||
15
- normalizedType.includes("dim"));
16
- };
17
- export const isAddon = (type) => {
18
- for (const x of PLEJD_ADDONS) {
19
- if (type.toLowerCase().includes(x.toLowerCase())) {
20
- return true;
21
- }
22
- }
23
- return false;
24
- };
25
- /**
26
- * Lights and switches from Plejd
27
- */
28
- const PLEJD_LIGHTS = ["DIM-01", "DIM-02", "LED-10", "DIM-01-2P", "LED-75"];
29
- // const PLEJD_SWITCHES = [
30
- // "REL-01",
31
- // "REL-02",
32
- // "REL-01-2P",
33
- // "DAL-01",
34
- // "SPR-01",
35
- // "CTR-01",
36
- // ];
37
- const PLEJD_ADDONS = [
38
- "RTR-01",
39
- "WPH-01",
40
- "WRT-01",
41
- "MNT-01",
42
- "MNT-02",
43
- "GWY-01",
44
- "BAT-01",
45
- "EXT-01",
46
- ];
47
- //# sourceMappingURL=settings.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC;AAErC;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,kBAAkB,CAAC;AAE9C,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AACvC,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEvC,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,EAAE;IACzC,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAC1C,+DAA+D;IAC/D,OAAO,YAAY,CAAC,IAAI,CACtB,CAAC,KAAK,EAAE,EAAE,CACR,cAAc,CAAC,UAAU,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QAC9C,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,CACjC,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAY,EAAE,EAAE;IACtC,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;QAC7B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YACjD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;AAE3E,2BAA2B;AAC3B,cAAc;AACd,cAAc;AACd,iBAAiB;AACjB,cAAc;AACd,cAAc;AACd,cAAc;AACd,KAAK;AAEL,MAAM,YAAY,GAAG;IACnB,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;CACT,CAAC"}
@@ -1,59 +0,0 @@
1
- import { isDimmable, isAddon } from "../src/settings";
2
-
3
- describe("Device Type Detection", () => {
4
- describe("isDimmable", () => {
5
- const testCases = [
6
- { model: "DIM-01", expected: true },
7
- { model: "DIM-02", expected: true },
8
- { model: "LED-10", expected: true },
9
- { model: "DIM-01-2P", expected: true },
10
- { model: "LED-75", expected: true },
11
- { model: "DIM-02-LC 2024 Q2", expected: true },
12
- { model: "Dim 2.2.1 Release Candidate", expected: true },
13
- { model: "RTR-01", expected: false },
14
- { model: "WPH-01", expected: false },
15
- { model: "UNKNOWN-MODEL", expected: false },
16
- ];
17
-
18
- testCases.forEach(({ model, expected }) => {
19
- it(`should identify ${model} as ${expected ? "dimmable" : "non-dimmable"}`, () => {
20
- expect(isDimmable(model)).toBe(expected);
21
- });
22
- });
23
-
24
- // Test with real-world data
25
- const realWorldDimmers = [
26
- "DIM-02-LC 2024 Q2",
27
- "Dim 2.2.1 Release Candidate",
28
- "DIM-02",
29
- "DIM-01",
30
- ];
31
-
32
- realWorldDimmers.forEach((model) => {
33
- it(`should identify real-world model ${model} as dimmable`, () => {
34
- expect(isDimmable(model)).toBe(true);
35
- });
36
- });
37
- });
38
-
39
- describe("isAddon", () => {
40
- const testCases = [
41
- { model: "RTR-01", expected: true },
42
- { model: "WPH-01", expected: true },
43
- { model: "WRT-01", expected: true },
44
- { model: "MNT-01", expected: true },
45
- { model: "MNT-02", expected: true },
46
- { model: "GWY-01", expected: true },
47
- { model: "BAT-01", expected: true },
48
- { model: "EXT-01", expected: true },
49
- { model: "DIM-01", expected: false },
50
- { model: "UNKNOWN-MODEL", expected: false },
51
- ];
52
-
53
- testCases.forEach(({ model, expected }) => {
54
- it(`should identify ${model} as ${expected ? "addon" : "non-addon"}`, () => {
55
- expect(isAddon(model)).toBe(expected);
56
- });
57
- });
58
- });
59
- });