homebridge-lanternic 0.1.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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +260 -0
  3. package/config.schema.json +412 -0
  4. package/dist/ble/magicLanternBleManager.d.ts +78 -0
  5. package/dist/ble/magicLanternBleManager.js +325 -0
  6. package/dist/ble/magicLanternBleManager.js.map +1 -0
  7. package/dist/ble/magicLanternCommands.d.ts +16 -0
  8. package/dist/ble/magicLanternCommands.js +49 -0
  9. package/dist/ble/magicLanternCommands.js.map +1 -0
  10. package/dist/ble/nobleTypes.d.ts +33 -0
  11. package/dist/ble/nobleTypes.js +2 -0
  12. package/dist/ble/nobleTypes.js.map +1 -0
  13. package/dist/color.d.ts +10 -0
  14. package/dist/color.js +65 -0
  15. package/dist/color.js.map +1 -0
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.js +6 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/platform.d.ts +23 -0
  20. package/dist/platform.js +150 -0
  21. package/dist/platform.js.map +1 -0
  22. package/dist/platformAccessory.d.ts +26 -0
  23. package/dist/platformAccessory.js +139 -0
  24. package/dist/platformAccessory.js.map +1 -0
  25. package/dist/settings.d.ts +2 -0
  26. package/dist/settings.js +3 -0
  27. package/dist/settings.js.map +1 -0
  28. package/dist/types.d.ts +62 -0
  29. package/dist/types.js +2 -0
  30. package/dist/types.js.map +1 -0
  31. package/dist/util/async.d.ts +2 -0
  32. package/dist/util/async.js +24 -0
  33. package/dist/util/async.js.map +1 -0
  34. package/dist/util/bluetooth.d.ts +3 -0
  35. package/dist/util/bluetooth.js +15 -0
  36. package/dist/util/bluetooth.js.map +1 -0
  37. package/package.json +81 -0
  38. package/tools/calibrate.mjs +314 -0
  39. package/tools/calibrator/app.js +399 -0
  40. package/tools/calibrator/index.html +91 -0
  41. package/tools/calibrator/styles.css +302 -0
  42. package/tools/explore.mjs +73 -0
  43. package/tools/scan.mjs +88 -0
  44. package/tools/send-sequence.mjs +76 -0
  45. package/tools/send.mjs +106 -0
