homebridge-lanternic 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -34
- package/config.schema.json +58 -124
- package/dist/ble/magicLanternCommands.d.ts +0 -1
- package/dist/ble/magicLanternCommands.js +0 -6
- package/dist/ble/magicLanternCommands.js.map +1 -1
- package/dist/platform.d.ts +0 -1
- package/dist/platform.js +4 -8
- package/dist/platform.js.map +1 -1
- package/dist/platformAccessory.d.ts +0 -7
- package/dist/platformAccessory.js +1 -78
- package/dist/platformAccessory.js.map +1 -1
- package/dist/types.d.ts +3 -16
- package/package.json +1 -1
- package/tools/send.mjs +11 -33
- package/dist/effects.d.ts +0 -15
- package/dist/effects.js +0 -50
- package/dist/effects.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Homebridge LanternIC
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/homebridge-lanternic)
|
|
4
|
+
[](https://www.npmjs.com/package/homebridge-lanternic)
|
|
4
5
|
[](https://github.com/Gibsonmb71/homebridge-lanternic/actions/workflows/ci.yml)
|
|
5
6
|
[](https://www.npmjs.com/package/homebridge-lanternic)
|
|
6
7
|
[](https://homebridge.io/)
|
|
@@ -15,7 +16,6 @@ These unbranded Magic Lantern BLE strips ship with multiple firmware variants so
|
|
|
15
16
|
- HomeKit Lightbulb service
|
|
16
17
|
- On/off with RGB-black fallback plus optional native Magic Lantern power frames
|
|
17
18
|
- Brightness via RGB scaling by default, with optional native Magic Lantern brightness frames
|
|
18
|
-
- Optional HomeKit switches for Magic Lantern effects
|
|
19
19
|
- HomeKit on/off, brightness, and color control confirmed on a `MELK-OC21WCT31` strip
|
|
20
20
|
- Homebridge UI config schema
|
|
21
21
|
- BLE discovery with log snippets and optional auto-add
|
|
@@ -23,7 +23,7 @@ These unbranded Magic Lantern BLE strips ship with multiple firmware variants so
|
|
|
23
23
|
- Command-builder tests for the known Magic Lantern frames
|
|
24
24
|
- Package smoke tests against Homebridge 1 and 2 on Node.js 22 and 24 in CI
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Effects and segment control are not supported as there is no good way to expose them in Apple Home.
|
|
27
27
|
|
|
28
28
|
## Install
|
|
29
29
|
|
|
@@ -56,11 +56,12 @@ LanternIC supports Homebridge UI through `config.schema.json`.
|
|
|
56
56
|
1. Install the plugin.
|
|
57
57
|
2. Open Homebridge UI.
|
|
58
58
|
3. Go to Plugins, select LanternIC, then Settings.
|
|
59
|
-
4.
|
|
60
|
-
5.
|
|
61
|
-
6.
|
|
59
|
+
4. Leave `Scan For Light Strips On Startup` enabled.
|
|
60
|
+
5. Restart Homebridge and open the logs.
|
|
61
|
+
6. Copy the `LanternIC device config: {...}` line for your strip into `Configured Light Strips`.
|
|
62
|
+
7. Save and restart Homebridge again.
|
|
62
63
|
|
|
63
|
-
|
|
64
|
+
Most setups only need a strip name and Bluetooth address. Use `Show Advanced Settings` only for Bluetooth adapter options, discovery filters, or protocol fallback tuning.
|
|
64
65
|
|
|
65
66
|
You can also use the local scanner:
|
|
66
67
|
|
|
@@ -78,7 +79,7 @@ LANTERNIC_SCAN_ALL=1 lanternic-scan
|
|
|
78
79
|
|
|
79
80
|
When working from this repository instead of a global install, use `npm run scan` in place of `lanternic-scan`.
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
`Automatically Add Discovered Strips` can create HomeKit accessories for matching BLE candidates. Leave it off until you are nearby and ready to test, because nearby BLE devices may advertise similar services.
|
|
82
83
|
|
|
83
84
|
## CLI Tools
|
|
84
85
|
|
|
@@ -89,7 +90,6 @@ lanternic-scan
|
|
|
89
90
|
lanternic-explore <address>
|
|
90
91
|
lanternic-send <address> rgb ff0000
|
|
91
92
|
lanternic-send <address> brightness 50
|
|
92
|
-
lanternic-send <address> effect 207 39
|
|
93
93
|
lanternic-send-sequence <address> 7e070503ff000010ef 7e07050300ff0010ef
|
|
94
94
|
lanternic-calibrate <address>
|
|
95
95
|
```
|
|
@@ -149,7 +149,7 @@ Start with discovery enabled and no devices:
|
|
|
149
149
|
{
|
|
150
150
|
"platform": "LanternIC",
|
|
151
151
|
"name": "LanternIC",
|
|
152
|
-
"
|
|
152
|
+
"showAdvanced": false,
|
|
153
153
|
"devices": [],
|
|
154
154
|
"discovery": {
|
|
155
155
|
"enabled": true,
|
|
@@ -168,6 +168,7 @@ Restart Homebridge and look for candidate device addresses in the logs. Then add
|
|
|
168
168
|
{
|
|
169
169
|
"name": "TV Strip",
|
|
170
170
|
"address": "BE:16:70:00:08:2A",
|
|
171
|
+
"showAdvanced": false,
|
|
171
172
|
"model": "Magic Lantern RGBIC",
|
|
172
173
|
"colorOrder": "rgb",
|
|
173
174
|
"powerMode": "both",
|
|
@@ -177,6 +178,7 @@ Restart Homebridge and look for candidate device addresses in the logs. Then add
|
|
|
177
178
|
"discovery": {
|
|
178
179
|
"enabled": true,
|
|
179
180
|
"autoAdd": false,
|
|
181
|
+
"showAdvanced": false,
|
|
180
182
|
"scanSeconds": 20,
|
|
181
183
|
"minRssi": -95,
|
|
182
184
|
"namePrefixes": [
|
|
@@ -207,31 +209,6 @@ Restart Homebridge and look for candidate device addresses in the logs. Then add
|
|
|
207
209
|
6. If colors are swapped, change `colorOrder`.
|
|
208
210
|
7. If Off leaves the strip visibly on, keep `powerMode` as `both` or try `rgbBlack`.
|
|
209
211
|
8. If brightness does not change, keep `brightnessMode` as `rgb`; if it double-dims, try `native`.
|
|
210
|
-
9. If effects are enabled, test one effect switch at a time. Turning an effect switch off should restore the saved HomeKit color.
|
|
211
|
-
|
|
212
|
-
## HomeKit Effects
|
|
213
|
-
|
|
214
|
-
Magic Lantern animation effects are exposed as optional `Switch` services on the same HomeKit accessory as the light. Apple Home does not provide a native effect picker for a `Lightbulb`, and custom effect characteristics usually do not appear in the Home app.
|
|
215
|
-
|
|
216
|
-
Effects are disabled by default. Enable them per strip:
|
|
217
|
-
|
|
218
|
-
```json
|
|
219
|
-
{
|
|
220
|
-
"effects": {
|
|
221
|
-
"enabled": true,
|
|
222
|
-
"defaultSpeed": 39,
|
|
223
|
-
"restoreColorOnDisable": true
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
When `effects.enabled` is true and no custom `items` list is provided, LanternIC creates these starter switches:
|
|
229
|
-
|
|
230
|
-
- AutoPlay: effect code `0`
|
|
231
|
-
- Magic Back: effect code `1`
|
|
232
|
-
- Yellow Marquee: effect code `207` (`0xcf`)
|
|
233
|
-
|
|
234
|
-
Only one effect switch is kept active at a time. Turning on an effect sends the speed frame followed by the effect frame. Turning off the active effect restores the saved HomeKit color and brightness when `restoreColorOnDisable` is true.
|
|
235
212
|
|
|
236
213
|
## Protocol Notes
|
|
237
214
|
|
package/config.schema.json
CHANGED
|
@@ -15,38 +15,18 @@
|
|
|
15
15
|
"type": "string",
|
|
16
16
|
"default": "LanternIC"
|
|
17
17
|
},
|
|
18
|
-
"
|
|
19
|
-
"title": "
|
|
20
|
-
"type": "
|
|
21
|
-
"default":
|
|
22
|
-
"description": "
|
|
23
|
-
"oneOf": [
|
|
24
|
-
{
|
|
25
|
-
"title": "Auto Mode",
|
|
26
|
-
"enum": [
|
|
27
|
-
"auto"
|
|
28
|
-
]
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"title": "Manual Mode",
|
|
32
|
-
"enum": [
|
|
33
|
-
"manual"
|
|
34
|
-
]
|
|
35
|
-
}
|
|
36
|
-
],
|
|
37
|
-
"condition": {
|
|
38
|
-
"functionBody": "return !model.devices || model.devices.length === 0;"
|
|
39
|
-
}
|
|
18
|
+
"showAdvanced": {
|
|
19
|
+
"title": "Show Advanced Settings",
|
|
20
|
+
"type": "boolean",
|
|
21
|
+
"default": false,
|
|
22
|
+
"description": "Show Bluetooth transport, discovery filters, and protocol tuning options. Most users can leave this off."
|
|
40
23
|
},
|
|
41
24
|
"devices": {
|
|
42
|
-
"title": "Light Strips",
|
|
25
|
+
"title": "Configured Light Strips",
|
|
43
26
|
"type": "array",
|
|
44
27
|
"buttonText": "Add Light Strip",
|
|
45
28
|
"default": [],
|
|
46
|
-
"description": "
|
|
47
|
-
"condition": {
|
|
48
|
-
"functionBody": "return (model.devices && model.devices.length > 0) || model.setupMode === 'manual';"
|
|
49
|
-
},
|
|
29
|
+
"description": "Add strips discovered in the logs. Usually only Name and Bluetooth Address are needed.",
|
|
50
30
|
"items": {
|
|
51
31
|
"type": "object",
|
|
52
32
|
"required": [
|
|
@@ -63,23 +43,39 @@
|
|
|
63
43
|
"address": {
|
|
64
44
|
"title": "Bluetooth Address / Id",
|
|
65
45
|
"type": "string",
|
|
66
|
-
"description": "
|
|
46
|
+
"description": "Use the address printed in LanternIC discovery logs. Colons are optional.",
|
|
67
47
|
"placeholder": "BE:16:70:00:08:2A"
|
|
68
48
|
},
|
|
49
|
+
"showAdvanced": {
|
|
50
|
+
"title": "Show Advanced Strip Settings",
|
|
51
|
+
"type": "boolean",
|
|
52
|
+
"default": false,
|
|
53
|
+
"description": "Show color order and protocol fallback settings for this strip."
|
|
54
|
+
},
|
|
69
55
|
"model": {
|
|
70
56
|
"title": "Model",
|
|
71
57
|
"type": "string",
|
|
72
|
-
"default": "Magic Lantern RGBIC"
|
|
58
|
+
"default": "Magic Lantern RGBIC",
|
|
59
|
+
"condition": {
|
|
60
|
+
"functionBody": "return model.devices && model.devices.showAdvanced === true;"
|
|
61
|
+
}
|
|
73
62
|
},
|
|
74
63
|
"manufacturer": {
|
|
75
64
|
"title": "Manufacturer",
|
|
76
65
|
"type": "string",
|
|
77
|
-
"default": "Magic Lantern"
|
|
66
|
+
"default": "Magic Lantern",
|
|
67
|
+
"condition": {
|
|
68
|
+
"functionBody": "return model.devices && model.devices.showAdvanced === true;"
|
|
69
|
+
}
|
|
78
70
|
},
|
|
79
71
|
"colorOrder": {
|
|
80
72
|
"title": "Color Order",
|
|
81
73
|
"type": "string",
|
|
82
74
|
"default": "rgb",
|
|
75
|
+
"description": "Change this only if red, green, and blue appear swapped in HomeKit.",
|
|
76
|
+
"condition": {
|
|
77
|
+
"functionBody": "return model.devices && model.devices.showAdvanced === true;"
|
|
78
|
+
},
|
|
83
79
|
"oneOf": [
|
|
84
80
|
{
|
|
85
81
|
"title": "RGB",
|
|
@@ -124,6 +120,9 @@
|
|
|
124
120
|
"type": "string",
|
|
125
121
|
"default": "both",
|
|
126
122
|
"description": "Both sends RGB black before the native off frame. This is the safest default for firmware that accepts RGB colors but ignores native power frames.",
|
|
123
|
+
"condition": {
|
|
124
|
+
"functionBody": "return model.devices && model.devices.showAdvanced === true;"
|
|
125
|
+
},
|
|
127
126
|
"oneOf": [
|
|
128
127
|
{
|
|
129
128
|
"title": "Native + RGB Black Fallback",
|
|
@@ -150,6 +149,9 @@
|
|
|
150
149
|
"type": "string",
|
|
151
150
|
"default": "rgb",
|
|
152
151
|
"description": "RGB scaling dims by changing RGB values, which works on strips that ignore native brightness frames.",
|
|
152
|
+
"condition": {
|
|
153
|
+
"functionBody": "return model.devices && model.devices.showAdvanced === true;"
|
|
154
|
+
},
|
|
153
155
|
"oneOf": [
|
|
154
156
|
{
|
|
155
157
|
"title": "RGB Scaling",
|
|
@@ -170,119 +172,42 @@
|
|
|
170
172
|
]
|
|
171
173
|
}
|
|
172
174
|
]
|
|
173
|
-
},
|
|
174
|
-
"effects": {
|
|
175
|
-
"title": "Effects",
|
|
176
|
-
"type": "object",
|
|
177
|
-
"additionalProperties": false,
|
|
178
|
-
"description": "Optional HomeKit switch services for Magic Lantern animation effects.",
|
|
179
|
-
"properties": {
|
|
180
|
-
"enabled": {
|
|
181
|
-
"title": "Expose Effect Switches",
|
|
182
|
-
"type": "boolean",
|
|
183
|
-
"default": false,
|
|
184
|
-
"description": "Add HomeKit switches for Magic Lantern effects on this strip. Turning an effect switch off restores the normal light color by default."
|
|
185
|
-
},
|
|
186
|
-
"defaultSpeed": {
|
|
187
|
-
"title": "Default Effect Speed",
|
|
188
|
-
"type": "integer",
|
|
189
|
-
"default": 39,
|
|
190
|
-
"minimum": 0,
|
|
191
|
-
"maximum": 100,
|
|
192
|
-
"description": "Speed frame value sent before effect frames. 0 is slowest, 100 is fastest."
|
|
193
|
-
},
|
|
194
|
-
"restoreColorOnDisable": {
|
|
195
|
-
"title": "Restore Color When Effect Turns Off",
|
|
196
|
-
"type": "boolean",
|
|
197
|
-
"default": true,
|
|
198
|
-
"description": "When an active effect switch is turned off, send the saved HomeKit color and brightness back to the strip."
|
|
199
|
-
},
|
|
200
|
-
"items": {
|
|
201
|
-
"title": "Effect Switches",
|
|
202
|
-
"type": "array",
|
|
203
|
-
"buttonText": "Add Effect",
|
|
204
|
-
"default": [
|
|
205
|
-
{
|
|
206
|
-
"name": "AutoPlay",
|
|
207
|
-
"code": 0
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
"name": "Magic Back",
|
|
211
|
-
"code": 1
|
|
212
|
-
},
|
|
213
|
-
{
|
|
214
|
-
"name": "Yellow Marquee",
|
|
215
|
-
"code": 207
|
|
216
|
-
}
|
|
217
|
-
],
|
|
218
|
-
"description": "Effect codes are the xx byte in 7e0503xx06ffff00ef. Codes may be decimal, so Yellow Marquee is 207 for hex CF.",
|
|
219
|
-
"items": {
|
|
220
|
-
"type": "object",
|
|
221
|
-
"required": [
|
|
222
|
-
"name",
|
|
223
|
-
"code"
|
|
224
|
-
],
|
|
225
|
-
"additionalProperties": false,
|
|
226
|
-
"properties": {
|
|
227
|
-
"name": {
|
|
228
|
-
"title": "Name",
|
|
229
|
-
"type": "string",
|
|
230
|
-
"default": "Effect"
|
|
231
|
-
},
|
|
232
|
-
"code": {
|
|
233
|
-
"title": "Effect Code",
|
|
234
|
-
"type": "integer",
|
|
235
|
-
"default": 0,
|
|
236
|
-
"minimum": 0,
|
|
237
|
-
"maximum": 255,
|
|
238
|
-
"description": "Decimal value for the effect byte. For example hex CF is decimal 207."
|
|
239
|
-
},
|
|
240
|
-
"id": {
|
|
241
|
-
"title": "Stable Id",
|
|
242
|
-
"type": "string",
|
|
243
|
-
"description": "Optional stable HomeKit service id. Leave blank unless you are renaming or reordering effects and want to preserve existing HomeKit tiles."
|
|
244
|
-
},
|
|
245
|
-
"speed": {
|
|
246
|
-
"title": "Effect Speed Override",
|
|
247
|
-
"type": "integer",
|
|
248
|
-
"minimum": 0,
|
|
249
|
-
"maximum": 100,
|
|
250
|
-
"description": "Optional speed override for this effect. Leave blank to use Default Effect Speed."
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
175
|
}
|
|
257
176
|
}
|
|
258
177
|
}
|
|
259
178
|
},
|
|
260
179
|
"discovery": {
|
|
261
|
-
"title": "Discovery",
|
|
180
|
+
"title": "Setup & Discovery",
|
|
262
181
|
"type": "object",
|
|
263
182
|
"additionalProperties": false,
|
|
264
|
-
"condition": {
|
|
265
|
-
"functionBody": "return model.devices && model.devices.length > 0;"
|
|
266
|
-
},
|
|
267
183
|
"properties": {
|
|
268
184
|
"enabled": {
|
|
269
|
-
"title": "
|
|
185
|
+
"title": "Scan For Light Strips On Startup",
|
|
270
186
|
"type": "boolean",
|
|
271
187
|
"default": true,
|
|
272
|
-
"description": "
|
|
188
|
+
"description": "Print nearby Magic Lantern candidates in the Homebridge logs with copy/paste device snippets."
|
|
273
189
|
},
|
|
274
190
|
"autoAdd": {
|
|
275
|
-
"title": "
|
|
191
|
+
"title": "Automatically Add Discovered Strips",
|
|
276
192
|
"type": "boolean",
|
|
277
193
|
"default": false,
|
|
278
|
-
"description": "
|
|
194
|
+
"description": "Create HomeKit accessories for matching candidates. Leave off unless you are nearby and ready to test."
|
|
195
|
+
},
|
|
196
|
+
"showAdvanced": {
|
|
197
|
+
"title": "Show Advanced Discovery Filters",
|
|
198
|
+
"type": "boolean",
|
|
199
|
+
"default": false,
|
|
200
|
+
"description": "Show scan duration, RSSI, name prefix, and service UUID filters."
|
|
279
201
|
},
|
|
280
202
|
"scanSeconds": {
|
|
281
203
|
"title": "Scan Time",
|
|
282
204
|
"type": "integer",
|
|
283
205
|
"default": 15,
|
|
284
206
|
"minimum": 3,
|
|
285
|
-
"maximum": 120
|
|
207
|
+
"maximum": 120,
|
|
208
|
+
"condition": {
|
|
209
|
+
"functionBody": "return model.showAdvanced === true || (model.discovery && model.discovery.showAdvanced === true);"
|
|
210
|
+
}
|
|
286
211
|
},
|
|
287
212
|
"minRssi": {
|
|
288
213
|
"title": "Minimum RSSI",
|
|
@@ -290,7 +215,10 @@
|
|
|
290
215
|
"default": -95,
|
|
291
216
|
"minimum": -127,
|
|
292
217
|
"maximum": 0,
|
|
293
|
-
"description": "Ignore very weak candidates. Higher values are stricter; for example -70 only finds nearby devices."
|
|
218
|
+
"description": "Ignore very weak candidates. Higher values are stricter; for example -70 only finds nearby devices.",
|
|
219
|
+
"condition": {
|
|
220
|
+
"functionBody": "return model.showAdvanced === true || (model.discovery && model.discovery.showAdvanced === true);"
|
|
221
|
+
}
|
|
294
222
|
},
|
|
295
223
|
"namePrefixes": {
|
|
296
224
|
"title": "Candidate Name Prefixes",
|
|
@@ -304,6 +232,9 @@
|
|
|
304
232
|
],
|
|
305
233
|
"items": {
|
|
306
234
|
"type": "string"
|
|
235
|
+
},
|
|
236
|
+
"condition": {
|
|
237
|
+
"functionBody": "return model.showAdvanced === true || (model.discovery && model.discovery.showAdvanced === true);"
|
|
307
238
|
}
|
|
308
239
|
},
|
|
309
240
|
"serviceUuids": {
|
|
@@ -315,6 +246,9 @@
|
|
|
315
246
|
"description": "Also treat devices advertising these services as candidates.",
|
|
316
247
|
"items": {
|
|
317
248
|
"type": "string"
|
|
249
|
+
},
|
|
250
|
+
"condition": {
|
|
251
|
+
"functionBody": "return model.showAdvanced === true || (model.discovery && model.discovery.showAdvanced === true);"
|
|
318
252
|
}
|
|
319
253
|
}
|
|
320
254
|
}
|
|
@@ -324,7 +258,7 @@
|
|
|
324
258
|
"type": "object",
|
|
325
259
|
"additionalProperties": false,
|
|
326
260
|
"condition": {
|
|
327
|
-
"functionBody": "return model.
|
|
261
|
+
"functionBody": "return model.showAdvanced === true;"
|
|
328
262
|
},
|
|
329
263
|
"properties": {
|
|
330
264
|
"binding": {
|
|
@@ -14,4 +14,3 @@ export declare const buildPowerOnPrefixCommands: (powerMode?: PowerMode, brightn
|
|
|
14
14
|
export declare const buildPowerOffCommands: (powerMode?: PowerMode) => Buffer[];
|
|
15
15
|
export declare const buildEffectSpeedCommand: (speed: number) => Buffer;
|
|
16
16
|
export declare const buildBasicEffectCommand: (effectCode: number) => Buffer;
|
|
17
|
-
export declare const buildBasicEffectCommands: (effectCode: number, speed: number) => Buffer[];
|
|
@@ -46,10 +46,4 @@ export const buildEffectSpeedCommand = (speed) => {
|
|
|
46
46
|
export const buildBasicEffectCommand = (effectCode) => {
|
|
47
47
|
return frame(0x7e, 0x05, 0x03, effectCode, 0x06, 0xff, 0xff, 0x00, 0xef);
|
|
48
48
|
};
|
|
49
|
-
export const buildBasicEffectCommands = (effectCode, speed) => {
|
|
50
|
-
return [
|
|
51
|
-
buildEffectSpeedCommand(speed),
|
|
52
|
-
buildBasicEffectCommand(effectCode),
|
|
53
|
-
];
|
|
54
|
-
};
|
|
55
49
|
//# sourceMappingURL=magicLanternCommands.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"magicLanternCommands.js","sourceRoot":"","sources":["../../src/ble/magicLanternCommands.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"magicLanternCommands.js","sourceRoot":"","sources":["../../src/ble/magicLanternCommands.ts"],"names":[],"mappings":"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"}
|
package/dist/platform.d.ts
CHANGED
package/dist/platform.js
CHANGED
|
@@ -39,7 +39,7 @@ export class LanternIcPlatform {
|
|
|
39
39
|
if (activeUuids.has(uuid)) {
|
|
40
40
|
continue;
|
|
41
41
|
}
|
|
42
|
-
if (this.discoveryAutoAdd(
|
|
42
|
+
if (this.discoveryAutoAdd() && accessory.context.autoDiscovered) {
|
|
43
43
|
const reason = discoverySucceeded ? 'it was previously auto-discovered' : 'discovery scan failed';
|
|
44
44
|
this.log.info(`Keeping cached auto-discovered accessory because ${reason}:`, accessory.displayName);
|
|
45
45
|
continue;
|
|
@@ -97,7 +97,7 @@ export class LanternIcPlatform {
|
|
|
97
97
|
}
|
|
98
98
|
for (const candidate of candidates) {
|
|
99
99
|
this.logCandidate(candidate);
|
|
100
|
-
if (!this.discoveryAutoAdd(
|
|
100
|
+
if (!this.discoveryAutoAdd() || this.isConfigured(candidate, configuredDevices)) {
|
|
101
101
|
continue;
|
|
102
102
|
}
|
|
103
103
|
const device = this.deviceFromCandidate(candidate);
|
|
@@ -130,12 +130,8 @@ export class LanternIcPlatform {
|
|
|
130
130
|
const candidateId = normalizeBluetoothId(candidate.address || candidate.id);
|
|
131
131
|
return devices.some(device => normalizeBluetoothId(device.address) === candidateId);
|
|
132
132
|
}
|
|
133
|
-
discoveryAutoAdd(
|
|
134
|
-
return this.config.discovery?.autoAdd === true
|
|
135
|
-
|| (this.firstSetupAutoMode() && configuredDevices.length === 0);
|
|
136
|
-
}
|
|
137
|
-
firstSetupAutoMode() {
|
|
138
|
-
return this.config.setupMode !== 'manual';
|
|
133
|
+
discoveryAutoAdd() {
|
|
134
|
+
return this.config.discovery?.autoAdd === true;
|
|
139
135
|
}
|
|
140
136
|
validDevices() {
|
|
141
137
|
const devices = this.config.devices ?? [];
|
package/dist/platform.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,sBAAsB,EAAwB,MAAM,iCAAiC,CAAC;AAC/F,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE3D,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEnF,MAAM,OAAO,iBAAiB;IAQV;IACA;IACA;IATF,OAAO,CAAiB;IACxB,cAAc,CAAwB;IACtC,GAAG,CAAyB;IAE3B,WAAW,GAAG,IAAI,GAAG,EAAwD,CAAC;IAE/F,YACkB,GAAY,EACZ,MAA+B,EAC/B,GAAQ;QAFR,QAAG,GAAH,GAAG,CAAS;QACZ,WAAM,GAAN,MAAM,CAAyB;QAC/B,QAAG,GAAH,GAAG,CAAK;QAExB,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,cAAc,GAAG,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC;QAC7C,IAAI,CAAC,GAAG,GAAG,IAAI,sBAAsB,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;QAEvD,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;YACrC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;YAC/C,KAAK,IAAI,CAAC,gBAAgB,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;gBACzC,IAAI,CAAC,GAAG,CAAC,KAAK,CACZ,yBAAyB,EACzB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,kBAAkB,CAAC,SAAuD;QACxE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,+BAA+B,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;QACtE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAClD,CAAC;IAEO,KAAK,CAAC,gBAAgB;QAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACpC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;QAEtC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAErF,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACjD,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,SAAS;YACX,CAAC;YAED,IAAI,IAAI,CAAC,gBAAgB,
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,8 +7,6 @@ export declare class LanternIcPlatformAccessory {
|
|
|
7
7
|
private readonly client;
|
|
8
8
|
private readonly accessory;
|
|
9
9
|
private readonly service;
|
|
10
|
-
private readonly effectSettings;
|
|
11
|
-
private readonly effectServices;
|
|
12
10
|
private readonly state;
|
|
13
11
|
private colorTimer?;
|
|
14
12
|
private colorWriteWaiters;
|
|
@@ -21,13 +19,8 @@ export declare class LanternIcPlatformAccessory {
|
|
|
21
19
|
private buildCurrentColorCommand;
|
|
22
20
|
private buildPowerOnCommands;
|
|
23
21
|
private buildBrightnessUpdateCommands;
|
|
24
|
-
private buildEffectCommands;
|
|
25
22
|
private powerMode;
|
|
26
23
|
private brightnessMode;
|
|
27
|
-
private configureEffectServices;
|
|
28
|
-
private setEffectSwitch;
|
|
29
|
-
private updateEffectSwitches;
|
|
30
|
-
private findEffect;
|
|
31
24
|
private resyncAfterReconnect;
|
|
32
25
|
private saveState;
|
|
33
26
|
}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { applyColorOrder, clampPercent, hsbToRgb } from './color.js';
|
|
2
|
-
import {
|
|
3
|
-
import { EFFECT_SERVICE_SUBTYPE_PREFIX, resolveEffectsConfig, } from './effects.js';
|
|
2
|
+
import { buildColorCommand, buildNativeBrightnessCommands, buildPowerOffCommands, buildPowerOnPrefixCommands, shouldScaleRgbBrightness, } from './ble/magicLanternCommands.js';
|
|
4
3
|
export class LanternIcPlatformAccessory {
|
|
5
4
|
platform;
|
|
6
5
|
device;
|
|
7
6
|
client;
|
|
8
7
|
accessory;
|
|
9
8
|
service;
|
|
10
|
-
effectSettings;
|
|
11
|
-
effectServices = new Map();
|
|
12
9
|
state;
|
|
13
10
|
colorTimer;
|
|
14
11
|
colorWriteWaiters = [];
|
|
@@ -25,12 +22,7 @@ export class LanternIcPlatformAccessory {
|
|
|
25
22
|
brightness: accessory.context.state?.brightness ?? 100,
|
|
26
23
|
hue: accessory.context.state?.hue ?? 0,
|
|
27
24
|
saturation: accessory.context.state?.saturation ?? 0,
|
|
28
|
-
activeEffect: accessory.context.state?.activeEffect,
|
|
29
25
|
};
|
|
30
|
-
this.effectSettings = resolveEffectsConfig(this.device.effects);
|
|
31
|
-
if (this.state.activeEffect && !this.findEffect(this.state.activeEffect)) {
|
|
32
|
-
this.state.activeEffect = undefined;
|
|
33
|
-
}
|
|
34
26
|
const informationService = accessory.getService(this.platform.Service.AccessoryInformation)
|
|
35
27
|
?? accessory.addService(this.platform.Service.AccessoryInformation);
|
|
36
28
|
informationService
|
|
@@ -52,15 +44,12 @@ export class LanternIcPlatformAccessory {
|
|
|
52
44
|
this.service.getCharacteristic(this.platform.Characteristic.Saturation)
|
|
53
45
|
.onGet(() => this.state.saturation)
|
|
54
46
|
.onSet(value => this.setSaturation(Number(value)));
|
|
55
|
-
this.configureEffectServices();
|
|
56
47
|
this.client.onReconnect(() => this.resyncAfterReconnect());
|
|
57
48
|
this.client.start();
|
|
58
49
|
}
|
|
59
50
|
async setOn(onValue) {
|
|
60
51
|
this.state.on = onValue;
|
|
61
|
-
this.state.activeEffect = undefined;
|
|
62
52
|
this.saveState();
|
|
63
|
-
this.updateEffectSwitches();
|
|
64
53
|
if (!onValue) {
|
|
65
54
|
await this.client.writeCommands(buildPowerOffCommands(this.powerMode()));
|
|
66
55
|
return;
|
|
@@ -69,9 +58,7 @@ export class LanternIcPlatformAccessory {
|
|
|
69
58
|
}
|
|
70
59
|
async setBrightness(value) {
|
|
71
60
|
this.state.brightness = clampPercent(Number(value));
|
|
72
|
-
this.state.activeEffect = undefined;
|
|
73
61
|
this.saveState();
|
|
74
|
-
this.updateEffectSwitches();
|
|
75
62
|
if (!this.state.on) {
|
|
76
63
|
return;
|
|
77
64
|
}
|
|
@@ -79,16 +66,12 @@ export class LanternIcPlatformAccessory {
|
|
|
79
66
|
}
|
|
80
67
|
async setHue(value) {
|
|
81
68
|
this.state.hue = Number(value);
|
|
82
|
-
this.state.activeEffect = undefined;
|
|
83
69
|
this.saveState();
|
|
84
|
-
this.updateEffectSwitches();
|
|
85
70
|
await this.scheduleColorWrite();
|
|
86
71
|
}
|
|
87
72
|
async setSaturation(value) {
|
|
88
73
|
this.state.saturation = clampPercent(Number(value));
|
|
89
|
-
this.state.activeEffect = undefined;
|
|
90
74
|
this.saveState();
|
|
91
|
-
this.updateEffectSwitches();
|
|
92
75
|
await this.scheduleColorWrite();
|
|
93
76
|
}
|
|
94
77
|
async scheduleColorWrite() {
|
|
@@ -135,77 +118,17 @@ export class LanternIcPlatformAccessory {
|
|
|
135
118
|
...(shouldScaleRgbBrightness(this.brightnessMode()) ? [this.buildCurrentColorCommand()] : []),
|
|
136
119
|
];
|
|
137
120
|
}
|
|
138
|
-
buildEffectCommands(effect) {
|
|
139
|
-
return [
|
|
140
|
-
...buildPowerOnPrefixCommands(this.powerMode(), this.brightnessMode(), this.state.brightness),
|
|
141
|
-
...buildBasicEffectCommands(effect.code, effect.speed),
|
|
142
|
-
];
|
|
143
|
-
}
|
|
144
121
|
powerMode() {
|
|
145
122
|
return this.device.powerMode ?? 'both';
|
|
146
123
|
}
|
|
147
124
|
brightnessMode() {
|
|
148
125
|
return this.device.brightnessMode ?? 'rgb';
|
|
149
126
|
}
|
|
150
|
-
configureEffectServices() {
|
|
151
|
-
const wantedSubtypes = new Set(this.effectSettings.effects.map(effect => effect.subtype));
|
|
152
|
-
for (const existingService of [...this.accessory.services]) {
|
|
153
|
-
if (existingService.UUID === this.platform.Service.Switch.UUID
|
|
154
|
-
&& existingService.subtype?.startsWith(EFFECT_SERVICE_SUBTYPE_PREFIX)
|
|
155
|
-
&& !wantedSubtypes.has(existingService.subtype)) {
|
|
156
|
-
this.accessory.removeService(existingService);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
for (const effect of this.effectSettings.effects) {
|
|
160
|
-
const service = this.accessory.getServiceById(this.platform.Service.Switch, effect.subtype)
|
|
161
|
-
?? this.accessory.addService(this.platform.Service.Switch, effect.name, effect.subtype);
|
|
162
|
-
service.setCharacteristic(this.platform.Characteristic.Name, effect.name);
|
|
163
|
-
service.getCharacteristic(this.platform.Characteristic.On)
|
|
164
|
-
.onGet(() => this.state.activeEffect === effect.subtype)
|
|
165
|
-
.onSet(value => this.setEffectSwitch(effect, Boolean(value)));
|
|
166
|
-
this.effectServices.set(effect.subtype, service);
|
|
167
|
-
}
|
|
168
|
-
this.updateEffectSwitches();
|
|
169
|
-
}
|
|
170
|
-
async setEffectSwitch(effect, onValue) {
|
|
171
|
-
if (!onValue) {
|
|
172
|
-
if (this.state.activeEffect !== effect.subtype) {
|
|
173
|
-
this.updateEffectSwitches();
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
this.state.activeEffect = undefined;
|
|
177
|
-
this.saveState();
|
|
178
|
-
this.updateEffectSwitches();
|
|
179
|
-
if (this.effectSettings.restoreColorOnDisable && this.state.on) {
|
|
180
|
-
await this.client.writeCommands(this.buildPowerOnCommands());
|
|
181
|
-
}
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
this.state.on = true;
|
|
185
|
-
this.state.activeEffect = effect.subtype;
|
|
186
|
-
this.saveState();
|
|
187
|
-
this.service.updateCharacteristic(this.platform.Characteristic.On, true);
|
|
188
|
-
this.updateEffectSwitches();
|
|
189
|
-
await this.client.writeCommands(this.buildEffectCommands(effect));
|
|
190
|
-
}
|
|
191
|
-
updateEffectSwitches() {
|
|
192
|
-
for (const [subtype, service] of this.effectServices) {
|
|
193
|
-
service.updateCharacteristic(this.platform.Characteristic.On, this.state.activeEffect === subtype);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
findEffect(subtype) {
|
|
197
|
-
return this.effectSettings.effects.find(effect => effect.subtype === subtype);
|
|
198
|
-
}
|
|
199
127
|
async resyncAfterReconnect() {
|
|
200
128
|
if (!this.state.on) {
|
|
201
129
|
return;
|
|
202
130
|
}
|
|
203
131
|
this.platform.log.debug(`[${this.device.name}] Resyncing desired state after BLE reconnect`);
|
|
204
|
-
const activeEffect = this.findEffect(this.state.activeEffect);
|
|
205
|
-
if (activeEffect) {
|
|
206
|
-
await this.client.writeCommands(this.buildEffectCommands(activeEffect));
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
132
|
await this.client.writeCommands(this.buildPowerOnCommands());
|
|
210
133
|
}
|
|
211
134
|
saveState() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platformAccessory.js","sourceRoot":"","sources":["../src/platformAccessory.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAErE,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"platformAccessory.js","sourceRoot":"","sources":["../src/platformAccessory.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAErE,OAAO,EACL,iBAAiB,EACjB,6BAA6B,EAC7B,qBAAqB,EACrB,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AAWvC,MAAM,OAAO,0BAA0B;IAalB;IAZF,MAAM,CAAwB;IAC9B,MAAM,CAAwB;IAC9B,SAAS,CAA+C;IACxD,OAAO,CAAU;IACjB,KAAK,CAAa;IAC3B,UAAU,CAAiC;IAC3C,iBAAiB,GAGpB,EAAE,CAAC;IAER,YACmB,QAA2B,EAC5C,SAAuD;QADtC,aAAQ,GAAR,QAAQ,CAAmB;QAG5C,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,aAAa,SAAS,CAAC,WAAW,sCAAsC,CAAC,CAAC;QAC5F,CAAC;QAED,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC;QACvC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1D,IAAI,CAAC,KAAK,GAAG;YACX,EAAE,EAAE,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,IAAI,KAAK;YACxC,UAAU,EAAE,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,UAAU,IAAI,GAAG;YACtD,GAAG,EAAE,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC;YACtC,UAAU,EAAE,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,UAAU,IAAI,CAAC;SACrD,CAAC;QAEF,MAAM,kBAAkB,GAAG,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,oBAAoB,CAAC;eACtF,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;QAEtE,kBAAkB;aACf,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,eAAe,CAAC;aACzG,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,qBAAqB,CAAC;aACjG,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAErF,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC;eAC/D,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAE3D,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEpF,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;aAC5D,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;aAC1B,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAE9C,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAC;aACpE,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;aAClC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAErD,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC;aAC7D,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;aAC3B,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAE9C,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAC;aACpE,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;aAClC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAErD,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC,CAAC;QAC3D,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,KAAK,CAAC,OAAgB;QAClC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,SAAS,EAAE,CAAC;QAEjB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,qBAAqB,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;YACzE,OAAO;QACT,CAAC;QAED,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAC/D,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,KAA0B;QACpD,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,SAAS,EAAE,CAAC;QAEjB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QAED,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,6BAA6B,EAAE,CAAC,CAAC;IACxE,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,KAA0B;QAC7C,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAClC,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,KAA0B;QACpD,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAClC,CAAC;IAEO,KAAK,CAAC,kBAAkB;QAC9B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAChC,CAAC;QAED,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YACjD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;gBAChC,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBACjD,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;gBAE5B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,CAAC;qBACzD,IAAI,CAAC,GAAG,EAAE;oBACT,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;wBAC7B,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnB,CAAC;gBACH,CAAC,CAAC;qBACD,KAAK,CAAC,KAAK,CAAC,EAAE;oBACb,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;wBAC7B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBACvB,CAAC;gBACH,CAAC,CAAC,CAAC;YACP,CAAC,EAAE,GAAG,CAAC,CAAC;QACV,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,wBAAwB;QAC9B,MAAM,UAAU,GAAG,wBAAwB,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC;QACjG,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QACxE,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,KAAK,CAAC,CAAC;QACtE,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAEO,oBAAoB;QAC1B,OAAO;YACL,GAAG,0BAA0B,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;YAC7F,IAAI,CAAC,wBAAwB,EAAE;SAChC,CAAC;IACJ,CAAC;IAEO,6BAA6B;QACnC,OAAO;YACL,GAAG,6BAA6B,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC;YAC9E,GAAG,CAAC,wBAAwB,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9F,CAAC;IACJ,CAAC;IAEO,SAAS;QACf,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC;IACzC,CAAC;IAEO,cAAc;QACpB,OAAO,IAAI,CAAC,MAAM,CAAC,cAAc,IAAI,KAAK,CAAC;IAC7C,CAAC;IAEO,KAAK,CAAC,oBAAoB;QAChC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,+CAA+C,CAAC,CAAC;QAC7F,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAC/D,CAAC;IAEO,SAAS;QACf,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QACjD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IAChE,CAAC;CACF"}
|
package/dist/types.d.ts
CHANGED
|
@@ -5,32 +5,20 @@ export type BrightnessMode = 'rgb' | 'native' | 'both';
|
|
|
5
5
|
export type NobleBinding = 'default' | 'hci' | 'mac' | 'win';
|
|
6
6
|
export type HciDriver = 'default' | 'native' | 'usb' | 'uart';
|
|
7
7
|
export type WriteMode = 'auto' | 'withResponse' | 'withoutResponse';
|
|
8
|
-
export type SetupMode = 'auto' | 'manual';
|
|
9
|
-
export interface LanternIcEffectConfig {
|
|
10
|
-
name: string;
|
|
11
|
-
code: number;
|
|
12
|
-
id?: string;
|
|
13
|
-
speed?: number;
|
|
14
|
-
}
|
|
15
|
-
export interface LanternIcEffectsConfig {
|
|
16
|
-
enabled?: boolean;
|
|
17
|
-
defaultSpeed?: number;
|
|
18
|
-
restoreColorOnDisable?: boolean;
|
|
19
|
-
items?: LanternIcEffectConfig[];
|
|
20
|
-
}
|
|
21
8
|
export interface LanternIcDeviceConfig {
|
|
22
9
|
name: string;
|
|
23
10
|
address: string;
|
|
11
|
+
showAdvanced?: boolean;
|
|
24
12
|
manufacturer?: string;
|
|
25
13
|
model?: string;
|
|
26
14
|
colorOrder?: ColorOrder;
|
|
27
15
|
powerMode?: PowerMode;
|
|
28
16
|
brightnessMode?: BrightnessMode;
|
|
29
|
-
effects?: LanternIcEffectsConfig;
|
|
30
17
|
}
|
|
31
18
|
export interface LanternIcDiscoveryConfig {
|
|
32
19
|
enabled?: boolean;
|
|
33
20
|
autoAdd?: boolean;
|
|
21
|
+
showAdvanced?: boolean;
|
|
34
22
|
scanSeconds?: number;
|
|
35
23
|
minRssi?: number;
|
|
36
24
|
namePrefixes?: string[];
|
|
@@ -59,7 +47,7 @@ export interface LanternIcBleConfig {
|
|
|
59
47
|
}
|
|
60
48
|
export interface LanternIcPlatformConfig extends PlatformConfig {
|
|
61
49
|
name?: string;
|
|
62
|
-
|
|
50
|
+
showAdvanced?: boolean;
|
|
63
51
|
devices?: LanternIcDeviceConfig[];
|
|
64
52
|
discovery?: LanternIcDiscoveryConfig;
|
|
65
53
|
ble?: LanternIcBleConfig;
|
|
@@ -69,7 +57,6 @@ export interface LanternIcStoredState {
|
|
|
69
57
|
brightness?: number;
|
|
70
58
|
hue?: number;
|
|
71
59
|
saturation?: number;
|
|
72
|
-
activeEffect?: string;
|
|
73
60
|
}
|
|
74
61
|
export interface LanternIcAccessoryContext {
|
|
75
62
|
autoDiscovered?: boolean;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "homebridge-lanternic",
|
|
3
3
|
"displayName": "LanternIC",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.2.
|
|
5
|
+
"version": "0.2.1",
|
|
6
6
|
"description": "Homebridge plugin for BLE RGBIC/RGBWIC light strips controlled by the Magic Lantern app.",
|
|
7
7
|
"author": "Gibson Bell",
|
|
8
8
|
"license": "MIT",
|
package/tools/send.mjs
CHANGED
|
@@ -4,20 +4,16 @@ import { withBindings } from '@stoprocent/noble';
|
|
|
4
4
|
const binding = process.env.LANTERNIC_BINDING ?? 'default';
|
|
5
5
|
const serviceUuid = (process.env.LANTERNIC_SERVICE_UUID ?? 'fff0').replace(/[^0-9a-f]/gi, '').toLowerCase();
|
|
6
6
|
const characteristicUuid = (process.env.LANTERNIC_CHARACTERISTIC_UUID ?? 'fff3').replace(/[^0-9a-f]/gi, '').toLowerCase();
|
|
7
|
-
const delayMs = Number(process.env.LANTERNIC_WRITE_DELAY_MS ?? 120);
|
|
8
7
|
const address = process.argv[2];
|
|
9
8
|
const command = process.argv[3];
|
|
10
9
|
const value = process.argv[4];
|
|
11
|
-
const extraValue = process.argv[5];
|
|
12
10
|
|
|
13
11
|
if (!address || !command) {
|
|
14
|
-
console.error('Usage: lanternic-send <address> <on|off|brightness|
|
|
15
|
-
console.error('Repo development: npm run send -- <address> <on|off|brightness|
|
|
12
|
+
console.error('Usage: lanternic-send <address> <on|off|brightness|rgb|raw> [value]');
|
|
13
|
+
console.error('Repo development: npm run send -- <address> <on|off|brightness|rgb|raw> [value]');
|
|
16
14
|
console.error('Examples:');
|
|
17
15
|
console.error(' lanternic-send be:16:70:00:08:2a on');
|
|
18
16
|
console.error(' lanternic-send be:16:70:00:08:2a brightness 50');
|
|
19
|
-
console.error(' lanternic-send be:16:70:00:08:2a speed 39');
|
|
20
|
-
console.error(' lanternic-send be:16:70:00:08:2a effect 207 39');
|
|
21
17
|
console.error(' lanternic-send be:16:70:00:08:2a rgb ff0000');
|
|
22
18
|
console.error(' lanternic-send be:16:70:00:08:2a raw 7e0404f00001ff00ef');
|
|
23
19
|
process.exit(2);
|
|
@@ -31,33 +27,18 @@ const noble = withBindings(binding);
|
|
|
31
27
|
const byte = number => Math.max(0, Math.min(255, Math.round(number)));
|
|
32
28
|
const percent = number => Math.max(0, Math.min(100, Math.round(number)));
|
|
33
29
|
const frame = (...bytes) => Buffer.from(bytes.map(byte));
|
|
34
|
-
const wait = milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds));
|
|
35
30
|
|
|
36
|
-
const
|
|
31
|
+
const buildPayload = () => {
|
|
37
32
|
if (command === 'on') {
|
|
38
|
-
return
|
|
33
|
+
return Buffer.from('7e0404f00001ff00ef', 'hex');
|
|
39
34
|
}
|
|
40
35
|
|
|
41
36
|
if (command === 'off') {
|
|
42
|
-
return
|
|
37
|
+
return Buffer.from('7e0404000000ff00ef', 'hex');
|
|
43
38
|
}
|
|
44
39
|
|
|
45
40
|
if (command === 'brightness') {
|
|
46
|
-
return
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (command === 'speed') {
|
|
50
|
-
return [frame(0x7e, 0x04, 0x02, percent(Number(value)), 0xff, 0xff, 0xff, 0x00, 0xef)];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (command === 'effect') {
|
|
54
|
-
const payloads = [];
|
|
55
|
-
if (extraValue !== undefined) {
|
|
56
|
-
payloads.push(frame(0x7e, 0x04, 0x02, percent(Number(extraValue)), 0xff, 0xff, 0xff, 0x00, 0xef));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
payloads.push(frame(0x7e, 0x05, 0x03, byte(Number(value)), 0x06, 0xff, 0xff, 0x00, 0xef));
|
|
60
|
-
return payloads;
|
|
41
|
+
return frame(0x7e, 0x04, 0x01, percent(Number(value)), 0x01, 0xff, 0xff, 0x00, 0xef);
|
|
61
42
|
}
|
|
62
43
|
|
|
63
44
|
if (command === 'rgb') {
|
|
@@ -65,18 +46,18 @@ const buildPayloads = () => {
|
|
|
65
46
|
if (rgb.length !== 6) {
|
|
66
47
|
throw new Error('rgb value must be RRGGBB hex, for example ff0000');
|
|
67
48
|
}
|
|
68
|
-
return
|
|
49
|
+
return Buffer.from(`7e070503${rgb}10ef`, 'hex');
|
|
69
50
|
}
|
|
70
51
|
|
|
71
52
|
if (command === 'raw') {
|
|
72
|
-
return
|
|
53
|
+
return Buffer.from(cleanId(value), 'hex');
|
|
73
54
|
}
|
|
74
55
|
|
|
75
56
|
throw new Error(`Unknown command: ${command}`);
|
|
76
57
|
};
|
|
77
58
|
|
|
78
|
-
const
|
|
79
|
-
console.log(`Sending ${
|
|
59
|
+
const payload = buildPayload();
|
|
60
|
+
console.log(`Sending ${payload.toString('hex')} to ${address} with binding=${binding}`);
|
|
80
61
|
|
|
81
62
|
await noble.waitForPoweredOnAsync(15_000);
|
|
82
63
|
await noble.startScanningAsync([], true);
|
|
@@ -117,10 +98,7 @@ try {
|
|
|
117
98
|
|
|
118
99
|
const withoutResponse = !characteristic.properties.includes('write')
|
|
119
100
|
&& characteristic.properties.includes('writeWithoutResponse');
|
|
120
|
-
|
|
121
|
-
await characteristic.writeAsync(payload, withoutResponse);
|
|
122
|
-
await wait(delayMs);
|
|
123
|
-
}
|
|
101
|
+
await characteristic.writeAsync(payload, withoutResponse);
|
|
124
102
|
console.log(`Write complete withoutResponse=${withoutResponse}`);
|
|
125
103
|
} finally {
|
|
126
104
|
await peripheral.disconnectAsync();
|
package/dist/effects.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { LanternIcEffectConfig, LanternIcEffectsConfig } from './types.js';
|
|
2
|
-
export declare const EFFECT_SERVICE_SUBTYPE_PREFIX = "lanternic-effect-";
|
|
3
|
-
export interface ResolvedLanternIcEffect {
|
|
4
|
-
name: string;
|
|
5
|
-
code: number;
|
|
6
|
-
speed: number;
|
|
7
|
-
subtype: string;
|
|
8
|
-
}
|
|
9
|
-
export interface ResolvedLanternIcEffectsConfig {
|
|
10
|
-
effects: ResolvedLanternIcEffect[];
|
|
11
|
-
restoreColorOnDisable: boolean;
|
|
12
|
-
}
|
|
13
|
-
export declare const DEFAULT_EFFECT_SPEED = 39;
|
|
14
|
-
export declare const DEFAULT_EFFECTS: LanternIcEffectConfig[];
|
|
15
|
-
export declare const resolveEffectsConfig: (config: LanternIcEffectsConfig | undefined) => ResolvedLanternIcEffectsConfig;
|
package/dist/effects.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { clampByte, clampPercent } from './color.js';
|
|
2
|
-
export const EFFECT_SERVICE_SUBTYPE_PREFIX = 'lanternic-effect-';
|
|
3
|
-
export const DEFAULT_EFFECT_SPEED = 39;
|
|
4
|
-
export const DEFAULT_EFFECTS = [
|
|
5
|
-
{ name: 'AutoPlay', code: 0 },
|
|
6
|
-
{ name: 'Magic Back', code: 1 },
|
|
7
|
-
{ name: 'Yellow Marquee', code: 0xcf },
|
|
8
|
-
];
|
|
9
|
-
const slugify = (value) => {
|
|
10
|
-
return value
|
|
11
|
-
.trim()
|
|
12
|
-
.toLowerCase()
|
|
13
|
-
.replace(/[^a-z0-9_-]+/g, '-')
|
|
14
|
-
.replace(/^-+|-+$/g, '')
|
|
15
|
-
.slice(0, 48);
|
|
16
|
-
};
|
|
17
|
-
export const resolveEffectsConfig = (config) => {
|
|
18
|
-
if (config?.enabled !== true) {
|
|
19
|
-
return {
|
|
20
|
-
effects: [],
|
|
21
|
-
restoreColorOnDisable: config?.restoreColorOnDisable ?? true,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
const defaultSpeed = clampPercent(config.defaultSpeed ?? DEFAULT_EFFECT_SPEED);
|
|
25
|
-
const usedSubtypes = new Set();
|
|
26
|
-
const effects = (config.items?.length ? config.items : DEFAULT_EFFECTS)
|
|
27
|
-
.filter(effect => typeof effect.name === 'string' && effect.name.trim().length > 0)
|
|
28
|
-
.map((effect) => {
|
|
29
|
-
const code = clampByte(effect.code);
|
|
30
|
-
const baseId = slugify(effect.id ?? `${effect.name}-${code}`) || `effect-${code}`;
|
|
31
|
-
let subtype = `${EFFECT_SERVICE_SUBTYPE_PREFIX}${baseId}`;
|
|
32
|
-
let suffix = 2;
|
|
33
|
-
while (usedSubtypes.has(subtype)) {
|
|
34
|
-
subtype = `${EFFECT_SERVICE_SUBTYPE_PREFIX}${baseId}-${suffix}`;
|
|
35
|
-
suffix += 1;
|
|
36
|
-
}
|
|
37
|
-
usedSubtypes.add(subtype);
|
|
38
|
-
return {
|
|
39
|
-
name: effect.name,
|
|
40
|
-
code,
|
|
41
|
-
speed: clampPercent(effect.speed ?? defaultSpeed),
|
|
42
|
-
subtype,
|
|
43
|
-
};
|
|
44
|
-
});
|
|
45
|
-
return {
|
|
46
|
-
effects,
|
|
47
|
-
restoreColorOnDisable: config.restoreColorOnDisable ?? true,
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
//# sourceMappingURL=effects.js.map
|
package/dist/effects.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"effects.js","sourceRoot":"","sources":["../src/effects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGrD,MAAM,CAAC,MAAM,6BAA6B,GAAG,mBAAmB,CAAC;AAcjE,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEvC,MAAM,CAAC,MAAM,eAAe,GAA4B;IACtD,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,EAAE;IAC7B,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,EAAE;IAC/B,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,EAAE;CACvC,CAAC;AAEF,MAAM,OAAO,GAAG,CAAC,KAAa,EAAU,EAAE;IACxC,OAAO,KAAK;SACT,IAAI,EAAE;SACN,WAAW,EAAE;SACb,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC;SAC7B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,MAA0C,EACV,EAAE;IAClC,IAAI,MAAM,EAAE,OAAO,KAAK,IAAI,EAAE,CAAC;QAC7B,OAAO;YACL,OAAO,EAAE,EAAE;YACX,qBAAqB,EAAE,MAAM,EAAE,qBAAqB,IAAI,IAAI;SAC7D,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC,YAAY,IAAI,oBAAoB,CAAC,CAAC;IAC/E,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IAEvC,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC;SACpE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;SAClF,GAAG,CAAC,CAAC,MAAM,EAA2B,EAAE;QACvC,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC,IAAI,UAAU,IAAI,EAAE,CAAC;QAClF,IAAI,OAAO,GAAG,GAAG,6BAA6B,GAAG,MAAM,EAAE,CAAC;QAC1D,IAAI,MAAM,GAAG,CAAC,CAAC;QAEf,OAAO,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACjC,OAAO,GAAG,GAAG,6BAA6B,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC;YAChE,MAAM,IAAI,CAAC,CAAC;QACd,CAAC;QAED,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAE1B,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI;YACJ,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,IAAI,YAAY,CAAC;YACjD,OAAO;SACR,CAAC;IACJ,CAAC,CAAC,CAAC;IAEL,OAAO;QACL,OAAO;QACP,qBAAqB,EAAE,MAAM,CAAC,qBAAqB,IAAI,IAAI;KAC5D,CAAC;AACJ,CAAC,CAAC"}
|