divoom-timesgate-sdk 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jason Praful
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,371 @@
1
+ # divoom-timesgate-sdk
2
+
3
+ > A fully type-safe Node.js & TypeScript SDK for controlling the **Divoom Times Gate** over your local network — with a high-resolution image & animation pipeline, complete command coverage, and zero-guesswork docs.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/divoom-timesgate-sdk.svg)](https://www.npmjs.com/package/divoom-timesgate-sdk)
6
+ [![CI](https://github.com/jasonpraful/divoom-timesgate-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/jasonpraful/divoom-timesgate-sdk/actions/workflows/ci.yml)
7
+ [![Types](https://img.shields.io/npm/types/divoom-timesgate-sdk.svg)](https://www.npmjs.com/package/divoom-timesgate-sdk)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
9
+
10
+ ---
11
+
12
+ ## Highlights
13
+
14
+ - 🧊 **Every documented command, fully typed** — system settings, dial/channel control, built-in tools, animations, and the image pipeline.
15
+ - 🖼️ **High-resolution image pipeline** — push crisp 128×128 JPEG frames (LANCZOS3 + q95, 4:4:4 chroma) via [`sharp`](https://sharp.pixelplumbing.com/). Album-art styling, accent extraction, designed "now-playing" panels, and multi-panel panoramas.
16
+ - 🧠 **Ergonomic + safe** — grouped client API (`client.system`, `client.draw`, …), argument validation with descriptive errors, and a typed error hierarchy.
17
+ - 🔌 **Modern packaging** — dual ESM/CJS, first-class types, tree-shakeable. The image module is a separate entry point so core-only users never pull in `sharp`.
18
+ - 🔎 **LAN discovery + cloud helpers** — find your device's IP, and browse dials/images/fonts from Divoom's cloud.
19
+ - ✅ **Tested & documented** — extensive unit tests and TSDoc on every public symbol.
20
+
21
+ ## Table of contents
22
+
23
+ - [Install](#install)
24
+ - [Quick start](#quick-start)
25
+ - [Getting your `LocalToken`](#getting-your-localtoken)
26
+ - [Finding your device](#finding-your-device)
27
+ - [Core concepts](#core-concepts)
28
+ - [The image pipeline](#the-image-pipeline)
29
+ - [Command reference](#command-reference)
30
+ - [Cloud discovery](#cloud-discovery)
31
+ - [Error handling](#error-handling)
32
+ - [Configuration](#configuration)
33
+ - [Examples](#examples)
34
+ - [Development](#development)
35
+
36
+ ## Install
37
+
38
+ ```bash
39
+ npm install divoom-timesgate-sdk
40
+ # or
41
+ pnpm add divoom-timesgate-sdk
42
+ # or
43
+ yarn add divoom-timesgate-sdk
44
+ ```
45
+
46
+ Requires **Node.js 18+** (uses the global `fetch`). `sharp` is included for the image pipeline and ships prebuilt binaries for common platforms.
47
+
48
+ ## Quick start
49
+
50
+ ```ts
51
+ import { TimesGateClient } from 'divoom-timesgate-sdk';
52
+ import { encodePanel } from 'divoom-timesgate-sdk/image';
53
+
54
+ const client = new TimesGateClient({
55
+ host: '192.168.1.50', // your Times Gate's LAN IP
56
+ localToken: 229930, // from the Divoom app → device → Settings (see below)
57
+ });
58
+
59
+ // Is it reachable?
60
+ if (!(await client.ping())) throw new Error('Times Gate not reachable');
61
+
62
+ // Set the brightness
63
+ await client.system.setBrightness(80);
64
+
65
+ // Push album art (or any image — file path, URL, or Buffer) to panel 0
66
+ const frame = await encodePanel('https://example.com/cover.jpg');
67
+ await client.draw.sendImage(0, frame.data);
68
+ ```
69
+
70
+ ## Getting your `LocalToken`
71
+
72
+ Many control commands (such as `Channel/SetBrightness`) are authenticated with a **`LocalToken`** — a stable, per-device number.
73
+
74
+ > ⚠️ The current public API docs don't list `LocalToken`, but real devices still use it. You can read it in the **Divoom mobile app → your Times Gate → Settings**. It looks like a number, e.g. `229930`.
75
+
76
+ Pass it once when you create the client and the SDK injects it into every request:
77
+
78
+ ```ts
79
+ const client = new TimesGateClient({ host: '192.168.1.50', localToken: 229930 });
80
+ ```
81
+
82
+ Read-only commands generally work without it, so for quick experiments you can omit it (it defaults to `0`). If a command comes back with a `DivoomDeviceError`, a missing/incorrect `LocalToken` is the usual cause.
83
+
84
+ ## Finding your device
85
+
86
+ If you don't know the device's IP, discover it from the same network:
87
+
88
+ ```ts
89
+ import { discoverDevices, TimesGateClient } from 'divoom-timesgate-sdk';
90
+
91
+ const devices = await discoverDevices();
92
+ // → [{ name: 'Times Gate', id: 300038420, ip: '192.168.1.50', mac: 'a8032aff46b1' }]
93
+
94
+ const gate = devices.find((d) => d.name.toLowerCase().includes('times gate'));
95
+ const client = new TimesGateClient({ host: gate!.ip, localToken: 229930 });
96
+ ```
97
+
98
+ > Discovery goes through Divoom's cloud (it reports devices seen from your public IP), so it needs outbound internet and won't work across different networks.
99
+
100
+ ## Core concepts
101
+
102
+ - **5 panels.** The Times Gate has five LCD panels, indexed `0`–`4` (`PanelIndex`), each **128×128**.
103
+ - **Panel masks.** Some commands act on multiple panels at once via an `LcdArray` — a 5-element mask like `[1, 0, 0, 0, 1]` (`1` = act, `0` = skip). The SDK builds these for you from panel indices, with helpers `panelToLcdArray` / `panelsToLcdArray` if you need them.
104
+ - **PicIDs.** The device caches image frames by a strictly-increasing `PicID`. `client.draw` manages this automatically (you can override or read the device's value with `getPicId()`).
105
+ - **Hardware versions.** Hardware 400 uses `http://IP:80/post` (the default). Hardware 402 uses `http://IP:9000/divoom_api` — pass `port: 9000, path: '/divoom_api'`.
106
+
107
+ ## The image pipeline
108
+
109
+ The image helpers live in the **`divoom-timesgate-sdk/image`** entry point (so projects that only send pre-encoded bytes never load `sharp`). Every helper returns an `EncodedFrame` whose `.data` is the base64 string you hand to `client.draw.sendImage(...)`.
110
+
111
+ ```ts
112
+ import {
113
+ encodePanel,
114
+ prepareAlbumArt,
115
+ renderTextPanel,
116
+ splitImageAcrossPanels,
117
+ getAccentColor,
118
+ solidFrame,
119
+ } from 'divoom-timesgate-sdk/image';
120
+ ```
121
+
122
+ ### Resize & encode any image
123
+
124
+ ```ts
125
+ const frame = await encodePanel('./photo.png', { fit: 'cover' }); // 128×128 JPEG q95
126
+ await client.draw.sendImage(0, frame.data);
127
+ ```
128
+
129
+ `encodePanel` accepts a **file path, an `http(s)` URL, a `Buffer`/`Uint8Array`, or a `sharp` instance**.
130
+
131
+ ### Album art (smooth or pixel)
132
+
133
+ ```ts
134
+ const art = await prepareAlbumArt(coverUrl, { style: 'smooth' }); // or 'pixel'
135
+ const accent = await getAccentColor(coverUrl); // vivid color for theming → '#E94F37'
136
+ await client.draw.sendImage(0, art.data);
137
+ ```
138
+
139
+ ### Designed "now-playing" text panel
140
+
141
+ Crisp, auto-wrapped text rendered with Pango — no reliance on host system fonts:
142
+
143
+ ```ts
144
+ const panel = await renderTextPanel({
145
+ eyebrow: 'Now Playing',
146
+ title: 'Bohemian Rhapsody',
147
+ subtitle: 'Queen',
148
+ accent: '#E94F37',
149
+ progress: 0.42, // draws a progress bar
150
+ });
151
+ await client.draw.sendImage(1, panel.data);
152
+ ```
153
+
154
+ ### One image across all five panels
155
+
156
+ ```ts
157
+ const tiles = await splitImageAcrossPanels('./wide-banner.png'); // panorama
158
+ for (const { panel, frame } of tiles) {
159
+ await client.draw.sendImage(panel, frame.data);
160
+ }
161
+ ```
162
+
163
+ ### Animations
164
+
165
+ ```ts
166
+ // Build frames however you like (here: a color cycle) and play them.
167
+ const frames = await Promise.all(
168
+ ['#FF0066', '#FF9900', '#33CC66', '#3399FF'].map((c) => solidFrame(c)),
169
+ );
170
+ await client.draw.sendAnimation(
171
+ [0],
172
+ frames.map((f) => f.data),
173
+ { picSpeed: 120 },
174
+ );
175
+ ```
176
+
177
+ ## Command reference
178
+
179
+ Everything hangs off a `TimesGateClient`, grouped by area. A few highlights:
180
+
181
+ ### `client.system`
182
+
183
+ | Method | Command | Notes |
184
+ | --------------------------------------------- | ----------------------- | -------------------------- |
185
+ | `setBrightness(0–100)` | `Channel/SetBrightness` | |
186
+ | `getAllConfig()` | `Channel/GetAllConf` | brightness, formats, flags |
187
+ | `setScreen(on)` | `Channel/OnOffScreen` | |
188
+ | `setTimeZone('GMT-5')` | `Sys/TimeZone` | |
189
+ | `setSystemTime(date)` | `Device/SetUTC` | `Date` or epoch seconds |
190
+ | `setWeatherLocation(lon, lat)` | `Sys/LogAndLat` | |
191
+ | `getWeather()` | `Device/GetWeatherInfo` | |
192
+ | `setTemperatureMode('celsius'\|'fahrenheit')` | `Device/SetDisTempMode` | |
193
+ | `setMirrorMode(enabled)` | `Device/SetMirrorMode` | |
194
+ | `setHourMode(use24Hour)` | `Device/SetTime24Flag` | |
195
+ | `getDeviceTime()` | `Device/GetDeviceTime` | |
196
+
197
+ ### `client.draw` (image pipeline)
198
+
199
+ ```ts
200
+ await client.draw.sendImage(panel, frame.data, { picSpeed: 1000 });
201
+ await client.draw.sendImageToPanels([0, 2, 4], frame.data);
202
+ await client.draw.sendAnimation([0], frames, { picSpeed: 100 });
203
+ await client.draw.resetCache(); // clear stuck frames / reset the PicID counter
204
+ const id = await client.draw.getPicId();
205
+ ```
206
+
207
+ ### `client.dial` (what each panel shows)
208
+
209
+ ```ts
210
+ await client.dial.selectWholeDial(7); // one dial across all 5 panels
211
+ await client.dial.setChannelMode('independent', 978);
212
+ await client.dial.selectSubDial(0, 10, 978); // panel 0 → dial 10
213
+ const { SelectIndex } = await client.dial.getIndex();
214
+ ```
215
+
216
+ ### `client.tool`
217
+
218
+ ```ts
219
+ await client.tool.setCountdown(1, 30); // 1:30 countdown, start
220
+ await client.tool.setStopwatch('start'); // 'start' | 'stop' | 'reset'
221
+ await client.tool.setScoreboard(3, 5); // red, blue
222
+ await client.tool.setNoiseMeter(true);
223
+ await client.tool.playBuzzer({ activeMs: 200, offMs: 100, totalMs: 900 });
224
+ ```
225
+
226
+ ### `client.animation`
227
+
228
+ ```ts
229
+ await client.animation.playGifUrls([0], ['http://f.divoom-gz.com/64_64.gif']);
230
+ await client.animation.playStoredGif([0], fileId); // FileId from the cloud client
231
+ await client.animation.sendText(0, 'Hello!', { align: 'center', color: '#FFD400' });
232
+ await client.animation.sendItemList(1, items, { backgroundGif });
233
+ ```
234
+
235
+ ### `client.batch`
236
+
237
+ ```ts
238
+ await client.batch.run([
239
+ { Command: 'Channel/SetBrightness', Brightness: 100 },
240
+ { Command: 'Channel/OnOffScreen', OnOff: 1 },
241
+ ]);
242
+ ```
243
+
244
+ ### Escape hatch
245
+
246
+ For anything not yet wrapped, send a raw payload (still typed, `LocalToken` still injected):
247
+
248
+ ```ts
249
+ const res = await client.send<{ error_code: number; PicId: number }>({
250
+ Command: 'Draw/GetHttpGifId',
251
+ });
252
+ ```
253
+
254
+ ## Cloud discovery
255
+
256
+ Two cloud-backed helpers (HTTPS to `app.divoom-gz.com`) complement the on-device API:
257
+
258
+ - **`discoverDevices(options?)` → `Promise<DiscoveredDevice[]>`** — find Times Gates on your network and read each one's IP, id, and MAC. See [Finding your device](#finding-your-device) for a full example.
259
+ - **`DivoomCloudClient`** — browse dials, stored images, and fonts; their ids then drive on-device commands.
260
+
261
+ ```ts
262
+ import { DivoomCloudClient } from 'divoom-timesgate-sdk';
263
+
264
+ const cloud = new DivoomCloudClient();
265
+ const { ClockList } = await cloud.getWholeDialList(1);
266
+ await client.dial.selectWholeDial(ClockList[0].ClockId);
267
+
268
+ const { DialTypeList } = await cloud.getDialTypes();
269
+ const { DialList } = await cloud.getDialList(DialTypeList[0], 1);
270
+ const { FontList } = await cloud.getFontList();
271
+ ```
272
+
273
+ ## Error handling
274
+
275
+ Every error extends `DivoomError`, so you can catch the whole family or narrow to a specific cause:
276
+
277
+ ```ts
278
+ import {
279
+ DivoomError,
280
+ DivoomDeviceError,
281
+ DivoomTimeoutError,
282
+ DivoomValidationError,
283
+ } from 'divoom-timesgate-sdk';
284
+
285
+ try {
286
+ await client.system.setBrightness(150); // invalid → throws before any request
287
+ } catch (err) {
288
+ if (err instanceof DivoomValidationError) console.error('Bad argument:', err.message);
289
+ else if (err instanceof DivoomTimeoutError) console.error('Device too slow');
290
+ else if (err instanceof DivoomDeviceError) console.error('Device said no:', err.errorCode);
291
+ else if (err instanceof DivoomError) console.error('Divoom error:', err.message);
292
+ }
293
+ ```
294
+
295
+ | Error | When |
296
+ | ----------------------- | ------------------------------------------- |
297
+ | `DivoomValidationError` | Bad argument, caught before sending. |
298
+ | `DivoomConnectionError` | Device unreachable. |
299
+ | `DivoomTimeoutError` | Request exceeded the timeout. |
300
+ | `DivoomHttpError` | Non-2xx HTTP response. |
301
+ | `DivoomDeviceError` | Device returned a non-zero `error_code`. |
302
+ | `DivoomCloudError` | Cloud API returned a non-zero `ReturnCode`. |
303
+
304
+ ## Configuration
305
+
306
+ ```ts
307
+ const client = new TimesGateClient({
308
+ host: '192.168.1.50',
309
+ localToken: 229930,
310
+ port: 80, // 9000 for hardware version 402
311
+ path: '/post', // '/divoom_api' for hardware version 402
312
+ protocol: 'http',
313
+ timeoutMs: 8000, // per-request timeout
314
+ retries: 2, // automatic retries for transient failures
315
+ retryDelayMs: 300, // exponential backoff base
316
+ fetch: customFetch, // inject your own fetch (proxies, tests, …)
317
+ });
318
+ ```
319
+
320
+ Transient failures (network errors, timeouts, 5xx) are retried with exponential backoff; device-level and 4xx errors are surfaced immediately.
321
+
322
+ ## Examples
323
+
324
+ Runnable example apps live in [`examples/`](./examples):
325
+
326
+ | Example | What it shows |
327
+ | --------------------------- | ------------------------------------------------------ |
328
+ | `01-quickstart` | Connect, ping, set brightness, push a frame. |
329
+ | `02-spotify-now-playing` | Album art + live now-playing panel (the flagship). |
330
+ | `03-system-and-weather` | Configure the device and read weather/config. |
331
+ | `04-tools` | Countdown, stopwatch, scoreboard, noise meter, buzzer. |
332
+ | `05-panorama` | One image spanning all five panels. |
333
+ | `06-animation` | Multi-frame animations and hosted GIFs. |
334
+ | `07-text-and-notifications` | Designed text panels and scrolling text. |
335
+
336
+ ```bash
337
+ pnpm install
338
+ pnpm --filter @divoom-timesgate-examples/01-quickstart start
339
+ ```
340
+
341
+ ## Security notes
342
+
343
+ - **Your LAN is the trust boundary.** The device API is plain HTTP and unauthenticated on the local network aside from the per-device `LocalToken`. The optional cloud helpers (`discoverDevices`, `DivoomCloudClient`) use HTTPS to `app.divoom-gz.com`.
344
+ - **Image sources.** The `image` helpers treat a string source as an `http(s)` URL or a **local file path**. Don't pass untrusted/attacker-influenced strings — a URL can trigger a server-side request (SSRF) and a path can read local files. URL downloads are bounded by a timeout and a size cap; for untrusted input, validate the source yourself or pass a `Buffer` you fetched under your own controls.
345
+ - **Device-side fetches.** `animation.playGifUrls` / `playGifPerPanel` / `sendItemList({ backgroundGif })` and `batch.useCommandSource` make the **device** fetch a URL you supply (and `useCommandSource` executes the returned commands). Only pass URLs you trust; allowlist hosts if end users can influence them.
346
+
347
+ See [SECURITY.md](./SECURITY.md) for reporting and more detail.
348
+
349
+ ## Development
350
+
351
+ ```bash
352
+ pnpm install
353
+ pnpm build # tsup → dual ESM/CJS + d.ts
354
+ pnpm test # vitest
355
+ pnpm test:coverage # coverage report
356
+ pnpm typecheck # tsc --noEmit
357
+ pnpm lint # eslint
358
+ pnpm format # prettier --write
359
+ pnpm check # typecheck + lint + format:check + test
360
+ pnpm docs # typedoc → ./docs
361
+ ```
362
+
363
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for the contribution workflow (we use [Changesets](https://github.com/changesets/changesets) for versioning).
364
+
365
+ ## Acknowledgements
366
+
367
+ Command shapes are transcribed from Divoom's official [ShowDoc API](https://doc.divoom-gz.com/web/#/12?page_id=538). This project is not affiliated with or endorsed by Divoom.
368
+
369
+ ## License
370
+
371
+ [MIT](./LICENSE) © Jason Praful
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Shared, transport-level types used across the whole SDK.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ /**
7
+ * A `fetch`-compatible function. The SDK uses the global {@link fetch} by
8
+ * default (Node 18+), but you can inject your own implementation — handy for
9
+ * testing, proxies, or custom networking.
10
+ */
11
+ type FetchLike = typeof fetch;
12
+ /**
13
+ * Identifies one of the Times Gate's five LCD panels, numbered left-to-right
14
+ * as `0 | 1 | 2 | 3 | 4`.
15
+ */
16
+ type PanelIndex = 0 | 1 | 2 | 3 | 4;
17
+ /**
18
+ * A five-element panel selection mask — one entry per panel, where `1` means
19
+ * "apply to this panel" and `0` means "leave it alone".
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const onlyFirstPanel: LcdArray = [1, 0, 0, 0, 0];
24
+ * const allPanels: LcdArray = [1, 1, 1, 1, 1];
25
+ * ```
26
+ */
27
+ type LcdArray = [number, number, number, number, number];
28
+ /**
29
+ * The base shape returned by every Times Gate command. A `error_code` of `0`
30
+ * indicates success; any other value is surfaced as a
31
+ * {@link DivoomDeviceError}.
32
+ */
33
+ interface DivoomBaseResponse {
34
+ /** `0` on success; non-zero indicates a device-side error. */
35
+ error_code: number;
36
+ /** Additional, command-specific fields returned by the device. */
37
+ [key: string]: unknown;
38
+ }
39
+ /**
40
+ * The minimal shape of any command request body. Every command carries a
41
+ * string {@link DivoomCommandRequest.Command | Command} discriminator and may
42
+ * include the device's {@link DivoomCommandRequest.LocalToken | LocalToken}.
43
+ */
44
+ interface DivoomCommandRequest {
45
+ /** The Divoom command name, e.g. `"Channel/SetBrightness"`. */
46
+ Command: string;
47
+ /**
48
+ * The device's local authentication token. Injected automatically by the
49
+ * {@link HttpTransport} when configured, so you rarely set this by hand.
50
+ */
51
+ LocalToken?: number;
52
+ }
53
+
54
+ export type { DivoomBaseResponse as D, FetchLike as F, LcdArray as L, PanelIndex as P, DivoomCommandRequest as a };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Shared, transport-level types used across the whole SDK.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ /**
7
+ * A `fetch`-compatible function. The SDK uses the global {@link fetch} by
8
+ * default (Node 18+), but you can inject your own implementation — handy for
9
+ * testing, proxies, or custom networking.
10
+ */
11
+ type FetchLike = typeof fetch;
12
+ /**
13
+ * Identifies one of the Times Gate's five LCD panels, numbered left-to-right
14
+ * as `0 | 1 | 2 | 3 | 4`.
15
+ */
16
+ type PanelIndex = 0 | 1 | 2 | 3 | 4;
17
+ /**
18
+ * A five-element panel selection mask — one entry per panel, where `1` means
19
+ * "apply to this panel" and `0` means "leave it alone".
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const onlyFirstPanel: LcdArray = [1, 0, 0, 0, 0];
24
+ * const allPanels: LcdArray = [1, 1, 1, 1, 1];
25
+ * ```
26
+ */
27
+ type LcdArray = [number, number, number, number, number];
28
+ /**
29
+ * The base shape returned by every Times Gate command. A `error_code` of `0`
30
+ * indicates success; any other value is surfaced as a
31
+ * {@link DivoomDeviceError}.
32
+ */
33
+ interface DivoomBaseResponse {
34
+ /** `0` on success; non-zero indicates a device-side error. */
35
+ error_code: number;
36
+ /** Additional, command-specific fields returned by the device. */
37
+ [key: string]: unknown;
38
+ }
39
+ /**
40
+ * The minimal shape of any command request body. Every command carries a
41
+ * string {@link DivoomCommandRequest.Command | Command} discriminator and may
42
+ * include the device's {@link DivoomCommandRequest.LocalToken | LocalToken}.
43
+ */
44
+ interface DivoomCommandRequest {
45
+ /** The Divoom command name, e.g. `"Channel/SetBrightness"`. */
46
+ Command: string;
47
+ /**
48
+ * The device's local authentication token. Injected automatically by the
49
+ * {@link HttpTransport} when configured, so you rarely set this by hand.
50
+ */
51
+ LocalToken?: number;
52
+ }
53
+
54
+ export type { DivoomBaseResponse as D, FetchLike as F, LcdArray as L, PanelIndex as P, DivoomCommandRequest as a };