@@ -0,0 +1,78 @@
1
+ import type { Logging } from 'homebridge';
2
+ import type { HciDriver, LanternIcBleConfig, LanternIcDeviceConfig, NobleBinding, WriteMode } from '../types.js';
3
+ import type { NoblePeripheral } from './nobleTypes.js';
4
+ export interface MagicLanternBleOptions {
5
+ binding: NobleBinding;
6
+ hciDriver: HciDriver;
7
+ hciDeviceId: number;
8
+ hciUserChannel: boolean;
9
+ hciExtended?: boolean;
10
+ serviceUuid: string;
11
+ characteristicUuid: string;
12
+ keepConnected: boolean;
13
+ connectTimeoutMs: number;
14
+ scanTimeoutMs: number;
15
+ writeTimeoutMs: number;
16
+ retryAttempts: number;
17
+ retryDelayMs: number;
18
+ maxRetryDelayMs: number;
19
+ reconnectDelayMs: number;
20
+ maxReconnectDelayMs: number;
21
+ writeDelayMs: number;
22
+ idleDisconnectMs: number;
23
+ writeMode: WriteMode;
24
+ }
25
+ export interface CandidateDevice {
26
+ id: string;
27
+ address?: string;
28
+ connectable?: boolean;
29
+ manufacturerData?: string;
30
+ name: string;
31
+ rssi?: number;
32
+ serviceUuids: string[];
33
+ }
34
+ export interface MagicLanternDiscoveryOptions {
35
+ namePrefixes: string[];
36
+ serviceUuids: string[];
37
+ minRssi?: number;
38
+ }
39
+ export declare class MagicLanternBleManager {
40
+ private readonly log;
41
+ readonly options: MagicLanternBleOptions;
42
+ private readonly noble;
43
+ private queue;
44
+ constructor(log: Logging, config: LanternIcBleConfig | undefined);
45
+ createClient(device: LanternIcDeviceConfig): MagicLanternBleClient;
46
+ discoverCandidates(timeoutMs: number, options: MagicLanternDiscoveryOptions): Promise<CandidateDevice[]>;
47
+ private bindingOptions;
48
+ runExclusive<T>(operation: () => Promise<T>): Promise<T>;
49
+ findPeripheral(device: LanternIcDeviceConfig): Promise<NoblePeripheral>;
50
+ private matchesDevice;
51
+ private peripheralId;
52
+ }
53
+ export declare class MagicLanternBleClient {
54
+ private readonly log;
55
+ private readonly manager;
56
+ private readonly device;
57
+ private peripheral?;
58
+ private characteristic?;
59
+ private idleTimer?;
60
+ private reconnectTimer?;
61
+ private readonly reconnectHandlers;
62
+ private reconnectDelayMs;
63
+ private expectedDisconnect;
64
+ private disconnectListener?;
65
+ constructor(log: Logging, manager: MagicLanternBleManager, device: LanternIcDeviceConfig);
66
+ onReconnect(handler: () => Promise<void> | void): () => void;
67
+ start(): void;
68
+ writeCommands(commands: Buffer[]): Promise<void>;
69
+ disconnect(reconnect?: boolean): Promise<void>;
70
+ private ensureConnected;
71
+ private write;
72
+ private shouldWriteWithoutResponse;
73
+ private scheduleIdleDisconnect;
74
+ private retryDelay;
75
+ private scheduleReconnect;
76
+ private clearReconnectTimer;
77
+ private emitReconnect;
78
+ }
@@ -0,0 +1,325 @@
1
+ import { withBindings } from '@stoprocent/noble';
2
+ import { delay, withTimeout } from '../util/async.js';
3
+ import { formatBluetoothAddress, normalizeBluetoothId, normalizeUuid } from '../util/bluetooth.js';
4
+ import { MAGIC_LANTERN_DEFAULT_CHARACTERISTIC_UUID, MAGIC_LANTERN_DEFAULT_SERVICE_UUID, } from './magicLanternCommands.js';
5
+ export class MagicLanternBleManager {
6
+ log;
7
+ options;
8
+ noble;
9
+ queue = Promise.resolve();
10
+ constructor(log, config) {
11
+ this.log = log;
12
+ this.options = {
13
+ binding: config?.binding ?? 'default',
14
+ hciDriver: config?.hciDriver ?? 'native',
15
+ hciDeviceId: config?.hciDeviceId ?? 0,
16
+ hciUserChannel: config?.hciUserChannel ?? false,
17
+ hciExtended: config?.hciExtended,
18
+ serviceUuid: normalizeUuid(config?.serviceUuid ?? MAGIC_LANTERN_DEFAULT_SERVICE_UUID),
19
+ characteristicUuid: normalizeUuid(config?.characteristicUuid ?? MAGIC_LANTERN_DEFAULT_CHARACTERISTIC_UUID),
20
+ keepConnected: config?.keepConnected ?? false,
21
+ connectTimeoutMs: config?.connectTimeoutMs ?? 15_000,
22
+ scanTimeoutMs: config?.scanTimeoutMs ?? 15_000,
23
+ writeTimeoutMs: config?.writeTimeoutMs ?? 5_000,
24
+ retryAttempts: config?.retryAttempts ?? 3,
25
+ retryDelayMs: config?.retryDelayMs ?? 500,
26
+ maxRetryDelayMs: config?.maxRetryDelayMs ?? 5_000,
27
+ reconnectDelayMs: config?.reconnectDelayMs ?? 1_000,
28
+ maxReconnectDelayMs: config?.maxReconnectDelayMs ?? 60_000,
29
+ writeDelayMs: config?.writeDelayMs ?? 120,
30
+ idleDisconnectMs: config?.idleDisconnectMs ?? 30_000,
31
+ writeMode: config?.writeMode ?? 'auto',
32
+ };
33
+ this.noble = withBindings(this.options.binding, this.bindingOptions());
34
+ }
35
+ createClient(device) {
36
+ return new MagicLanternBleClient(this.log, this, device);
37
+ }
38
+ async discoverCandidates(timeoutMs, options) {
39
+ return this.runExclusive(async () => {
40
+ await this.noble.waitForPoweredOnAsync(this.options.connectTimeoutMs);
41
+ const candidates = new Map();
42
+ const normalizedPrefixes = options.namePrefixes.map(prefix => prefix.toLowerCase());
43
+ const normalizedServiceUuids = options.serviceUuids.map(normalizeUuid);
44
+ await this.noble.startScanningAsync([], true);
45
+ try {
46
+ await new Promise(resolve => {
47
+ const onDiscover = (peripheral) => {
48
+ const name = peripheral.advertisement?.localName ?? '';
49
+ const serviceUuids = peripheral.advertisement?.serviceUuids?.map(normalizeUuid) ?? [];
50
+ const matchesPrefix = Boolean(name)
51
+ && normalizedPrefixes.some(prefix => name.toLowerCase().startsWith(prefix));
52
+ const matchesService = normalizedServiceUuids.length > 0
53
+ && serviceUuids.some(serviceUuid => normalizedServiceUuids.includes(serviceUuid));
54
+ const matchesRssi = typeof options.minRssi !== 'number'
55
+ || typeof peripheral.rssi !== 'number'
56
+ || peripheral.rssi >= options.minRssi;
57
+ if ((!matchesPrefix && !matchesService) || !matchesRssi) {
58
+ return;
59
+ }
60
+ const id = this.peripheralId(peripheral);
61
+ candidates.set(normalizeBluetoothId(id), {
62
+ id,
63
+ address: peripheral.address || undefined,
64
+ connectable: peripheral.connectable,
65
+ manufacturerData: peripheral.advertisement?.manufacturerData?.toString('hex'),
66
+ name,
67
+ rssi: peripheral.rssi,
68
+ serviceUuids,
69
+ });
70
+ };
71
+ this.noble.on('discover', onDiscover);
72
+ setTimeout(() => {
73
+ this.noble.removeListener('discover', onDiscover);
74
+ resolve();
75
+ }, timeoutMs);
76
+ });
77
+ }
78
+ finally {
79
+ await this.noble.stopScanningAsync();
80
+ }
81
+ return [...candidates.values()];
82
+ });
83
+ }
84
+ bindingOptions() {
85
+ const shouldUseHciOptions = this.options.binding === 'hci'
86
+ || (this.options.binding === 'default' && process.platform === 'linux');
87
+ if (!shouldUseHciOptions) {
88
+ return undefined;
89
+ }
90
+ return {
91
+ deviceId: this.options.hciDeviceId,
92
+ extended: this.options.hciExtended,
93
+ hciDriver: this.options.hciDriver,
94
+ userChannel: this.options.hciUserChannel,
95
+ };
96
+ }
97
+ async runExclusive(operation) {
98
+ const run = this.queue.then(operation, operation);
99
+ this.queue = run.then(() => undefined, () => undefined);
100
+ return run;
101
+ }
102
+ async findPeripheral(device) {
103
+ await this.noble.waitForPoweredOnAsync(this.options.connectTimeoutMs);
104
+ await this.noble.startScanningAsync([], true);
105
+ let onDiscover;
106
+ try {
107
+ return await withTimeout(new Promise((resolve) => {
108
+ onDiscover = (peripheral) => {
109
+ if (!this.matchesDevice(peripheral, device)) {
110
+ return;
111
+ }
112
+ if (onDiscover) {
113
+ this.noble.removeListener('discover', onDiscover);
114
+ }
115
+ resolve(peripheral);
116
+ };
117
+ this.noble.on('discover', onDiscover);
118
+ }), this.options.scanTimeoutMs, `Timed out scanning for ${device.name} (${formatBluetoothAddress(device.address)})`);
119
+ }
120
+ finally {
121
+ if (onDiscover) {
122
+ this.noble.removeListener('discover', onDiscover);
123
+ }
124
+ await this.noble.stopScanningAsync();
125
+ }
126
+ }
127
+ matchesDevice(peripheral, device) {
128
+ const configuredId = normalizeBluetoothId(device.address);
129
+ const discoveredIds = [
130
+ peripheral.id,
131
+ peripheral.uuid,
132
+ peripheral.address,
133
+ ].filter((value) => Boolean(value)).map(normalizeBluetoothId);
134
+ return discoveredIds.includes(configuredId);
135
+ }
136
+ peripheralId(peripheral) {
137
+ return peripheral.address || peripheral.uuid || peripheral.id;
138
+ }
139
+ }
140
+ export class MagicLanternBleClient {
141
+ log;
142
+ manager;
143
+ device;
144
+ peripheral;
145
+ characteristic;
146
+ idleTimer;
147
+ reconnectTimer;
148
+ reconnectHandlers = new Set();
149
+ reconnectDelayMs;
150
+ expectedDisconnect = false;
151
+ disconnectListener;
152
+ constructor(log, manager, device) {
153
+ this.log = log;
154
+ this.manager = manager;
155
+ this.device = device;
156
+ this.reconnectDelayMs = this.manager.options.reconnectDelayMs;
157
+ }
158
+ onReconnect(handler) {
159
+ this.reconnectHandlers.add(handler);
160
+ return () => {
161
+ this.reconnectHandlers.delete(handler);
162
+ };
163
+ }
164
+ start() {
165
+ if (!this.manager.options.keepConnected) {
166
+ return;
167
+ }
168
+ this.scheduleReconnect(0);
169
+ }
170
+ async writeCommands(commands) {
171
+ this.clearReconnectTimer();
172
+ await this.manager.runExclusive(async () => {
173
+ for (let attempt = 1; attempt <= this.manager.options.retryAttempts; attempt += 1) {
174
+ try {
175
+ await this.ensureConnected();
176
+ for (const command of commands) {
177
+ await this.write(command);
178
+ await delay(this.manager.options.writeDelayMs);
179
+ }
180
+ this.scheduleIdleDisconnect();
181
+ this.reconnectDelayMs = this.manager.options.reconnectDelayMs;
182
+ return;
183
+ }
184
+ catch (error) {
185
+ await this.disconnect(false);
186
+ if (attempt >= this.manager.options.retryAttempts) {
187
+ this.scheduleReconnect();
188
+ throw error;
189
+ }
190
+ const retryDelay = this.retryDelay(attempt);
191
+ this.log.debug(`[${this.device.name}] BLE write failed, retrying in ${retryDelay}ms (${attempt}/${this.manager.options.retryAttempts}):`, error instanceof Error ? error.message : String(error));
192
+ await delay(retryDelay);
193
+ }
194
+ }
195
+ });
196
+ }
197
+ async disconnect(reconnect = false) {
198
+ if (this.idleTimer) {
199
+ clearTimeout(this.idleTimer);
200
+ this.idleTimer = undefined;
201
+ }
202
+ if (!reconnect) {
203
+ this.clearReconnectTimer();
204
+ }
205
+ const peripheral = this.peripheral;
206
+ const listener = this.disconnectListener;
207
+ this.peripheral = undefined;
208
+ this.characteristic = undefined;
209
+ this.disconnectListener = undefined;
210
+ if (peripheral?.state === 'connected') {
211
+ if (listener) {
212
+ peripheral.removeListener('disconnect', listener);
213
+ }
214
+ this.expectedDisconnect = true;
215
+ try {
216
+ await withTimeout(peripheral.disconnectAsync(), this.manager.options.connectTimeoutMs, `Timed out disconnecting from ${this.device.name}`);
217
+ }
218
+ finally {
219
+ this.expectedDisconnect = false;
220
+ }
221
+ }
222
+ else if (listener && peripheral) {
223
+ peripheral.removeListener('disconnect', listener);
224
+ }
225
+ }
226
+ async ensureConnected() {
227
+ if (this.peripheral?.state === 'connected' && this.characteristic) {
228
+ return;
229
+ }
230
+ const peripheral = await this.manager.findPeripheral(this.device);
231
+ this.disconnectListener = () => {
232
+ this.log.debug(`[${this.device.name}] BLE peripheral disconnected`);
233
+ this.peripheral = undefined;
234
+ this.characteristic = undefined;
235
+ this.disconnectListener = undefined;
236
+ if (!this.expectedDisconnect) {
237
+ this.scheduleReconnect();
238
+ }
239
+ };
240
+ peripheral.on('disconnect', this.disconnectListener);
241
+ await withTimeout(peripheral.connectAsync(), this.manager.options.connectTimeoutMs, `Timed out connecting to ${this.device.name}`);
242
+ const { characteristics } = await peripheral.discoverSomeServicesAndCharacteristicsAsync([this.manager.options.serviceUuid], [this.manager.options.characteristicUuid]);
243
+ const characteristic = characteristics[0];
244
+ if (!characteristic) {
245
+ await peripheral.disconnectAsync();
246
+ throw new Error(`Missing Magic Lantern characteristic ${this.manager.options.serviceUuid}/${this.manager.options.characteristicUuid}`);
247
+ }
248
+ this.peripheral = peripheral;
249
+ this.characteristic = characteristic;
250
+ this.reconnectDelayMs = this.manager.options.reconnectDelayMs;
251
+ this.log.debug(`[${this.device.name}] BLE connected`);
252
+ }
253
+ async write(command) {
254
+ if (!this.characteristic) {
255
+ throw new Error(`Cannot write to ${this.device.name}; BLE characteristic is not ready`);
256
+ }
257
+ const withoutResponse = this.shouldWriteWithoutResponse(this.characteristic);
258
+ this.log.debug(`[${this.device.name}] BLE write ${command.toString('hex')} withoutResponse=${withoutResponse}`);
259
+ await withTimeout(this.characteristic.writeAsync(command, withoutResponse), this.manager.options.writeTimeoutMs, `Timed out writing to ${this.device.name}`);
260
+ }
261
+ shouldWriteWithoutResponse(characteristic) {
262
+ if (this.manager.options.writeMode === 'withResponse') {
263
+ return false;
264
+ }
265
+ if (this.manager.options.writeMode === 'withoutResponse') {
266
+ return true;
267
+ }
268
+ return !characteristic.properties.includes('write') && characteristic.properties.includes('writeWithoutResponse');
269
+ }
270
+ scheduleIdleDisconnect() {
271
+ if (this.idleTimer) {
272
+ clearTimeout(this.idleTimer);
273
+ this.idleTimer = undefined;
274
+ }
275
+ if (this.manager.options.keepConnected || this.manager.options.idleDisconnectMs <= 0) {
276
+ return;
277
+ }
278
+ this.idleTimer = setTimeout(() => {
279
+ this.disconnect(false).catch(error => {
280
+ this.log.debug(`[${this.device.name}] Idle disconnect failed:`, error instanceof Error ? error.message : String(error));
281
+ });
282
+ }, this.manager.options.idleDisconnectMs);
283
+ }
284
+ retryDelay(attempt) {
285
+ return Math.min(this.manager.options.maxRetryDelayMs, this.manager.options.retryDelayMs * 2 ** (attempt - 1));
286
+ }
287
+ scheduleReconnect(delayMs = this.reconnectDelayMs) {
288
+ if (!this.manager.options.keepConnected || this.reconnectTimer) {
289
+ return;
290
+ }
291
+ this.reconnectTimer = setTimeout(() => {
292
+ this.reconnectTimer = undefined;
293
+ void this.manager.runExclusive(async () => {
294
+ try {
295
+ await this.ensureConnected();
296
+ this.log.debug(`[${this.device.name}] BLE reconnect complete`);
297
+ this.reconnectDelayMs = this.manager.options.reconnectDelayMs;
298
+ this.emitReconnect();
299
+ }
300
+ catch (error) {
301
+ this.log.debug(`[${this.device.name}] BLE reconnect failed:`, error instanceof Error ? error.message : String(error));
302
+ this.reconnectDelayMs = Math.min(this.manager.options.maxReconnectDelayMs, Math.max(this.manager.options.reconnectDelayMs, this.reconnectDelayMs * 2));
303
+ this.scheduleReconnect();
304
+ }
305
+ });
306
+ }, delayMs);
307
+ }
308
+ clearReconnectTimer() {
309
+ if (!this.reconnectTimer) {
310
+ return;
311
+ }
312
+ clearTimeout(this.reconnectTimer);
313
+ this.reconnectTimer = undefined;
314
+ }
315
+ emitReconnect() {
316
+ for (const handler of this.reconnectHandlers) {
317
+ setTimeout(() => {
318
+ Promise.resolve(handler()).catch(error => {
319
+ this.log.debug(`[${this.device.name}] Reconnect handler failed:`, error instanceof Error ? error.message : String(error));
320
+ });
321
+ }, 0);
322
+ }
323
+ }
324
+ }
325
+ //# sourceMappingURL=magicLanternBleManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"magicLanternBleManager.js","sourceRoot":"","sources":["../../src/ble/magicLanternBleManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { type RgbColor } from '../color.js';
2
+ import type { BrightnessMode, PowerMode } from '../types.js';
3
+ export declare const MAGIC_LANTERN_DEFAULT_SERVICE_UUID = "fff0";
4
+ export declare const MAGIC_LANTERN_DEFAULT_CHARACTERISTIC_UUID = "fff3";
5
+ export declare const buildPowerCommand: (on: boolean) => Buffer;
6
+ export declare const buildColorCommand: ({ red, green, blue }: RgbColor) => Buffer;
7
+ export declare const buildBrightnessCommand: (brightness: number) => Buffer;
8
+ export declare const shouldUseNativePower: (powerMode?: PowerMode) => boolean;
9
+ export declare const shouldUseRgbBlackOff: (powerMode?: PowerMode) => boolean;
10
+ export declare const shouldUseNativeBrightness: (brightnessMode?: BrightnessMode) => boolean;
11
+ export declare const shouldScaleRgbBrightness: (brightnessMode?: BrightnessMode) => boolean;
12
+ export declare const buildNativeBrightnessCommands: (brightness: number, brightnessMode?: BrightnessMode) => Buffer[];
13
+ export declare const buildPowerOnPrefixCommands: (powerMode?: PowerMode, brightnessMode?: BrightnessMode, brightness?: number) => Buffer[];
14
+ export declare const buildPowerOffCommands: (powerMode?: PowerMode) => Buffer[];
15
+ export declare const buildEffectSpeedCommand: (speed: number) => Buffer;
16
+ export declare const buildBasicEffectCommand: (effectCode: number) => Buffer;
@@ -0,0 +1,49 @@
1
+ import { clampByte, clampPercent } from '../color.js';
2
+ export const MAGIC_LANTERN_DEFAULT_SERVICE_UUID = 'fff0';
3
+ export const MAGIC_LANTERN_DEFAULT_CHARACTERISTIC_UUID = 'fff3';
4
+ const frame = (...bytes) => Buffer.from(bytes.map(clampByte));
5
+ export const buildPowerCommand = (on) => {
6
+ return on
7
+ ? Buffer.from('7e0404f00001ff00ef', 'hex')
8
+ : Buffer.from('7e0404000000ff00ef', 'hex');
9
+ };
10
+ export const buildColorCommand = ({ red, green, blue }) => {
11
+ return frame(0x7e, 0x07, 0x05, 0x03, red, green, blue, 0x10, 0xef);
12
+ };
13
+ export const buildBrightnessCommand = (brightness) => {
14
+ return frame(0x7e, 0x04, 0x01, clampPercent(brightness), 0x01, 0xff, 0xff, 0x00, 0xef);
15
+ };
16
+ export const shouldUseNativePower = (powerMode = 'both') => {
17
+ return powerMode === 'native' || powerMode === 'both';
18
+ };
19
+ export const shouldUseRgbBlackOff = (powerMode = 'both') => {
20
+ return powerMode === 'rgbBlack' || powerMode === 'both';
21
+ };
22
+ export const shouldUseNativeBrightness = (brightnessMode = 'rgb') => {
23
+ return brightnessMode === 'native' || brightnessMode === 'both';
24
+ };
25
+ export const shouldScaleRgbBrightness = (brightnessMode = 'rgb') => {
26
+ return brightnessMode === 'rgb' || brightnessMode === 'both';
27
+ };
28
+ export const buildNativeBrightnessCommands = (brightness, brightnessMode = 'rgb') => {
29
+ return shouldUseNativeBrightness(brightnessMode) ? [buildBrightnessCommand(brightness)] : [];
30
+ };
31
+ export const buildPowerOnPrefixCommands = (powerMode = 'both', brightnessMode = 'rgb', brightness = 100) => {
32
+ return [
33
+ ...(shouldUseNativePower(powerMode) ? [buildPowerCommand(true)] : []),
34
+ ...buildNativeBrightnessCommands(brightness, brightnessMode),
35
+ ];
36
+ };
37
+ export const buildPowerOffCommands = (powerMode = 'both') => {
38
+ return [
39
+ ...(shouldUseRgbBlackOff(powerMode) ? [buildColorCommand({ red: 0, green: 0, blue: 0 })] : []),
40
+ ...(shouldUseNativePower(powerMode) ? [buildPowerCommand(false)] : []),
41
+ ];
42
+ };
43
+ export const buildEffectSpeedCommand = (speed) => {
44
+ return frame(0x7e, 0x04, 0x02, clampPercent(speed), 0xff, 0xff, 0xff, 0x00, 0xef);
45
+ };
46
+ export const buildBasicEffectCommand = (effectCode) => {
47
+ return frame(0x7e, 0x05, 0x03, effectCode, 0x06, 0xff, 0xff, 0x00, 0xef);
48
+ };
49
+ //# sourceMappingURL=magicLanternCommands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"magicLanternCommands.js","sourceRoot":"","sources":["../../src/ble/magicLanternCommands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ export interface NobleAdvertisement {
2
+ localName?: string;
3
+ manufacturerData?: Buffer;
4
+ serviceUuids?: string[];
5
+ }
6
+ export interface NobleCharacteristic {
7
+ uuid: string;
8
+ properties: string[];
9
+ writeAsync(data: Buffer, withoutResponse: boolean): Promise<void>;
10
+ }
11
+ export interface NoblePeripheral {
12
+ id: string;
13
+ uuid?: string;
14
+ address?: string;
15
+ advertisement?: NobleAdvertisement;
16
+ connectable?: boolean;
17
+ rssi?: number;
18
+ state?: string;
19
+ connectAsync(): Promise<void>;
20
+ disconnectAsync(): Promise<void>;
21
+ discoverSomeServicesAndCharacteristicsAsync(serviceUuids: string[], characteristicUuids: string[]): Promise<{
22
+ characteristics: NobleCharacteristic[];
23
+ }>;
24
+ on(event: 'disconnect', listener: () => void): void;
25
+ removeListener(event: 'disconnect', listener: () => void): void;
26
+ }
27
+ export interface NobleAdapter {
28
+ waitForPoweredOnAsync(timeout?: number): Promise<void>;
29
+ startScanningAsync(serviceUuids?: string[], allowDuplicates?: boolean): Promise<void>;
30
+ stopScanningAsync(): Promise<void>;
31
+ on(event: 'discover', listener: (peripheral: NoblePeripheral) => void): void;
32
+ removeListener(event: 'discover', listener: (peripheral: NoblePeripheral) => void): void;
33
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=nobleTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nobleTypes.js","sourceRoot":"","sources":["../../src/ble/nobleTypes.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import type { ColorOrder } from './types.js';
2
+ export interface RgbColor {
3
+ red: number;
4
+ green: number;
5
+ blue: number;
6
+ }
7
+ export declare const clampByte: (value: number) => number;
8
+ export declare const clampPercent: (value: number) => number;
9
+ export declare const hsbToRgb: (hue: number, saturation: number, brightness: number) => RgbColor;
10
+ export declare const applyColorOrder: (color: RgbColor, colorOrder?: ColorOrder) => RgbColor;
package/dist/color.js ADDED
@@ -0,0 +1,65 @@
1
+ export const clampByte = (value) => {
2
+ if (!Number.isFinite(value)) {
3
+ return 0;
4
+ }
5
+ return Math.max(0, Math.min(255, Math.round(value)));
6
+ };
7
+ export const clampPercent = (value) => {
8
+ if (!Number.isFinite(value)) {
9
+ return 0;
10
+ }
11
+ return Math.max(0, Math.min(100, Math.round(value)));
12
+ };
13
+ export const hsbToRgb = (hue, saturation, brightness) => {
14
+ const h = ((hue % 360) + 360) % 360;
15
+ const s = Math.max(0, Math.min(100, saturation)) / 100;
16
+ const v = Math.max(0, Math.min(100, brightness)) / 100;
17
+ const c = v * s;
18
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
19
+ const m = v - c;
20
+ let red = 0;
21
+ let green = 0;
22
+ let blue = 0;
23
+ if (h < 60) {
24
+ red = c;
25
+ green = x;
26
+ }
27
+ else if (h < 120) {
28
+ red = x;
29
+ green = c;
30
+ }
31
+ else if (h < 180) {
32
+ green = c;
33
+ blue = x;
34
+ }
35
+ else if (h < 240) {
36
+ green = x;
37
+ blue = c;
38
+ }
39
+ else if (h < 300) {
40
+ red = x;
41
+ blue = c;
42
+ }
43
+ else {
44
+ red = c;
45
+ blue = x;
46
+ }
47
+ return {
48
+ red: clampByte((red + m) * 255),
49
+ green: clampByte((green + m) * 255),
50
+ blue: clampByte((blue + m) * 255),
51
+ };
52
+ };
53
+ export const applyColorOrder = (color, colorOrder = 'rgb') => {
54
+ const values = {
55
+ r: color.red,
56
+ g: color.green,
57
+ b: color.blue,
58
+ };
59
+ return {
60
+ red: values[colorOrder[0]],
61
+ green: values[colorOrder[1]],
62
+ blue: values[colorOrder[2]],
63
+ };
64
+ };
65
+ //# sourceMappingURL=color.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.js","sourceRoot":"","sources":["../src/color.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { API } from 'homebridge';
2
+ declare const _default: (api: API) => void;
3
+ export default _default;
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import { LanternIcPlatform } from './platform.js';
2
+ import { PLATFORM_NAME } from './settings.js';
3
+ export default (api) => {
4
+ api.registerPlatform(PLATFORM_NAME, LanternIcPlatform);
5
+ };
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,eAAe,CAAC,GAAQ,EAAE,EAAE;IAC1B,GAAG,CAAC,gBAAgB,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;AACzD,CAAC,CAAC"}