@novastar/taurus 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/dist/index.mjs ADDED
@@ -0,0 +1,355 @@
1
+ import { randomUUID } from "crypto";
2
+ import tls from "tls";
3
+ import { Struct, typed } from "typed-struct";
4
+ import dgram from "dgram";
5
+ import { networkInterfaces } from "os";
6
+
7
+ //#region src/packet.ts
8
+ const TAURUS_PACKET_FLAG = 1313822273;
9
+ const TAURUS_REQUEST = 21073;
10
+ const TAURUS_RESPONSE = 21072;
11
+ const TAURUS_DISCOVERY = 34901;
12
+ /**
13
+ * Taurus request argument.
14
+ *
15
+ * Wire layout:
16
+ * action UInt8
17
+ * type UInt8
18
+ * reserved UInt16LE
19
+ */
20
+ const TaurusRequest = new Struct("TaurusRequest").UInt8("action").UInt8("type").seek(2).compile();
21
+ /**
22
+ * Taurus response argument.
23
+ *
24
+ * Wire layout:
25
+ * status UInt16LE
26
+ * action UInt8
27
+ * type UInt8
28
+ */
29
+ const TaurusResponse = new Struct("TaurusResponse").UInt16LE("status").UInt8("action").UInt8("type").compile();
30
+ /**
31
+ * Taurus packet.
32
+ *
33
+ * The same 4 bytes are exposed as both request and response structures.
34
+ * packetType determines which representation should be used.
35
+ */
36
+ const TaurusPacket = new Struct("TaurusPacket").UInt32LE("flag", TAURUS_PACKET_FLAG).UInt32LE("sequence").UInt16LE("packetType", typed()).UInt16LE("what").Struct("req", TaurusRequest).back().Struct("res", TaurusResponse).UInt32LE("length").UInt8("encodeType").seek(1).UInt16LE("checksum").Buffer("body").compile();
37
+ const TAURUS_HEADER_SIZE = TaurusPacket.baseSize;
38
+ const FLAG_OFFSET = TaurusPacket.getOffsetOf("flag");
39
+ const LENGTH_OFFSET = TaurusPacket.getOffsetOf("length");
40
+ const CHECKSUM_OFFSET = TaurusPacket.getOffsetOf("checksum");
41
+ const signedByte = (value) => value > 127 ? value - 256 : value;
42
+ /**
43
+ * Calculates Taurus header checksum.
44
+ *
45
+ * The checksum is the sum of all signed 8-bit values preceding
46
+ * the checksum field.
47
+ */
48
+ const calculateTaurusHeaderChecksum = (header) => {
49
+ if (header.length < CHECKSUM_OFFSET) throw new RangeError("Taurus header is too short");
50
+ let checksum = 0;
51
+ for (let index = 0; index < CHECKSUM_OFFSET; index += 1) checksum += signedByte(header[index]);
52
+ return checksum & 65535;
53
+ };
54
+ /**
55
+ * Returns the full packet size once the complete header is available.
56
+ */
57
+ const getTaurusPacketSize = (buffer) => {
58
+ if (buffer.length < TAURUS_HEADER_SIZE) return;
59
+ const view = Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength);
60
+ if (view.readUInt32LE(FLAG_OFFSET) !== 1313822273) throw new Error("Invalid Taurus packet flag");
61
+ return TAURUS_HEADER_SIZE + view.readUInt32LE(LENGTH_OFFSET);
62
+ };
63
+ /**
64
+ * Encodes a Taurus request.
65
+ */
66
+ const encodeTaurusRequest = (sequence, { what, type, action }, body = Buffer.alloc(0), encodeType = 0) => {
67
+ const payload = typeof body === "string" ? Buffer.from(body, "utf8") : body;
68
+ const packet = new TaurusPacket(TAURUS_HEADER_SIZE + payload.length);
69
+ packet.sequence = sequence;
70
+ packet.packetType = TAURUS_REQUEST;
71
+ packet.what = what;
72
+ packet.req.action = action;
73
+ packet.req.type = type;
74
+ packet.length = payload.length;
75
+ packet.encodeType = encodeType;
76
+ payload.copy(packet.body);
77
+ const raw = TaurusPacket.raw(packet);
78
+ packet.checksum = calculateTaurusHeaderChecksum(raw);
79
+ return raw;
80
+ };
81
+ /** Encodes the UDP search request used by Taurus players on port 16601. */
82
+ const encodeTaurusDiscoveryRequest = (supportsEncryption = true) => {
83
+ const packet = new TaurusPacket(TAURUS_HEADER_SIZE);
84
+ packet.sequence = 4294967295;
85
+ packet.packetType = TAURUS_DISCOVERY;
86
+ packet.what = 129;
87
+ packet.req.action = supportsEncryption ? 1 : 0;
88
+ packet.req.type = 0;
89
+ packet.length = 0;
90
+ packet.checksum = calculateTaurusHeaderChecksum(TaurusPacket.raw(packet));
91
+ return TaurusPacket.raw(packet);
92
+ };
93
+ /**
94
+ * Decodes and validates a Taurus packet.
95
+ */
96
+ const decodeTaurusPacket = (buffer) => {
97
+ const view = Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength);
98
+ const packetSize = getTaurusPacketSize(view);
99
+ if (packetSize === void 0 || view.length < packetSize) throw new Error("Incomplete Taurus packet");
100
+ const expectedChecksum = view.readUInt16LE(CHECKSUM_OFFSET);
101
+ const actualChecksum = calculateTaurusHeaderChecksum(view);
102
+ if (actualChecksum !== expectedChecksum) throw new Error(`Invalid Taurus header checksum: expected 0x${expectedChecksum.toString(16)}, calculated 0x${actualChecksum.toString(16)}`);
103
+ return new TaurusPacket(view.subarray(0, packetSize));
104
+ };
105
+
106
+ //#endregion
107
+ //#region src/connection.ts
108
+ var TaurusResponseError = class extends Error {
109
+ status;
110
+ responseBody;
111
+ constructor(status, responseBody) {
112
+ super(`Taurus request failed with status 0x${status.toString(16)}${responseBody ? `: ${responseBody}` : ""}`);
113
+ this.status = status;
114
+ this.responseBody = responseBody;
115
+ this.name = "TaurusResponseError";
116
+ }
117
+ };
118
+ var TaurusConnection = class {
119
+ stream;
120
+ timeout;
121
+ #pending = /* @__PURE__ */ new Map();
122
+ #buffer = Buffer.alloc(0);
123
+ #sequence = 0;
124
+ #closed = false;
125
+ constructor(stream, timeout = 5e3) {
126
+ this.stream = stream;
127
+ this.timeout = timeout;
128
+ stream.on("data", (chunk) => this.#onData(Buffer.from(chunk)));
129
+ stream.once("close", () => this.#close(/* @__PURE__ */ new Error("Taurus connection closed")));
130
+ stream.once("error", (error) => this.#close(error));
131
+ }
132
+ get closed() {
133
+ return this.#closed;
134
+ }
135
+ request(command, body) {
136
+ if (this.#closed) return Promise.reject(/* @__PURE__ */ new Error("Taurus connection is closed"));
137
+ const sequence = (this.#sequence += 1) >>> 0;
138
+ const packet = encodeTaurusRequest(sequence, command, body);
139
+ return new Promise((resolve, reject) => {
140
+ const timer = setTimeout(() => {
141
+ this.#pending.delete(sequence);
142
+ reject(/* @__PURE__ */ new Error(`Taurus request ${sequence} timed out`));
143
+ }, this.timeout);
144
+ this.#pending.set(sequence, {
145
+ resolve,
146
+ reject,
147
+ timer
148
+ });
149
+ this.stream.write(packet, (error) => {
150
+ if (!error) return;
151
+ clearTimeout(timer);
152
+ this.#pending.delete(sequence);
153
+ reject(error);
154
+ });
155
+ });
156
+ }
157
+ async requestJson(command, body) {
158
+ const response = await this.request(command, body === void 0 ? void 0 : JSON.stringify(body));
159
+ const responseBody = response.body.toString("utf8");
160
+ if (response.res.status !== 0) throw new TaurusResponseError(response.res.status, responseBody);
161
+ return responseBody ? JSON.parse(responseBody) : void 0;
162
+ }
163
+ close() {
164
+ if (!this.stream.destroyed) this.stream.destroy();
165
+ this.#close(/* @__PURE__ */ new Error("Taurus connection closed"));
166
+ }
167
+ #onData(chunk) {
168
+ this.#buffer = this.#buffer.length ? Buffer.concat([this.#buffer, chunk]) : chunk;
169
+ while (this.#buffer.length >= TAURUS_HEADER_SIZE) {
170
+ let packetSize;
171
+ try {
172
+ packetSize = getTaurusPacketSize(this.#buffer);
173
+ } catch (error) {
174
+ this.#close(error);
175
+ this.stream.destroy(error);
176
+ return;
177
+ }
178
+ if (packetSize === void 0 || this.#buffer.length < packetSize) return;
179
+ const raw = this.#buffer.subarray(0, packetSize);
180
+ this.#buffer = this.#buffer.subarray(packetSize);
181
+ const packet = decodeTaurusPacket(raw);
182
+ if (packet.packetType !== 21072) continue;
183
+ const pending = this.#pending.get(packet.sequence);
184
+ if (!pending) continue;
185
+ clearTimeout(pending.timer);
186
+ this.#pending.delete(packet.sequence);
187
+ pending.resolve(packet);
188
+ }
189
+ }
190
+ #close(error) {
191
+ if (this.#closed) return;
192
+ this.#closed = true;
193
+ for (const pending of this.#pending.values()) {
194
+ clearTimeout(pending.timer);
195
+ pending.reject(error);
196
+ }
197
+ this.#pending.clear();
198
+ }
199
+ };
200
+
201
+ //#endregion
202
+ //#region src/client.ts
203
+ const TAURUS_MANAGEMENT_PORT = 16606;
204
+ const numberOrUndefined = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
205
+ var TaurusClient = class TaurusClient {
206
+ connection;
207
+ constructor(connection) {
208
+ this.connection = connection;
209
+ }
210
+ static async connect(options) {
211
+ const { host, port = TAURUS_MANAGEMENT_PORT, timeout = 5e3, privacy = true, ...tlsOptions } = options;
212
+ if (!privacy) {
213
+ const { connect } = await import("net");
214
+ const socket = connect({
215
+ host,
216
+ port
217
+ });
218
+ await new Promise((resolve, reject) => {
219
+ socket.once("connect", resolve);
220
+ socket.once("error", reject);
221
+ });
222
+ return new TaurusClient(new TaurusConnection(socket, timeout));
223
+ }
224
+ const socket = tls.connect({
225
+ host,
226
+ port,
227
+ rejectUnauthorized: false,
228
+ ...tlsOptions
229
+ });
230
+ await new Promise((resolve, reject) => {
231
+ socket.once("secureConnect", resolve);
232
+ socket.once("error", reject);
233
+ });
234
+ return new TaurusClient(new TaurusConnection(socket, timeout));
235
+ }
236
+ async login(options) {
237
+ const { token: _token, password: _password, ...safeResponse } = await this.connection.requestJson({
238
+ what: 0,
239
+ type: 0,
240
+ action: 0
241
+ }, {
242
+ sn: options.sn,
243
+ username: options.username ?? "admin",
244
+ password: options.password,
245
+ loginType: 0,
246
+ source: {
247
+ type: 1,
248
+ platform: 2,
249
+ platformVersion: "3.6.0"
250
+ },
251
+ clientId: options.clientId ?? randomUUID(),
252
+ clientName: options.clientName ?? "novastar.js"
253
+ });
254
+ return safeResponse;
255
+ }
256
+ async getBrightness() {
257
+ const result = await this.connection.requestJson({
258
+ what: 24,
259
+ type: 1,
260
+ action: 5
261
+ });
262
+ return {
263
+ brightness: numberOrUndefined(result.brightness),
264
+ ratio: numberOrUndefined(result.ratio),
265
+ solidity: result.solidity === true,
266
+ orderId: numberOrUndefined(result.orderId) ?? -1
267
+ };
268
+ }
269
+ async setBrightness(percent, persist = false) {
270
+ if (!Number.isFinite(percent) || percent < 0 || percent > 100) throw new RangeError("Brightness must be between 0 and 100");
271
+ await this.connection.requestJson({
272
+ what: 24,
273
+ type: 1,
274
+ action: 4
275
+ }, {
276
+ ratio: percent,
277
+ solidity: persist,
278
+ orderId: -1
279
+ });
280
+ }
281
+ async getEnvironmentBrightness() {
282
+ const value = numberOrUndefined((await this.connection.requestJson({
283
+ what: 26,
284
+ type: 0,
285
+ action: 5
286
+ })).value);
287
+ if (value === void 0 || value === -1e3) throw new Error("Taurus ambient light sensor is unavailable");
288
+ return value;
289
+ }
290
+ close() {
291
+ this.connection.close();
292
+ }
293
+ };
294
+
295
+ //#endregion
296
+ //#region src/discovery.ts
297
+ const TAURUS_DISCOVERY_PORT = 16601;
298
+ const ipv4ToUint32 = (address) => address.split(".").reduce((result, octet) => result << 8 | Number(octet), 0) >>> 0;
299
+ const getBroadcastAddress = (address, netmask) => {
300
+ const broadcast = (ipv4ToUint32(address) | ~ipv4ToUint32(netmask)) >>> 0;
301
+ return [
302
+ 24,
303
+ 16,
304
+ 8,
305
+ 0
306
+ ].map((shift) => broadcast >>> shift & 255).join(".");
307
+ };
308
+ const parseTaurusDiscoveryResponse = (message, address) => {
309
+ try {
310
+ const packet = decodeTaurusPacket(message);
311
+ if (packet.packetType !== 34901 || packet.what !== 1) return void 0;
312
+ const value = JSON.parse(packet.body.toString("utf8"));
313
+ if (typeof value.sn !== "string" || typeof value.tcpPort !== "number") return void 0;
314
+ return {
315
+ ...value,
316
+ address
317
+ };
318
+ } catch {
319
+ return;
320
+ }
321
+ };
322
+ const discoverFromInterface = ({ address, netmask }, timeout, destination) => new Promise((resolve) => {
323
+ const socket = dgram.createSocket("udp4");
324
+ const players = /* @__PURE__ */ new Map();
325
+ let completed = false;
326
+ const complete = () => {
327
+ if (completed) return;
328
+ completed = true;
329
+ try {
330
+ socket.close();
331
+ } catch {}
332
+ resolve([...players.values()]);
333
+ };
334
+ socket.once("error", complete);
335
+ socket.on("message", (message, remote) => {
336
+ const player = parseTaurusDiscoveryResponse(message, remote.address);
337
+ if (player) players.set(player.sn, player);
338
+ });
339
+ socket.bind(0, address, () => {
340
+ socket.setBroadcast(true);
341
+ const target = destination ?? getBroadcastAddress(address, netmask);
342
+ socket.send(encodeTaurusDiscoveryRequest(), TAURUS_DISCOVERY_PORT, target, (error) => {
343
+ if (error) complete();
344
+ });
345
+ setTimeout(complete, timeout).unref();
346
+ });
347
+ });
348
+ const discoverTaurusPlayers = async (destination, timeout = 1e3) => {
349
+ const interfaces = Object.values(networkInterfaces()).flat().filter((info) => Boolean(info && !info.internal && info.family === "IPv4"));
350
+ return [...(await Promise.all(interfaces.map((info) => discoverFromInterface(info, timeout, destination)))).flat().reduce((players, player) => players.set(player.sn, player), /* @__PURE__ */ new Map()).values()];
351
+ };
352
+
353
+ //#endregion
354
+ export { TAURUS_DISCOVERY, TAURUS_DISCOVERY_PORT, TAURUS_HEADER_SIZE, TAURUS_MANAGEMENT_PORT, TAURUS_PACKET_FLAG, TAURUS_REQUEST, TAURUS_RESPONSE, TaurusClient, TaurusConnection, TaurusPacket, TaurusResponseError, calculateTaurusHeaderChecksum, decodeTaurusPacket, discoverTaurusPlayers, encodeTaurusDiscoveryRequest, encodeTaurusRequest, getTaurusPacketSize, parseTaurusDiscoveryResponse };
355
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["#pending","#onData","#close","#closed","#sequence","#buffer"],"sources":["../src/packet.ts","../src/connection.ts","../src/client.ts","../src/discovery.ts"],"sourcesContent":["import { ExtractType, Struct, typed } from 'typed-struct';\n\nexport const TAURUS_PACKET_FLAG = 0x4e4f5641;\nexport const TAURUS_REQUEST = 0x5251;\nexport const TAURUS_RESPONSE = 0x5250;\nexport const TAURUS_DISCOVERY = 0x8855;\n\n/**\n * Taurus request argument.\n *\n * Wire layout:\n * action UInt8\n * type UInt8\n * reserved UInt16LE\n */\nconst TaurusRequest = new Struct('TaurusRequest')\n .UInt8('action')\n .UInt8('type')\n .seek(2) // reserved\n .compile();\n\nexport interface TaurusRequest extends ExtractType<typeof TaurusRequest, false> {}\n\n/**\n * Taurus response argument.\n *\n * Wire layout:\n * status UInt16LE\n * action UInt8\n * type UInt8\n */\nconst TaurusResponse = new Struct('TaurusResponse')\n .UInt16LE('status')\n .UInt8('action')\n .UInt8('type')\n .compile();\n\nexport interface TaurusResponse extends ExtractType<typeof TaurusResponse, false> {}\n\n/**\n * Taurus packet.\n *\n * The same 4 bytes are exposed as both request and response structures.\n * packetType determines which representation should be used.\n */\nexport const TaurusPacket = new Struct('TaurusPacket')\n .UInt32LE('flag', TAURUS_PACKET_FLAG)\n .UInt32LE('sequence')\n .UInt16LE(\n 'packetType',\n typed<typeof TAURUS_REQUEST | typeof TAURUS_RESPONSE | typeof TAURUS_DISCOVERY>(),\n )\n .UInt16LE('what')\n\n // Union: request/response occupy the same four bytes.\n .Struct('req', TaurusRequest)\n .back()\n .Struct('res', TaurusResponse)\n .UInt32LE('length')\n .UInt8('encodeType')\n .seek(1)\n .UInt16LE('checksum')\n .Buffer('body')\n .compile();\n\nexport interface TaurusPacket extends ExtractType<typeof TaurusPacket, false> {}\n\nexport interface TaurusCommand {\n what: number;\n type: number;\n action: number;\n}\n\nexport const TAURUS_HEADER_SIZE = TaurusPacket.baseSize;\n\nconst FLAG_OFFSET = TaurusPacket.getOffsetOf('flag');\nconst LENGTH_OFFSET = TaurusPacket.getOffsetOf('length');\nconst CHECKSUM_OFFSET = TaurusPacket.getOffsetOf('checksum');\n\nconst signedByte = (value: number): number => (value > 0x7f ? value - 0x100 : value);\n\n/**\n * Calculates Taurus header checksum.\n *\n * The checksum is the sum of all signed 8-bit values preceding\n * the checksum field.\n */\nexport const calculateTaurusHeaderChecksum = (header: Uint8Array): number => {\n if (header.length < CHECKSUM_OFFSET) {\n throw new RangeError('Taurus header is too short');\n }\n\n let checksum = 0;\n\n for (let index = 0; index < CHECKSUM_OFFSET; index += 1) {\n checksum += signedByte(header[index]);\n }\n\n return checksum & 0xffff;\n};\n\n/**\n * Returns the full packet size once the complete header is available.\n */\nexport const getTaurusPacketSize = (buffer: Uint8Array): number | undefined => {\n if (buffer.length < TAURUS_HEADER_SIZE) {\n return undefined;\n }\n\n const view = Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength);\n\n if (view.readUInt32LE(FLAG_OFFSET) !== TAURUS_PACKET_FLAG) {\n throw new Error('Invalid Taurus packet flag');\n }\n\n return TAURUS_HEADER_SIZE + view.readUInt32LE(LENGTH_OFFSET);\n};\n\n/**\n * Encodes a Taurus request.\n */\nexport const encodeTaurusRequest = (\n sequence: number,\n { what, type, action }: TaurusCommand,\n body: string | Buffer = Buffer.alloc(0),\n encodeType = 0,\n): Buffer => {\n const payload = typeof body === 'string' ? Buffer.from(body, 'utf8') : body;\n\n const packet = new TaurusPacket(TAURUS_HEADER_SIZE + payload.length);\n\n packet.sequence = sequence;\n packet.packetType = TAURUS_REQUEST;\n packet.what = what;\n\n packet.req.action = action;\n packet.req.type = type;\n\n packet.length = payload.length;\n packet.encodeType = encodeType;\n\n payload.copy(packet.body);\n\n const raw = TaurusPacket.raw(packet);\n\n packet.checksum = calculateTaurusHeaderChecksum(raw);\n\n return raw;\n};\n\n/** Encodes the UDP search request used by Taurus players on port 16601. */\nexport const encodeTaurusDiscoveryRequest = (supportsEncryption = true): Buffer => {\n const packet = new TaurusPacket(TAURUS_HEADER_SIZE);\n packet.sequence = 0xffffffff;\n packet.packetType = TAURUS_DISCOVERY;\n packet.what = 0x81;\n packet.req.action = supportsEncryption ? 1 : 0;\n packet.req.type = 0;\n packet.length = 0;\n packet.checksum = calculateTaurusHeaderChecksum(TaurusPacket.raw(packet));\n return TaurusPacket.raw(packet);\n};\n\n/**\n * Decodes and validates a Taurus packet.\n */\nexport const decodeTaurusPacket = (buffer: Uint8Array): TaurusPacket => {\n const view = Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength);\n\n const packetSize = getTaurusPacketSize(view);\n\n if (packetSize === undefined || view.length < packetSize) {\n throw new Error('Incomplete Taurus packet');\n }\n\n const expectedChecksum = view.readUInt16LE(CHECKSUM_OFFSET);\n const actualChecksum = calculateTaurusHeaderChecksum(view);\n\n if (actualChecksum !== expectedChecksum) {\n throw new Error(\n `Invalid Taurus header checksum: expected 0x${expectedChecksum.toString(16)}, ` +\n `calculated 0x${actualChecksum.toString(16)}`,\n );\n }\n\n return new TaurusPacket(view.subarray(0, packetSize));\n};\n","import { Duplex } from 'stream';\n\nimport {\n decodeTaurusPacket,\n encodeTaurusRequest,\n getTaurusPacketSize,\n TAURUS_HEADER_SIZE,\n TAURUS_RESPONSE,\n TaurusCommand,\n TaurusPacket,\n} from './packet';\n\ntype PendingRequest = {\n resolve: (packet: TaurusPacket) => void;\n reject: (error: Error) => void;\n timer: NodeJS.Timeout;\n};\n\nexport class TaurusResponseError extends Error {\n constructor(\n public readonly status: number,\n public readonly responseBody: string,\n ) {\n super(\n `Taurus request failed with status 0x${status.toString(16)}${responseBody ? `: ${responseBody}` : ''}`,\n );\n this.name = 'TaurusResponseError';\n }\n}\n\nexport class TaurusConnection {\n readonly #pending = new Map<number, PendingRequest>();\n #buffer = Buffer.alloc(0);\n #sequence = 0;\n #closed = false;\n\n constructor(\n public readonly stream: Duplex,\n public readonly timeout = 5000,\n ) {\n stream.on('data', (chunk: Buffer | string) => this.#onData(Buffer.from(chunk)));\n stream.once('close', () => this.#close(new Error('Taurus connection closed')));\n stream.once('error', (error: Error) => this.#close(error));\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n\n request(command: TaurusCommand, body?: string | Buffer): Promise<TaurusPacket> {\n if (this.#closed) return Promise.reject(new Error('Taurus connection is closed'));\n const sequence = (this.#sequence += 1) >>> 0;\n const packet = encodeTaurusRequest(sequence, command, body);\n\n return new Promise<TaurusPacket>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#pending.delete(sequence);\n reject(new Error(`Taurus request ${sequence} timed out`));\n }, this.timeout);\n this.#pending.set(sequence, { resolve, reject, timer });\n this.stream.write(packet, (error) => {\n if (!error) return;\n clearTimeout(timer);\n this.#pending.delete(sequence);\n reject(error);\n });\n });\n }\n\n async requestJson<T>(command: TaurusCommand, body?: unknown): Promise<T> {\n const response = await this.request(\n command,\n body === undefined ? undefined : JSON.stringify(body),\n );\n const responseBody = response.body.toString('utf8');\n if (response.res.status !== 0) throw new TaurusResponseError(response.res.status, responseBody);\n return (responseBody ? JSON.parse(responseBody) : undefined) as T;\n }\n\n close(): void {\n if (!this.stream.destroyed) this.stream.destroy();\n this.#close(new Error('Taurus connection closed'));\n }\n\n #onData(chunk: Buffer): void {\n this.#buffer = this.#buffer.length ? Buffer.concat([this.#buffer, chunk]) : chunk;\n while (this.#buffer.length >= TAURUS_HEADER_SIZE) {\n let packetSize: number | undefined;\n try {\n packetSize = getTaurusPacketSize(this.#buffer);\n } catch (error) {\n this.#close(error as Error);\n this.stream.destroy(error as Error);\n return;\n }\n if (packetSize === undefined || this.#buffer.length < packetSize) return;\n const raw = this.#buffer.subarray(0, packetSize);\n this.#buffer = this.#buffer.subarray(packetSize);\n const packet = decodeTaurusPacket(raw);\n if (packet.packetType !== TAURUS_RESPONSE) continue;\n const pending = this.#pending.get(packet.sequence);\n if (!pending) continue;\n clearTimeout(pending.timer);\n this.#pending.delete(packet.sequence);\n pending.resolve(packet);\n }\n }\n\n #close(error: Error): void {\n if (this.#closed) return;\n this.#closed = true;\n for (const pending of this.#pending.values()) {\n clearTimeout(pending.timer);\n pending.reject(error);\n }\n this.#pending.clear();\n }\n}\n","import { randomUUID } from 'crypto';\nimport tls, { ConnectionOptions } from 'tls';\n\nimport { TaurusConnection } from './connection';\n\n// cspell:ignore logined validition\n\nexport const TAURUS_MANAGEMENT_PORT = 16606;\n\nexport interface TaurusConnectOptions extends Omit<ConnectionOptions, 'host' | 'port'> {\n host: string;\n port?: number;\n timeout?: number;\n privacy?: boolean;\n}\n\nexport interface TaurusLoginOptions {\n sn: string;\n password: string;\n username?: string;\n clientId?: string;\n clientName?: string;\n}\n\nexport interface TaurusLoginResult {\n logined: boolean;\n validation?: boolean;\n validition?: boolean;\n username?: string;\n sn?: string;\n}\n\nexport interface TaurusBrightness {\n brightness?: number;\n ratio?: number;\n solidity: boolean;\n orderId: number;\n}\n\nconst numberOrUndefined = (value: unknown): number | undefined =>\n typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n\nexport class TaurusClient {\n private constructor(public readonly connection: TaurusConnection) {}\n\n static async connect(options: TaurusConnectOptions): Promise<TaurusClient> {\n const {\n host,\n port = TAURUS_MANAGEMENT_PORT,\n timeout = 5000,\n privacy = true,\n ...tlsOptions\n } = options;\n if (!privacy) {\n const { connect } = await import('net');\n const socket = connect({ host, port });\n await new Promise<void>((resolve, reject) => {\n socket.once('connect', resolve);\n socket.once('error', reject);\n });\n return new TaurusClient(new TaurusConnection(socket, timeout));\n }\n\n const socket = tls.connect({\n host,\n port,\n rejectUnauthorized: false,\n ...tlsOptions,\n });\n await new Promise<void>((resolve, reject) => {\n socket.once('secureConnect', resolve);\n socket.once('error', reject);\n });\n return new TaurusClient(new TaurusConnection(socket, timeout));\n }\n\n async login(options: TaurusLoginOptions): Promise<TaurusLoginResult> {\n const response = await this.connection.requestJson<\n TaurusLoginResult & { token?: string; password?: string }\n >(\n { what: 0, type: 0, action: 0 },\n {\n sn: options.sn,\n username: options.username ?? 'admin',\n password: options.password,\n loginType: 0,\n source: { type: 1, platform: 2, platformVersion: '3.6.0' },\n clientId: options.clientId ?? randomUUID(),\n clientName: options.clientName ?? 'novastar.js',\n },\n );\n const { token: _token, password: _password, ...safeResponse } = response;\n return safeResponse;\n }\n\n async getBrightness(): Promise<TaurusBrightness> {\n const result = await this.connection.requestJson<Record<string, unknown>>({\n what: 0x18,\n type: 1,\n action: 5,\n });\n return {\n brightness: numberOrUndefined(result.brightness),\n ratio: numberOrUndefined(result.ratio),\n solidity: result.solidity === true,\n orderId: numberOrUndefined(result.orderId) ?? -1,\n };\n }\n\n async setBrightness(percent: number, persist = false): Promise<void> {\n if (!Number.isFinite(percent) || percent < 0 || percent > 100) {\n throw new RangeError('Brightness must be between 0 and 100');\n }\n await this.connection.requestJson<unknown>(\n { what: 0x18, type: 1, action: 4 },\n { ratio: percent, solidity: persist, orderId: -1 },\n );\n }\n\n async getEnvironmentBrightness(): Promise<number> {\n const result = await this.connection.requestJson<{ value?: unknown }>({\n what: 0x1a,\n type: 0,\n action: 5,\n });\n const value = numberOrUndefined(result.value);\n if (value === undefined || value === -1000)\n throw new Error('Taurus ambient light sensor is unavailable');\n return value;\n }\n\n close(): void {\n this.connection.close();\n }\n}\n","import dgram from 'dgram';\nimport { type NetworkInterfaceInfo, networkInterfaces } from 'os';\n\nimport { decodeTaurusPacket, encodeTaurusDiscoveryRequest, TAURUS_DISCOVERY } from './packet';\n\n// cspell:ignore logined sysset\n\nexport const TAURUS_DISCOVERY_PORT = 16601;\n\nexport interface TaurusPlayerInfo {\n address: string;\n aliasName: string;\n encodeType: number;\n height: number;\n modelId: number;\n platform: string;\n privacy: boolean;\n productName: string;\n rotation: number;\n sn: string;\n tcpPort: number;\n width: number;\n ftpPort?: number;\n key?: string;\n logined?: boolean;\n loginedUsernames?: string[];\n syssetFtpPort?: number;\n syssetTcpPort?: number;\n terminalEntrancePortHttps?: number;\n terminalEntrancePortWebSocket?: number;\n}\n\nconst ipv4ToUint32 = (address: string): number =>\n address.split('.').reduce((result, octet) => (result << 8) | Number(octet), 0) >>> 0;\n\nconst getBroadcastAddress = (address: string, netmask: string): string => {\n const broadcast = (ipv4ToUint32(address) | ~ipv4ToUint32(netmask)) >>> 0;\n return [24, 16, 8, 0].map((shift) => (broadcast >>> shift) & 0xff).join('.');\n};\n\nexport const parseTaurusDiscoveryResponse = (\n message: Uint8Array,\n address: string,\n): TaurusPlayerInfo | undefined => {\n try {\n const packet = decodeTaurusPacket(message);\n if (packet.packetType !== TAURUS_DISCOVERY || packet.what !== 1) return undefined;\n const value = JSON.parse(packet.body.toString('utf8')) as Partial<TaurusPlayerInfo>;\n if (typeof value.sn !== 'string' || typeof value.tcpPort !== 'number') return undefined;\n return { ...value, address } as TaurusPlayerInfo;\n } catch {\n return undefined;\n }\n};\n\nconst discoverFromInterface = (\n { address, netmask }: NetworkInterfaceInfo,\n timeout: number,\n destination?: string,\n): Promise<TaurusPlayerInfo[]> =>\n new Promise((resolve) => {\n const socket = dgram.createSocket('udp4');\n const players = new Map<string, TaurusPlayerInfo>();\n let completed = false;\n const complete = (): void => {\n if (completed) return;\n completed = true;\n try {\n socket.close();\n } catch {\n // The socket may already be closed after an error.\n }\n resolve([...players.values()]);\n };\n socket.once('error', complete);\n socket.on('message', (message, remote) => {\n const player = parseTaurusDiscoveryResponse(message, remote.address);\n if (player) players.set(player.sn, player);\n });\n socket.bind(0, address, () => {\n socket.setBroadcast(true);\n const target = destination ?? getBroadcastAddress(address, netmask);\n socket.send(encodeTaurusDiscoveryRequest(), TAURUS_DISCOVERY_PORT, target, (error) => {\n if (error) complete();\n });\n setTimeout(complete, timeout).unref();\n });\n });\n\nexport const discoverTaurusPlayers = async (\n destination?: string,\n timeout = 1000,\n): Promise<TaurusPlayerInfo[]> => {\n const interfaces = Object.values(networkInterfaces())\n .flat()\n .filter((info): info is NetworkInterfaceInfo =>\n Boolean(info && !info.internal && info.family === 'IPv4'),\n );\n const results = await Promise.all(\n interfaces.map((info) => discoverFromInterface(info, timeout, destination)),\n );\n return [\n ...results\n .flat()\n .reduce(\n (players, player) => players.set(player.sn, player),\n new Map<string, TaurusPlayerInfo>(),\n )\n .values(),\n ];\n};\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@novastar/taurus",
3
+ "version": "0.1.0",
4
+ "description": "Client for the NovaStar Taurus multimedia player management protocol",
5
+ "main": "dist/index.cjs",
6
+ "types": "dist/index.d.mts",
7
+ "module": "dist/index.mjs",
8
+ "exports": {
9
+ ".": {
10
+ "require": "./dist/index.cjs",
11
+ "import": "./dist/index.mjs",
12
+ "types": "./dist/index.d.mts"
13
+ },
14
+ "./package.json": "./package.json"
15
+ },
16
+ "license": "MIT",
17
+ "author": "Andrei Sarakeev <sarakusha@gmail.com>",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/sarakusha/novastar.git",
21
+ "directory": "packages/taurus"
22
+ },
23
+ "homepage": "https://sarakusha.github.io/novastar/",
24
+ "keywords": [
25
+ "novastar",
26
+ "taurus",
27
+ "TB60",
28
+ "digital-signage"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsdown",
32
+ "fix": "run-s fix:*",
33
+ "fix:prettier": "prettier \"src/**/*.ts\" --write",
34
+ "fix:lint": "pnpm exec eslint src --fix",
35
+ "test": "run-s build test:*",
36
+ "test:lint": "pnpm exec eslint src",
37
+ "test:prettier": "prettier \"src/**/*.ts\" --list-different",
38
+ "test:spelling": "cspell \"{README.md,src/**/*.ts}\"",
39
+ "test:unit": "jest src",
40
+ "yalc:push": "yalc push",
41
+ "yalc:update": "pnpm build && yalc publish --push"
42
+ },
43
+ "files": [
44
+ "dist",
45
+ "!**/*.spec.*",
46
+ "!**/*.tsbuildinfo",
47
+ "CHANGELOG.md",
48
+ "LICENSE",
49
+ "README.md"
50
+ ],
51
+ "jest": {
52
+ "preset": "ts-jest",
53
+ "testEnvironment": "node",
54
+ "collectCoverage": true,
55
+ "coverageReporters": [
56
+ "lcov",
57
+ "html"
58
+ ]
59
+ },
60
+ "sideEffects": false,
61
+ "publishConfig": {
62
+ "access": "public"
63
+ },
64
+ "dependencies": {
65
+ "typed-struct": "^2.7.3"
66
+ }
67
+ }