homebridge-lanternic 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,16 +1,21 @@
1
1
  # Homebridge LanternIC
2
2
 
3
- Homebridge plugin for BLE RGBIC/RGBWIC light strips controlled by the Magic Lantern app.
3
+ [![npm version](https://img.shields.io/npm/v/homebridge-lanternic.svg)](https://www.npmjs.com/package/homebridge-lanternic)
4
+ [![CI](https://github.com/Gibsonmb71/homebridge-lanternic/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Gibsonmb71/homebridge-lanternic/actions/workflows/ci.yml)
5
+ [![Node.js](https://img.shields.io/node/v/homebridge-lanternic.svg)](https://www.npmjs.com/package/homebridge-lanternic)
6
+ [![Homebridge](https://img.shields.io/badge/homebridge-1.8%20%7C%202.x-blue.svg)](https://homebridge.io/)
7
+ [![License](https://img.shields.io/npm/l/homebridge-lanternic.svg)](LICENSE)
4
8
 
5
- The goal is a reliable local-only bridge for the common unbranded Magic Lantern strips and lamps. These devices are usually BLE lights, not Magic Home Wi-Fi devices, so this plugin talks directly to the BLE service used by the Magic Lantern app.
9
+ Homebridge plugin for BLE RGBIC/RGBWIC light strips controlled by the Magic Lantern app.
6
10
 
7
11
  ## Status
8
12
 
9
- LanternIC is a hardware-tested pre-release plugin. It is designed for production-style Homebridge use, with the important caveat that these unbranded Magic Lantern BLE strips ship with multiple firmware variants.
13
+ These unbranded Magic Lantern BLE strips ship with multiple firmware variants so it's hard to ensure compatibility for all strips.
10
14
 
11
15
  - HomeKit Lightbulb service
12
16
  - On/off with RGB-black fallback plus optional native Magic Lantern power frames
13
17
  - Brightness via RGB scaling by default, with optional native Magic Lantern brightness frames
18
+ - Optional HomeKit switches for Magic Lantern effects
14
19
  - HomeKit on/off, brightness, and color control confirmed on a `MELK-OC21WCT31` strip
15
20
  - Homebridge UI config schema
16
21
  - BLE discovery with log snippets and optional auto-add
@@ -18,11 +23,11 @@ LanternIC is a hardware-tested pre-release plugin. It is designed for production
18
23
  - Command-builder tests for the known Magic Lantern frames
19
24
  - Package smoke tests against Homebridge 1 and 2 on Node.js 22 and 24 in CI
20
25
 
21
- Effects, segment control, RGBW white-channel behavior, and startup scenes are deliberately left out until more real devices are tested.
26
+ Segment-level IC control is not supported yet.
22
27
 
23
28
  ## Install
24
29
 
25
- Once published to npm, install it like any other Homebridge plugin:
30
+ Install it like any other Homebridge plugin:
26
31
 
27
32
  ```sh
28
33
  sudo npm install -g homebridge-lanternic
@@ -35,6 +40,14 @@ npm install
35
40
  npm run build
36
41
  sudo hb-service link
37
42
  ```
43
+ > [!CAUTION]
44
+ >
45
+ > Only the following platforms are supported:
46
+ >
47
+ > - **macOS**
48
+ > - If using macOS, you'll need to give Node.js permission to use Bluetooth in **System Settings → Privacy & Security → Bluetooth**.
49
+ > - **Linux**
50
+ > - 64-bit, not tested on 32-bit systems
38
51
 
39
52
  ## Homebridge UI Setup
40
53
 
@@ -43,9 +56,11 @@ LanternIC supports Homebridge UI through `config.schema.json`.
43
56
  1. Install the plugin.
44
57
  2. Open Homebridge UI.
45
58
  3. Go to Plugins, select LanternIC, then Settings.
46
- 4. Enable `Log Discovered Candidate Devices`.
47
- 5. Restart Homebridge and open the logs.
48
- 6. Copy the `LanternIC device config: {...}` line for your strip into the `Light Strips` list.
59
+ 4. Choose `Auto Mode` for first setup, or `Manual Mode` if you already know your strip's Bluetooth address.
60
+ 5. Save and restart Homebridge.
61
+ 6. In Auto Mode, LanternIC scans and auto-adds matching first-run candidates. In Manual Mode, paste the address into the `Light Strips` list.
62
+
63
+ After at least one light strip is configured, the setup mode picker is hidden and the advanced Discovery/Bluetooth settings become available.
49
64
 
50
65
  You can also use the local scanner:
51
66
 
@@ -63,7 +78,7 @@ LANTERNIC_SCAN_ALL=1 lanternic-scan
63
78
 
64
79
  When working from this repository instead of a global install, use `npm run scan` in place of `lanternic-scan`.
65
80
 
66
- `Auto-Add Discovered Devices` can create HomeKit accessories automatically for matching BLE candidates. Leave it off until the discovery filters only match your strips, because nearby BLE devices may advertise similar services.
81
+ First-run `Auto Mode` can create HomeKit accessories automatically for matching BLE candidates. After setup, use the advanced `Auto-Add Discovered Devices` setting only when your filters are tight, because nearby BLE devices may advertise similar services.
67
82
 
68
83
  ## CLI Tools
69
84
 
@@ -74,6 +89,7 @@ lanternic-scan
74
89
  lanternic-explore <address>
75
90
  lanternic-send <address> rgb ff0000
76
91
  lanternic-send <address> brightness 50
92
+ lanternic-send <address> effect 207 39
77
93
  lanternic-send-sequence <address> 7e070503ff000010ef 7e07050300ff0010ef
78
94
  lanternic-calibrate <address>
79
95
  ```
@@ -133,6 +149,7 @@ Start with discovery enabled and no devices:
133
149
  {
134
150
  "platform": "LanternIC",
135
151
  "name": "LanternIC",
152
+ "setupMode": "auto",
136
153
  "devices": [],
137
154
  "discovery": {
138
155
  "enabled": true,
@@ -190,6 +207,31 @@ Restart Homebridge and look for candidate device addresses in the logs. Then add
190
207
  6. If colors are swapped, change `colorOrder`.
191
208
  7. If Off leaves the strip visibly on, keep `powerMode` as `both` or try `rgbBlack`.
192
209
  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.
193
235
 
194
236
  ## Protocol Notes
195
237
 
@@ -213,7 +255,7 @@ The core frames available here are:
213
255
  - Effect speed: `7e0402xxffffff00ef`, where `xx` is 0-100 decimal encoded as one byte
214
256
  - Basic effect: `7e0503xx06ffff00ef`
215
257
 
216
- These protocol notes come from Home Assistant community reverse-engineering reports and BLE packet examples:
258
+ > These Magic Lantern BLE frame notes are based on packet-captures from [@kassabov](https://github.com/kassabov) in Home Assistant Core issue [#145934](https://github.com/home-assistant/core/issues/145934). (thanks!)
217
259
 
218
260
  - Home Assistant Core issue: <https://github.com/home-assistant/core/issues/145934>
219
261
  - Home Assistant community thread: <https://community.home-assistant.io/t/new-integration-for-ble-magic-lantern/454055>
@@ -233,8 +275,6 @@ The command builders are isolated so we can add variants if your strip uses a sl
233
275
 
234
276
  ## Reliability Choices
235
277
 
236
- This plugin avoids the usual BLE plugin failure pattern of overlapping scans, overlapping writes, and stale connections:
237
-
238
278
  - All BLE operations are serialized through a single manager queue.
239
279
  - Every characteristic write has a timeout and is retried with exponential backoff.
240
280
  - Failed writes force a disconnect and rediscovery.
@@ -15,12 +15,38 @@
15
15
  "type": "string",
16
16
  "default": "LanternIC"
17
17
  },
18
+ "setupMode": {
19
+ "title": "Setup Mode",
20
+ "type": "string",
21
+ "default": "auto",
22
+ "description": "Choose Auto Mode for first setup unless you already know your strip's Bluetooth address.",
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
+ }
40
+ },
18
41
  "devices": {
19
42
  "title": "Light Strips",
20
43
  "type": "array",
21
44
  "buttonText": "Add Light Strip",
22
45
  "default": [],
23
46
  "description": "Use discovery logs or lanternic-scan to find Magic Lantern BLE device ids. On Linux this is usually the MAC address; on macOS it may be a long CoreBluetooth id.",
47
+ "condition": {
48
+ "functionBody": "return (model.devices && model.devices.length > 0) || model.setupMode === 'manual';"
49
+ },
24
50
  "items": {
25
51
  "type": "object",
26
52
  "required": [
@@ -144,6 +170,89 @@
144
170
  ]
145
171
  }
146
172
  ]
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
+ }
147
256
  }
148
257
  }
149
258
  }
@@ -152,6 +261,9 @@
152
261
  "title": "Discovery",
153
262
  "type": "object",
154
263
  "additionalProperties": false,
264
+ "condition": {
265
+ "functionBody": "return model.devices && model.devices.length > 0;"
266
+ },
155
267
  "properties": {
156
268
  "enabled": {
157
269
  "title": "Log Discovered Candidate Devices",
@@ -211,6 +323,9 @@
211
323
  "title": "Bluetooth",
212
324
  "type": "object",
213
325
  "additionalProperties": false,
326
+ "condition": {
327
+ "functionBody": "return model.devices && model.devices.length > 0;"
328
+ },
214
329
  "properties": {
215
330
  "binding": {
216
331
  "title": "Noble Binding",
@@ -14,3 +14,4 @@ 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,4 +46,10 @@ 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
+ };
49
55
  //# 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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"}
@@ -0,0 +1,15 @@
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;
@@ -0,0 +1,50 @@
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
@@ -0,0 +1 @@
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"}
@@ -18,6 +18,7 @@ export declare class LanternIcPlatform implements DynamicPlatformPlugin {
18
18
  private deviceFromCandidate;
19
19
  private isConfigured;
20
20
  private discoveryAutoAdd;
21
+ private firstSetupAutoMode;
21
22
  private validDevices;
22
23
  private deviceUniqueId;
23
24
  }
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() && accessory.context.autoDiscovered) {
42
+ if (this.discoveryAutoAdd(devices) && 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() || this.isConfigured(candidate, configuredDevices)) {
100
+ if (!this.discoveryAutoAdd(configuredDevices) || this.isConfigured(candidate, configuredDevices)) {
101
101
  continue;
102
102
  }
103
103
  const device = this.deviceFromCandidate(candidate);
@@ -130,8 +130,12 @@ 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;
133
+ discoveryAutoAdd(configuredDevices) {
134
+ return this.config.discovery?.autoAdd === true
135
+ || (this.firstSetupAutoMode() && configuredDevices.length === 0);
136
+ }
137
+ firstSetupAutoMode() {
138
+ return this.config.setupMode !== 'manual';
135
139
  }
136
140
  validDevices() {
137
141
  const devices = this.config.devices ?? [];
@@ -1 +1 @@
1
- {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
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,CAAC,OAAO,CAAC,IAAI,SAAS,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;gBACvE,MAAM,MAAM,GAAG,kBAAkB,CAAC,CAAC,CAAC,mCAAmC,CAAC,CAAC,CAAC,uBAAuB,CAAC;gBAClG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,oDAAoD,MAAM,GAAG,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;gBACpG,SAAS;YACX,CAAC;YAED,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,mDAAmD,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;YAC1F,IAAI,CAAC,GAAG,CAAC,6BAA6B,CAAC,WAAW,EAAE,aAAa,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QAClF,CAAC;IACH,CAAC;IAEO,uBAAuB,CAAC,MAA6B,EAAE,cAAuB;QACpF,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;QACrE,MAAM,iBAAiB,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAErD,IAAI,iBAAiB,EAAE,CAAC;YACtB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,0CAA0C,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAC;YACzF,iBAAiB,CAAC,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;YAC1C,iBAAiB,CAAC,OAAO,CAAC,cAAc,GAAG,cAAc,CAAC;YAC1D,IAAI,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC;YACxD,IAAI,0BAA0B,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;QAC1D,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE,MAAM,CAAC,IAAI,EAAE,sBAAsB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5F,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAiD,CAAC;YACpH,SAAS,CAAC,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;YAClC,SAAS,CAAC,OAAO,CAAC,cAAc,GAAG,cAAc,CAAC;YAClD,IAAI,0BAA0B,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAChD,IAAI,CAAC,GAAG,CAAC,2BAA2B,CAAC,WAAW,EAAE,aAAa,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YAC9E,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACxC,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,KAAK,CAAC,wBAAwB,CACpC,iBAA0C,EAC1C,WAAwB;QAExB,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,KAAK,KAAK,EAAE,CAAC;YAC7C,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,IAAI,EAAE,CAAC;QAC7D,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,IAAI;YAC1D,SAAS;YACT,MAAM;YACN,YAAY;YACZ,KAAK;YACL,IAAI;SACL,CAAC;QACF,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,IAAI;YAC1D,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,WAAW,IAAI,MAAM;SACvC,CAAC;QAEF,IAAI,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,WAAW,uCAAuC,CAAC,CAAC;YAC9E,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,WAAW,GAAG,IAAI,EAAE;gBACvE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO;gBACvC,YAAY;gBACZ,YAAY;aACb,CAAC,CAAC;YAEH,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC5B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;gBAC9E,OAAO,IAAI,CAAC;YACd,CAAC;YAED,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;gBACnC,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;gBAE7B,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,CAAC;oBACjG,SAAS;gBACX,CAAC;gBAED,MAAM,MAAM,GAAG,IAAI,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC;gBACnD,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;YAC9D,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,GAAG,CAAC,IAAI,CACX,4BAA4B,EAC5B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAEO,YAAY,CAAC,SAA0B;QAC7C,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7F,MAAM,IAAI,GAAG,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjF,MAAM,QAAQ,GAAG,SAAS,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1G,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,6BAA6B,SAAS,CAAC,IAAI,IAAI,WAAW,YAAY,OAAO,GAAG,IAAI,GAAG,QAAQ,EAAE,CAAC,CAAC;QACjH,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,4BAA4B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC;IACnG,CAAC;IAEO,mBAAmB,CAAC,SAA0B;QACpD,OAAO;YACL,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,aAAa,SAAS,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE;YAC7D,OAAO,EAAE,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC,EAAE;YAC1C,YAAY,EAAE,eAAe;YAC7B,KAAK,EAAE,qBAAqB;YAC5B,UAAU,EAAE,KAAK;SAClB,CAAC;IACJ,CAAC;IAEO,YAAY,CAAC,SAA0B,EAAE,OAAgC;QAC/E,MAAM,WAAW,GAAG,oBAAoB,CAAC,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC,EAAE,CAAC,CAAC;QAE5E,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC,CAAC;IACtF,CAAC;IAEO,gBAAgB,CAAC,iBAA0C;QACjE,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI;eACzC,CAAC,IAAI,CAAC,kBAAkB,EAAE,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC;IACrE,CAAC;IAEO,kBAAkB;QACxB,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,KAAK,QAAQ,CAAC;IAC5C,CAAC;IAEO,YAAY;QAClB,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;QAE1C,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAmC,EAAE;YAChE,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACrC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,wDAAwD,CAAC,CAAC;gBACxE,OAAO,KAAK,CAAC;YACf,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,cAAc,CAAC,MAA6B;QAClD,OAAO,aAAa,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;IAC7D,CAAC;CACF"}
@@ -7,6 +7,8 @@ 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;
10
12
  private readonly state;
11
13
  private colorTimer?;
12
14
  private colorWriteWaiters;
@@ -19,8 +21,13 @@ export declare class LanternIcPlatformAccessory {
19
21
  private buildCurrentColorCommand;
20
22
  private buildPowerOnCommands;
21
23
  private buildBrightnessUpdateCommands;
24
+ private buildEffectCommands;
22
25
  private powerMode;
23
26
  private brightnessMode;
27
+ private configureEffectServices;
28
+ private setEffectSwitch;
29
+ private updateEffectSwitches;
30
+ private findEffect;
24
31
  private resyncAfterReconnect;
25
32
  private saveState;
26
33
  }
@@ -1,11 +1,14 @@
1
1
  import { applyColorOrder, clampPercent, hsbToRgb } from './color.js';
2
- import { buildColorCommand, buildNativeBrightnessCommands, buildPowerOffCommands, buildPowerOnPrefixCommands, shouldScaleRgbBrightness, } from './ble/magicLanternCommands.js';
2
+ import { buildBasicEffectCommands, buildColorCommand, buildNativeBrightnessCommands, buildPowerOffCommands, buildPowerOnPrefixCommands, shouldScaleRgbBrightness, } from './ble/magicLanternCommands.js';
3
+ import { EFFECT_SERVICE_SUBTYPE_PREFIX, resolveEffectsConfig, } from './effects.js';
3
4
  export class LanternIcPlatformAccessory {
4
5
  platform;
5
6
  device;
6
7
  client;
7
8
  accessory;
8
9
  service;
10
+ effectSettings;
11
+ effectServices = new Map();
9
12
  state;
10
13
  colorTimer;
11
14
  colorWriteWaiters = [];
@@ -22,7 +25,12 @@ export class LanternIcPlatformAccessory {
22
25
  brightness: accessory.context.state?.brightness ?? 100,
23
26
  hue: accessory.context.state?.hue ?? 0,
24
27
  saturation: accessory.context.state?.saturation ?? 0,
28
+ activeEffect: accessory.context.state?.activeEffect,
25
29
  };
30
+ this.effectSettings = resolveEffectsConfig(this.device.effects);
31
+ if (this.state.activeEffect && !this.findEffect(this.state.activeEffect)) {
32
+ this.state.activeEffect = undefined;
33
+ }
26
34
  const informationService = accessory.getService(this.platform.Service.AccessoryInformation)
27
35
  ?? accessory.addService(this.platform.Service.AccessoryInformation);
28
36
  informationService
@@ -44,12 +52,15 @@ export class LanternIcPlatformAccessory {
44
52
  this.service.getCharacteristic(this.platform.Characteristic.Saturation)
45
53
  .onGet(() => this.state.saturation)
46
54
  .onSet(value => this.setSaturation(Number(value)));
55
+ this.configureEffectServices();
47
56
  this.client.onReconnect(() => this.resyncAfterReconnect());
48
57
  this.client.start();
49
58
  }
50
59
  async setOn(onValue) {
51
60
  this.state.on = onValue;
61
+ this.state.activeEffect = undefined;
52
62
  this.saveState();
63
+ this.updateEffectSwitches();
53
64
  if (!onValue) {
54
65
  await this.client.writeCommands(buildPowerOffCommands(this.powerMode()));
55
66
  return;
@@ -58,7 +69,9 @@ export class LanternIcPlatformAccessory {
58
69
  }
59
70
  async setBrightness(value) {
60
71
  this.state.brightness = clampPercent(Number(value));
72
+ this.state.activeEffect = undefined;
61
73
  this.saveState();
74
+ this.updateEffectSwitches();
62
75
  if (!this.state.on) {
63
76
  return;
64
77
  }
@@ -66,12 +79,16 @@ export class LanternIcPlatformAccessory {
66
79
  }
67
80
  async setHue(value) {
68
81
  this.state.hue = Number(value);
82
+ this.state.activeEffect = undefined;
69
83
  this.saveState();
84
+ this.updateEffectSwitches();
70
85
  await this.scheduleColorWrite();
71
86
  }
72
87
  async setSaturation(value) {
73
88
  this.state.saturation = clampPercent(Number(value));
89
+ this.state.activeEffect = undefined;
74
90
  this.saveState();
91
+ this.updateEffectSwitches();
75
92
  await this.scheduleColorWrite();
76
93
  }
77
94
  async scheduleColorWrite() {
@@ -118,17 +135,77 @@ export class LanternIcPlatformAccessory {
118
135
  ...(shouldScaleRgbBrightness(this.brightnessMode()) ? [this.buildCurrentColorCommand()] : []),
119
136
  ];
120
137
  }
138
+ buildEffectCommands(effect) {
139
+ return [
140
+ ...buildPowerOnPrefixCommands(this.powerMode(), this.brightnessMode(), this.state.brightness),
141
+ ...buildBasicEffectCommands(effect.code, effect.speed),
142
+ ];
143
+ }
121
144
  powerMode() {
122
145
  return this.device.powerMode ?? 'both';
123
146
  }
124
147
  brightnessMode() {
125
148
  return this.device.brightnessMode ?? 'rgb';
126
149
  }
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
+ }
127
199
  async resyncAfterReconnect() {
128
200
  if (!this.state.on) {
129
201
  return;
130
202
  }
131
203
  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
+ }
132
209
  await this.client.writeCommands(this.buildPowerOnCommands());
133
210
  }
134
211
  saveState() {
@@ -1 +1 @@
1
- {"version":3,"file":"platformAccessory.js","sourceRoot":"","sources":["../src/platformAccessory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"platformAccessory.js","sourceRoot":"","sources":["../src/platformAccessory.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -5,6 +5,19 @@ 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
+ }
8
21
  export interface LanternIcDeviceConfig {
9
22
  name: string;
10
23
  address: string;
@@ -13,6 +26,7 @@ export interface LanternIcDeviceConfig {
13
26
  colorOrder?: ColorOrder;
14
27
  powerMode?: PowerMode;
15
28
  brightnessMode?: BrightnessMode;
29
+ effects?: LanternIcEffectsConfig;
16
30
  }
17
31
  export interface LanternIcDiscoveryConfig {
18
32
  enabled?: boolean;
@@ -45,6 +59,7 @@ export interface LanternIcBleConfig {
45
59
  }
46
60
  export interface LanternIcPlatformConfig extends PlatformConfig {
47
61
  name?: string;
62
+ setupMode?: SetupMode;
48
63
  devices?: LanternIcDeviceConfig[];
49
64
  discovery?: LanternIcDiscoveryConfig;
50
65
  ble?: LanternIcBleConfig;
@@ -54,6 +69,7 @@ export interface LanternIcStoredState {
54
69
  brightness?: number;
55
70
  hue?: number;
56
71
  saturation?: number;
72
+ activeEffect?: string;
57
73
  }
58
74
  export interface LanternIcAccessoryContext {
59
75
  autoDiscovered?: boolean;
package/package.json CHANGED
@@ -2,17 +2,17 @@
2
2
  "name": "homebridge-lanternic",
3
3
  "displayName": "LanternIC",
4
4
  "type": "module",
5
- "version": "0.1.2",
5
+ "version": "0.2.0",
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",
9
- "homepage": "https://github.com/gibsonmb71/homebridge-lanternic#readme",
9
+ "homepage": "https://github.com/Gibsonmb71/homebridge-lanternic#readme",
10
10
  "repository": {
11
11
  "type": "git",
12
- "url": "git+https://github.com/gibsonmb71/homebridge-lanternic.git"
12
+ "url": "git+https://github.com/Gibsonmb71/homebridge-lanternic.git"
13
13
  },
14
14
  "bugs": {
15
- "url": "https://github.com/gibsonmb71/homebridge-lanternic/issues"
15
+ "url": "https://github.com/Gibsonmb71/homebridge-lanternic/issues"
16
16
  },
17
17
  "keywords": [
18
18
  "homebridge-plugin",
package/tools/send.mjs CHANGED
@@ -4,16 +4,20 @@ 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);
7
8
  const address = process.argv[2];
8
9
  const command = process.argv[3];
9
10
  const value = process.argv[4];
11
+ const extraValue = process.argv[5];
10
12
 
11
13
  if (!address || !command) {
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]');
14
+ console.error('Usage: lanternic-send <address> <on|off|brightness|speed|effect|rgb|raw> [value] [speed]');
15
+ console.error('Repo development: npm run send -- <address> <on|off|brightness|speed|effect|rgb|raw> [value] [speed]');
14
16
  console.error('Examples:');
15
17
  console.error(' lanternic-send be:16:70:00:08:2a on');
16
18
  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');
17
21
  console.error(' lanternic-send be:16:70:00:08:2a rgb ff0000');
18
22
  console.error(' lanternic-send be:16:70:00:08:2a raw 7e0404f00001ff00ef');
19
23
  process.exit(2);
@@ -27,18 +31,33 @@ const noble = withBindings(binding);
27
31
  const byte = number => Math.max(0, Math.min(255, Math.round(number)));
28
32
  const percent = number => Math.max(0, Math.min(100, Math.round(number)));
29
33
  const frame = (...bytes) => Buffer.from(bytes.map(byte));
34
+ const wait = milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds));
30
35
 
31
- const buildPayload = () => {
36
+ const buildPayloads = () => {
32
37
  if (command === 'on') {
33
- return Buffer.from('7e0404f00001ff00ef', 'hex');
38
+ return [Buffer.from('7e0404f00001ff00ef', 'hex')];
34
39
  }
35
40
 
36
41
  if (command === 'off') {
37
- return Buffer.from('7e0404000000ff00ef', 'hex');
42
+ return [Buffer.from('7e0404000000ff00ef', 'hex')];
38
43
  }
39
44
 
40
45
  if (command === 'brightness') {
41
- return frame(0x7e, 0x04, 0x01, percent(Number(value)), 0x01, 0xff, 0xff, 0x00, 0xef);
46
+ return [frame(0x7e, 0x04, 0x01, percent(Number(value)), 0x01, 0xff, 0xff, 0x00, 0xef)];
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;
42
61
  }
43
62
 
44
63
  if (command === 'rgb') {
@@ -46,18 +65,18 @@ const buildPayload = () => {
46
65
  if (rgb.length !== 6) {
47
66
  throw new Error('rgb value must be RRGGBB hex, for example ff0000');
48
67
  }
49
- return Buffer.from(`7e070503${rgb}10ef`, 'hex');
68
+ return [Buffer.from(`7e070503${rgb}10ef`, 'hex')];
50
69
  }
51
70
 
52
71
  if (command === 'raw') {
53
- return Buffer.from(cleanId(value), 'hex');
72
+ return [Buffer.from(cleanId(value), 'hex')];
54
73
  }
55
74
 
56
75
  throw new Error(`Unknown command: ${command}`);
57
76
  };
58
77
 
59
- const payload = buildPayload();
60
- console.log(`Sending ${payload.toString('hex')} to ${address} with binding=${binding}`);
78
+ const payloads = buildPayloads();
79
+ console.log(`Sending ${payloads.map(payload => payload.toString('hex')).join(', ')} to ${address} with binding=${binding}`);
61
80
 
62
81
  await noble.waitForPoweredOnAsync(15_000);
63
82
  await noble.startScanningAsync([], true);
@@ -98,7 +117,10 @@ try {
98
117
 
99
118
  const withoutResponse = !characteristic.properties.includes('write')
100
119
  && characteristic.properties.includes('writeWithoutResponse');
101
- await characteristic.writeAsync(payload, withoutResponse);
120
+ for (const payload of payloads) {
121
+ await characteristic.writeAsync(payload, withoutResponse);
122
+ await wait(delayMs);
123
+ }
102
124
  console.log(`Write complete withoutResponse=${withoutResponse}`);
103
125
  } finally {
104
126
  await peripheral.disconnectAsync();