lib-maestro-dmx 1.0.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 +21 -0
- package/README.md +729 -0
- package/dist/control/osc-control.d.ts +156 -0
- package/dist/control/osc-control.js +312 -0
- package/dist/control.d.ts +96 -0
- package/dist/control.js +259 -0
- package/dist/http.d.ts +10 -0
- package/dist/http.js +46 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.js +90 -0
- package/dist/internal/normalize.d.ts +10 -0
- package/dist/internal/normalize.js +25 -0
- package/dist/live.d.ts +42 -0
- package/dist/live.js +44 -0
- package/dist/normalize.d.ts +5 -0
- package/dist/normalize.js +20 -0
- package/dist/osc.d.ts +45 -0
- package/dist/osc.js +279 -0
- package/dist/patterns.d.ts +35 -0
- package/dist/patterns.js +69 -0
- package/dist/read/brightness.d.ts +17 -0
- package/dist/read/brightness.js +18 -0
- package/dist/read/color-palettes.d.ts +46 -0
- package/dist/read/color-palettes.js +86 -0
- package/dist/read/fixture-groups.d.ts +31 -0
- package/dist/read/fixture-groups.js +56 -0
- package/dist/read/fx-palettes.d.ts +28 -0
- package/dist/read/fx-palettes.js +38 -0
- package/dist/read/live.d.ts +37 -0
- package/dist/read/live.js +40 -0
- package/dist/read/lookup.d.ts +71 -0
- package/dist/read/lookup.js +91 -0
- package/dist/read/palette-assignments.d.ts +31 -0
- package/dist/read/palette-assignments.js +47 -0
- package/dist/read/patterns-available.d.ts +19 -0
- package/dist/read/patterns-available.js +21 -0
- package/dist/read/patterns.d.ts +48 -0
- package/dist/read/patterns.js +95 -0
- package/dist/read/read-api.d.ts +20 -0
- package/dist/read/read-api.js +30 -0
- package/dist/read/shared.d.ts +45 -0
- package/dist/read/shared.js +65 -0
- package/dist/read/show-state.d.ts +35 -0
- package/dist/read/show-state.js +38 -0
- package/dist/read/show.d.ts +51 -0
- package/dist/read/show.js +62 -0
- package/dist/read/system-info.d.ts +39 -0
- package/dist/read/system-info.js +29 -0
- package/dist/read-api.d.ts +10 -0
- package/dist/read-api.js +18 -0
- package/dist/transport/http.d.ts +19 -0
- package/dist/transport/http.js +86 -0
- package/dist/transport/osc.d.ts +73 -0
- package/dist/transport/osc.js +301 -0
- package/package.json +58 -0
package/dist/osc.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface OscFloatArgument {
|
|
2
|
+
type: "float";
|
|
3
|
+
value: number;
|
|
4
|
+
}
|
|
5
|
+
export type OscArgument = boolean | number | string | OscFloatArgument;
|
|
6
|
+
export interface OscMessage {
|
|
7
|
+
address: string;
|
|
8
|
+
args: OscArgument[];
|
|
9
|
+
}
|
|
10
|
+
export interface OscResponse extends OscMessage {
|
|
11
|
+
remoteAddress: string;
|
|
12
|
+
remotePort: number;
|
|
13
|
+
}
|
|
14
|
+
export interface OscClientOptions {
|
|
15
|
+
host: string;
|
|
16
|
+
port: number;
|
|
17
|
+
localPort?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface OscThrottleOptions {
|
|
20
|
+
intervalMs?: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function oscFloat(value: number): OscFloatArgument;
|
|
23
|
+
export declare function encodeOscMessage(message: OscMessage): Buffer;
|
|
24
|
+
export declare function decodeOscMessage(buffer: Buffer): OscMessage;
|
|
25
|
+
export declare class OscClient {
|
|
26
|
+
private static readonly DEFAULT_THROTTLE_MS;
|
|
27
|
+
private readonly host;
|
|
28
|
+
private readonly port;
|
|
29
|
+
private readonly localPort?;
|
|
30
|
+
private readonly throttledSendStates;
|
|
31
|
+
private socket;
|
|
32
|
+
constructor(options: OscClientOptions);
|
|
33
|
+
get connected(): boolean;
|
|
34
|
+
connect(): Promise<void>;
|
|
35
|
+
close(): Promise<void>;
|
|
36
|
+
send(address: string, ...args: OscArgument[]): Promise<void>;
|
|
37
|
+
sendThrottled(address: string, argsOrOptions: OscArgument | OscThrottleOptions, ...remainingArgs: OscArgument[]): Promise<void>;
|
|
38
|
+
probe(address: string, args?: OscArgument[], timeoutMs?: number): Promise<OscResponse[]>;
|
|
39
|
+
private getSocket;
|
|
40
|
+
private getThrottledSendState;
|
|
41
|
+
private pruneThrottledSendState;
|
|
42
|
+
private clearThrottledSendStates;
|
|
43
|
+
private normalizeThrottledSendArguments;
|
|
44
|
+
private isOscThrottleOptions;
|
|
45
|
+
}
|
package/dist/osc.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import dgram from "node:dgram";
|
|
2
|
+
function padToFourBytes(length) {
|
|
3
|
+
const remainder = length % 4;
|
|
4
|
+
return remainder === 0 ? length : length + (4 - remainder);
|
|
5
|
+
}
|
|
6
|
+
function encodeOscString(value) {
|
|
7
|
+
const raw = Buffer.from(value, "utf8");
|
|
8
|
+
const size = padToFourBytes(raw.length + 1);
|
|
9
|
+
const result = Buffer.alloc(size);
|
|
10
|
+
raw.copy(result, 0);
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
function decodeOscString(buffer, offset) {
|
|
14
|
+
const end = buffer.indexOf(0, offset);
|
|
15
|
+
if (end === -1) {
|
|
16
|
+
throw new Error("Invalid OSC string: missing null terminator.");
|
|
17
|
+
}
|
|
18
|
+
const value = buffer.toString("utf8", offset, end);
|
|
19
|
+
return {
|
|
20
|
+
value,
|
|
21
|
+
nextOffset: padToFourBytes(end + 1)
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function encodeOscArgument(argument) {
|
|
25
|
+
if (isOscFloatArgument(argument)) {
|
|
26
|
+
const payload = Buffer.alloc(4);
|
|
27
|
+
payload.writeFloatBE(argument.value, 0);
|
|
28
|
+
return { typeTag: "f", payload };
|
|
29
|
+
}
|
|
30
|
+
if (typeof argument === "string") {
|
|
31
|
+
return {
|
|
32
|
+
typeTag: "s",
|
|
33
|
+
payload: encodeOscString(argument)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (typeof argument === "number") {
|
|
37
|
+
if (Number.isInteger(argument)) {
|
|
38
|
+
const payload = Buffer.alloc(4);
|
|
39
|
+
payload.writeInt32BE(argument, 0);
|
|
40
|
+
return { typeTag: "i", payload };
|
|
41
|
+
}
|
|
42
|
+
const payload = Buffer.alloc(4);
|
|
43
|
+
payload.writeFloatBE(argument, 0);
|
|
44
|
+
return { typeTag: "f", payload };
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
typeTag: argument ? "T" : "F",
|
|
48
|
+
payload: Buffer.alloc(0)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function isOscFloatArgument(argument) {
|
|
52
|
+
return typeof argument === "object" && argument !== null && argument.type === "float";
|
|
53
|
+
}
|
|
54
|
+
export function oscFloat(value) {
|
|
55
|
+
return {
|
|
56
|
+
type: "float",
|
|
57
|
+
value
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function encodeOscMessage(message) {
|
|
61
|
+
const address = encodeOscString(message.address);
|
|
62
|
+
const encodedArguments = message.args.map(encodeOscArgument);
|
|
63
|
+
const typeTagString = `,${encodedArguments.map((argument) => argument.typeTag).join("")}`;
|
|
64
|
+
const typeTags = encodeOscString(typeTagString);
|
|
65
|
+
const payload = encodedArguments.map((argument) => argument.payload);
|
|
66
|
+
return Buffer.concat([address, typeTags, ...payload]);
|
|
67
|
+
}
|
|
68
|
+
export function decodeOscMessage(buffer) {
|
|
69
|
+
let offset = 0;
|
|
70
|
+
const address = decodeOscString(buffer, offset);
|
|
71
|
+
offset = address.nextOffset;
|
|
72
|
+
const typeTags = decodeOscString(buffer, offset);
|
|
73
|
+
offset = typeTags.nextOffset;
|
|
74
|
+
if (!typeTags.value.startsWith(",")) {
|
|
75
|
+
throw new Error("Invalid OSC type tag string.");
|
|
76
|
+
}
|
|
77
|
+
const args = [];
|
|
78
|
+
for (const typeTag of typeTags.value.slice(1)) {
|
|
79
|
+
switch (typeTag) {
|
|
80
|
+
case "i":
|
|
81
|
+
args.push(buffer.readInt32BE(offset));
|
|
82
|
+
offset += 4;
|
|
83
|
+
break;
|
|
84
|
+
case "f":
|
|
85
|
+
args.push(buffer.readFloatBE(offset));
|
|
86
|
+
offset += 4;
|
|
87
|
+
break;
|
|
88
|
+
case "s": {
|
|
89
|
+
const value = decodeOscString(buffer, offset);
|
|
90
|
+
args.push(value.value);
|
|
91
|
+
offset = value.nextOffset;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case "T":
|
|
95
|
+
args.push(true);
|
|
96
|
+
break;
|
|
97
|
+
case "F":
|
|
98
|
+
args.push(false);
|
|
99
|
+
break;
|
|
100
|
+
default:
|
|
101
|
+
throw new Error(`Unsupported OSC type tag "${typeTag}".`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
address: address.value,
|
|
106
|
+
args
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export class OscClient {
|
|
110
|
+
static DEFAULT_THROTTLE_MS = 250;
|
|
111
|
+
host;
|
|
112
|
+
port;
|
|
113
|
+
localPort;
|
|
114
|
+
throttledSendStates = new Map();
|
|
115
|
+
socket;
|
|
116
|
+
constructor(options) {
|
|
117
|
+
this.host = options.host;
|
|
118
|
+
this.port = options.port;
|
|
119
|
+
this.localPort = options.localPort;
|
|
120
|
+
}
|
|
121
|
+
get connected() {
|
|
122
|
+
return this.socket !== undefined;
|
|
123
|
+
}
|
|
124
|
+
async connect() {
|
|
125
|
+
if (this.socket) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const socket = dgram.createSocket("udp4");
|
|
129
|
+
await new Promise((resolve, reject) => {
|
|
130
|
+
socket.once("error", reject);
|
|
131
|
+
socket.bind(this.localPort ?? 0, () => {
|
|
132
|
+
socket.off("error", reject);
|
|
133
|
+
resolve();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
this.socket = socket;
|
|
137
|
+
}
|
|
138
|
+
async close() {
|
|
139
|
+
this.clearThrottledSendStates();
|
|
140
|
+
if (!this.socket) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const socket = this.socket;
|
|
144
|
+
this.socket = undefined;
|
|
145
|
+
await new Promise((resolve, reject) => {
|
|
146
|
+
const onClose = () => {
|
|
147
|
+
socket.off("error", onError);
|
|
148
|
+
resolve();
|
|
149
|
+
};
|
|
150
|
+
const onError = (error) => {
|
|
151
|
+
socket.off("close", onClose);
|
|
152
|
+
reject(error);
|
|
153
|
+
};
|
|
154
|
+
socket.once("close", onClose);
|
|
155
|
+
socket.once("error", onError);
|
|
156
|
+
socket.close();
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async send(address, ...args) {
|
|
160
|
+
const socket = await this.getSocket();
|
|
161
|
+
const message = encodeOscMessage({ address, args });
|
|
162
|
+
await new Promise((resolve, reject) => {
|
|
163
|
+
socket.send(message, this.port, this.host, (error) => {
|
|
164
|
+
if (error) {
|
|
165
|
+
reject(error);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
resolve();
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
async sendThrottled(address, argsOrOptions, ...remainingArgs) {
|
|
173
|
+
const { args, intervalMs } = this.normalizeThrottledSendArguments(argsOrOptions, remainingArgs);
|
|
174
|
+
const state = this.getThrottledSendState(address);
|
|
175
|
+
const now = Date.now();
|
|
176
|
+
if (state.lastSentAt === undefined || now - state.lastSentAt >= intervalMs) {
|
|
177
|
+
state.lastSentAt = now;
|
|
178
|
+
void this.send(address, ...args).catch(() => { });
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
state.pendingArgs = args;
|
|
182
|
+
if (state.timer) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const delayMs = Math.max(intervalMs - (now - state.lastSentAt), 0);
|
|
186
|
+
state.timer = setTimeout(() => {
|
|
187
|
+
state.timer = undefined;
|
|
188
|
+
const pendingArgs = state.pendingArgs;
|
|
189
|
+
state.pendingArgs = undefined;
|
|
190
|
+
if (!pendingArgs) {
|
|
191
|
+
this.pruneThrottledSendState(address, state);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
state.lastSentAt = Date.now();
|
|
195
|
+
void this.send(address, ...pendingArgs)
|
|
196
|
+
.catch(() => { })
|
|
197
|
+
.finally(() => {
|
|
198
|
+
this.pruneThrottledSendState(address, state);
|
|
199
|
+
});
|
|
200
|
+
}, delayMs);
|
|
201
|
+
}
|
|
202
|
+
async probe(address, args = [], timeoutMs = 1000) {
|
|
203
|
+
const socket = await this.getSocket();
|
|
204
|
+
const responses = [];
|
|
205
|
+
const onMessage = (buffer, remote) => {
|
|
206
|
+
try {
|
|
207
|
+
const message = decodeOscMessage(buffer);
|
|
208
|
+
responses.push({
|
|
209
|
+
...message,
|
|
210
|
+
remoteAddress: remote.address,
|
|
211
|
+
remotePort: remote.port
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Ignore packets that are not decodable as plain OSC messages.
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
socket.on("message", onMessage);
|
|
219
|
+
try {
|
|
220
|
+
await this.send(address, ...args);
|
|
221
|
+
await new Promise((resolve) => {
|
|
222
|
+
setTimeout(resolve, timeoutMs);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
socket.off("message", onMessage);
|
|
227
|
+
}
|
|
228
|
+
return responses;
|
|
229
|
+
}
|
|
230
|
+
async getSocket() {
|
|
231
|
+
await this.connect();
|
|
232
|
+
if (!this.socket) {
|
|
233
|
+
throw new Error("OSC socket is not available.");
|
|
234
|
+
}
|
|
235
|
+
return this.socket;
|
|
236
|
+
}
|
|
237
|
+
getThrottledSendState(address) {
|
|
238
|
+
const existingState = this.throttledSendStates.get(address);
|
|
239
|
+
if (existingState) {
|
|
240
|
+
return existingState;
|
|
241
|
+
}
|
|
242
|
+
const newState = {
|
|
243
|
+
lastSentAt: undefined,
|
|
244
|
+
pendingArgs: undefined,
|
|
245
|
+
timer: undefined
|
|
246
|
+
};
|
|
247
|
+
this.throttledSendStates.set(address, newState);
|
|
248
|
+
return newState;
|
|
249
|
+
}
|
|
250
|
+
pruneThrottledSendState(address, state) {
|
|
251
|
+
if (state.timer || state.pendingArgs) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
this.throttledSendStates.delete(address);
|
|
255
|
+
}
|
|
256
|
+
clearThrottledSendStates() {
|
|
257
|
+
for (const state of this.throttledSendStates.values()) {
|
|
258
|
+
if (state.timer) {
|
|
259
|
+
clearTimeout(state.timer);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
this.throttledSendStates.clear();
|
|
263
|
+
}
|
|
264
|
+
normalizeThrottledSendArguments(argsOrOptions, remainingArgs) {
|
|
265
|
+
if (this.isOscThrottleOptions(argsOrOptions)) {
|
|
266
|
+
return {
|
|
267
|
+
args: remainingArgs,
|
|
268
|
+
intervalMs: argsOrOptions.intervalMs ?? OscClient.DEFAULT_THROTTLE_MS
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
args: [argsOrOptions, ...remainingArgs],
|
|
273
|
+
intervalMs: OscClient.DEFAULT_THROTTLE_MS
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
isOscThrottleOptions(value) {
|
|
277
|
+
return typeof value === "object" && value !== null && "intervalMs" in value;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { HttpClient } from "./http.js";
|
|
2
|
+
import { ReadApi } from "./read-api.js";
|
|
3
|
+
export interface PatternParameter {
|
|
4
|
+
param: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
shape?: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface PatternDefinition {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
params: PatternParameter[];
|
|
14
|
+
advancedParams: PatternParameter[];
|
|
15
|
+
}
|
|
16
|
+
export interface PatternManifest {
|
|
17
|
+
interfaceVersion?: string;
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
patterns: PatternDefinition[];
|
|
22
|
+
}
|
|
23
|
+
export interface PatternsState {
|
|
24
|
+
manifests: PatternManifest[];
|
|
25
|
+
fetchedAt: Date;
|
|
26
|
+
}
|
|
27
|
+
interface PatternsApiResponse {
|
|
28
|
+
manifests?: unknown;
|
|
29
|
+
}
|
|
30
|
+
export declare class PatternsApi extends ReadApi<PatternsState, PatternsApiResponse> {
|
|
31
|
+
constructor(httpClient: HttpClient);
|
|
32
|
+
protected getPath(): string;
|
|
33
|
+
protected normalizeResponse(response: PatternsApiResponse, fetchedAt: Date): PatternsState;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
package/dist/patterns.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { HttpClient } from "./http.js";
|
|
2
|
+
import { isRecord, normalizeArray, toOptionalString } from "./normalize.js";
|
|
3
|
+
import { ReadApi } from "./read-api.js";
|
|
4
|
+
function normalizePatternParameter(value) {
|
|
5
|
+
if (!isRecord(value)) {
|
|
6
|
+
return undefined;
|
|
7
|
+
}
|
|
8
|
+
const param = toOptionalString(value.param);
|
|
9
|
+
if (!param) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
const shape = Array.isArray(value.shape)
|
|
13
|
+
? value.shape.filter((entry) => typeof entry === "string")
|
|
14
|
+
: undefined;
|
|
15
|
+
return {
|
|
16
|
+
param,
|
|
17
|
+
name: toOptionalString(value.name),
|
|
18
|
+
description: toOptionalString(value.description),
|
|
19
|
+
shape: shape?.length ? shape : undefined
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function normalizePatternDefinition(value) {
|
|
23
|
+
if (!isRecord(value)) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const id = toOptionalString(value.id);
|
|
27
|
+
const name = toOptionalString(value.name);
|
|
28
|
+
if (!id || !name) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
id,
|
|
33
|
+
name,
|
|
34
|
+
description: toOptionalString(value.description),
|
|
35
|
+
params: normalizeArray(value.params, normalizePatternParameter),
|
|
36
|
+
advancedParams: normalizeArray(value.advancedParams, normalizePatternParameter)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function normalizePatternManifest(value) {
|
|
40
|
+
if (!isRecord(value)) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
const id = toOptionalString(value.id);
|
|
44
|
+
const name = toOptionalString(value.name);
|
|
45
|
+
if (!id || !name) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
interfaceVersion: toOptionalString(value.interfaceVersion),
|
|
50
|
+
id,
|
|
51
|
+
name,
|
|
52
|
+
description: toOptionalString(value.description),
|
|
53
|
+
patterns: normalizeArray(value.patterns, normalizePatternDefinition)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export class PatternsApi extends ReadApi {
|
|
57
|
+
constructor(httpClient) {
|
|
58
|
+
super(httpClient);
|
|
59
|
+
}
|
|
60
|
+
getPath() {
|
|
61
|
+
return "/api/v1/patterns";
|
|
62
|
+
}
|
|
63
|
+
normalizeResponse(response, fetchedAt) {
|
|
64
|
+
return {
|
|
65
|
+
manifests: normalizeArray(response.manifests, normalizePatternManifest),
|
|
66
|
+
fetchedAt
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { HttpClient } from "../transport/http.js";
|
|
2
|
+
import { ReadApi } from "./read-api.js";
|
|
3
|
+
/** Snapshot of the global MaestroDMX stage brightness. */
|
|
4
|
+
export interface BrightnessState {
|
|
5
|
+
value?: number;
|
|
6
|
+
fetchedAt: Date;
|
|
7
|
+
}
|
|
8
|
+
interface BrightnessApiResponse {
|
|
9
|
+
value?: unknown;
|
|
10
|
+
}
|
|
11
|
+
/** Reader for `/api/v1/brightness`. */
|
|
12
|
+
export declare class BrightnessApi extends ReadApi<BrightnessState, BrightnessApiResponse> {
|
|
13
|
+
constructor(httpClient: HttpClient);
|
|
14
|
+
protected getPath(): string;
|
|
15
|
+
protected normalizeResponse(response: BrightnessApiResponse, fetchedAt: Date): BrightnessState;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { toOptionalNumber } from "../internal/normalize.js";
|
|
2
|
+
import { HttpClient } from "../transport/http.js";
|
|
3
|
+
import { ReadApi } from "./read-api.js";
|
|
4
|
+
/** Reader for `/api/v1/brightness`. */
|
|
5
|
+
export class BrightnessApi extends ReadApi {
|
|
6
|
+
constructor(httpClient) {
|
|
7
|
+
super(httpClient);
|
|
8
|
+
}
|
|
9
|
+
getPath() {
|
|
10
|
+
return "/api/v1/brightness";
|
|
11
|
+
}
|
|
12
|
+
normalizeResponse(response, fetchedAt) {
|
|
13
|
+
return {
|
|
14
|
+
value: toOptionalNumber(response.value),
|
|
15
|
+
fetchedAt
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { HttpClient } from "../transport/http.js";
|
|
2
|
+
import { ReadApi } from "./read-api.js";
|
|
3
|
+
/** One RGBWAUV-style color stop in an extended MaestroDMX palette. */
|
|
4
|
+
export interface PaletteColor {
|
|
5
|
+
r?: number;
|
|
6
|
+
g?: number;
|
|
7
|
+
b?: number;
|
|
8
|
+
w?: number;
|
|
9
|
+
am?: number;
|
|
10
|
+
u?: number;
|
|
11
|
+
}
|
|
12
|
+
/** One color palette definition returned by MaestroDMX. */
|
|
13
|
+
export interface ColorPalette {
|
|
14
|
+
paletteId: string;
|
|
15
|
+
name: string;
|
|
16
|
+
type?: string;
|
|
17
|
+
children: string[];
|
|
18
|
+
hexColors: string[];
|
|
19
|
+
colors: PaletteColor[];
|
|
20
|
+
readOnly?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** Snapshot of the MaestroDMX color palette catalog. */
|
|
23
|
+
export interface ColorPalettesState {
|
|
24
|
+
palettes: ColorPalette[];
|
|
25
|
+
fetchedAt: Date;
|
|
26
|
+
}
|
|
27
|
+
interface ColorPalettesApiResponse {
|
|
28
|
+
palettes?: unknown;
|
|
29
|
+
}
|
|
30
|
+
/** Reader for `/api/v1/palettes/color`. */
|
|
31
|
+
export declare class ColorPalettesApi extends ReadApi<ColorPalettesState, ColorPalettesApiResponse> {
|
|
32
|
+
constructor(httpClient: HttpClient);
|
|
33
|
+
protected getPath(): string;
|
|
34
|
+
protected normalizeResponse(response: ColorPalettesApiResponse, fetchedAt: Date): ColorPalettesState;
|
|
35
|
+
/** Finds one palette by palette id. */
|
|
36
|
+
findById(paletteId: string, snapshot?: ColorPalettesState): ColorPalette | undefined;
|
|
37
|
+
/** Finds one palette by its display name. */
|
|
38
|
+
findByName(name: string, snapshot?: ColorPalettesState): ColorPalette | undefined;
|
|
39
|
+
/** Returns grouped palettes only. */
|
|
40
|
+
listGroups(snapshot?: ColorPalettesState): ColorPalette[];
|
|
41
|
+
/** Returns individual palettes only. */
|
|
42
|
+
listLeafPalettes(snapshot?: ColorPalettesState): ColorPalette[];
|
|
43
|
+
/** Resolves the child palettes referenced by a grouped palette. */
|
|
44
|
+
resolveChildren(paletteId: string, snapshot?: ColorPalettesState): ColorPalette[];
|
|
45
|
+
}
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { isRecord, normalizeArray, toOptionalBoolean, toOptionalNumber, toOptionalString } from "../internal/normalize.js";
|
|
2
|
+
import { HttpClient } from "../transport/http.js";
|
|
3
|
+
import { ReadApi } from "./read-api.js";
|
|
4
|
+
function normalizePaletteColor(value) {
|
|
5
|
+
if (!isRecord(value)) {
|
|
6
|
+
return undefined;
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
r: toOptionalNumber(value.r),
|
|
10
|
+
g: toOptionalNumber(value.g),
|
|
11
|
+
b: toOptionalNumber(value.b),
|
|
12
|
+
w: toOptionalNumber(value.w),
|
|
13
|
+
am: toOptionalNumber(value.am),
|
|
14
|
+
u: toOptionalNumber(value.u)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function normalizeColorPalette(value) {
|
|
18
|
+
if (!isRecord(value)) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
const paletteId = toOptionalString(value.paletteId);
|
|
22
|
+
const name = toOptionalString(value.name);
|
|
23
|
+
if (!paletteId || !name) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const hexColors = Array.isArray(value.hexColors)
|
|
27
|
+
? value.hexColors
|
|
28
|
+
.filter((entry) => typeof entry === "string" || typeof entry === "number")
|
|
29
|
+
.map((entry) => String(entry))
|
|
30
|
+
: [];
|
|
31
|
+
const children = Array.isArray(value.children)
|
|
32
|
+
? value.children
|
|
33
|
+
.filter((entry) => typeof entry === "string" || typeof entry === "number")
|
|
34
|
+
.map((entry) => String(entry))
|
|
35
|
+
: [];
|
|
36
|
+
return {
|
|
37
|
+
paletteId,
|
|
38
|
+
name,
|
|
39
|
+
type: toOptionalString(value.type),
|
|
40
|
+
children,
|
|
41
|
+
hexColors,
|
|
42
|
+
colors: normalizeArray(value.colors, normalizePaletteColor),
|
|
43
|
+
readOnly: toOptionalBoolean(value.readOnly)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Reader for `/api/v1/palettes/color`. */
|
|
47
|
+
export class ColorPalettesApi extends ReadApi {
|
|
48
|
+
constructor(httpClient) {
|
|
49
|
+
super(httpClient);
|
|
50
|
+
}
|
|
51
|
+
getPath() {
|
|
52
|
+
return "/api/v1/palettes/color";
|
|
53
|
+
}
|
|
54
|
+
normalizeResponse(response, fetchedAt) {
|
|
55
|
+
return {
|
|
56
|
+
palettes: normalizeArray(response.palettes, normalizeColorPalette),
|
|
57
|
+
fetchedAt
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** Finds one palette by palette id. */
|
|
61
|
+
findById(paletteId, snapshot) {
|
|
62
|
+
return this.resolveSnapshot(snapshot)?.palettes.find((palette) => palette.paletteId === paletteId);
|
|
63
|
+
}
|
|
64
|
+
/** Finds one palette by its display name. */
|
|
65
|
+
findByName(name, snapshot) {
|
|
66
|
+
return this.resolveSnapshot(snapshot)?.palettes.find((palette) => palette.name === name);
|
|
67
|
+
}
|
|
68
|
+
/** Returns grouped palettes only. */
|
|
69
|
+
listGroups(snapshot) {
|
|
70
|
+
return this.resolveSnapshot(snapshot)?.palettes.filter((palette) => palette.type === "GROUP") ?? [];
|
|
71
|
+
}
|
|
72
|
+
/** Returns individual palettes only. */
|
|
73
|
+
listLeafPalettes(snapshot) {
|
|
74
|
+
return this.resolveSnapshot(snapshot)?.palettes.filter((palette) => palette.type !== "GROUP") ?? [];
|
|
75
|
+
}
|
|
76
|
+
/** Resolves the child palettes referenced by a grouped palette. */
|
|
77
|
+
resolveChildren(paletteId, snapshot) {
|
|
78
|
+
const palette = this.findById(paletteId, snapshot);
|
|
79
|
+
if (!palette || palette.type !== "GROUP") {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
return palette.children
|
|
83
|
+
.map((childId) => this.findById(childId, snapshot))
|
|
84
|
+
.filter((child) => child !== undefined);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { HttpClient } from "../transport/http.js";
|
|
2
|
+
import { ReadApi } from "./read-api.js";
|
|
3
|
+
/** Runtime fixture-group metadata for one MaestroDMX group. */
|
|
4
|
+
export interface FixtureGroup {
|
|
5
|
+
id: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
paramGroup?: string;
|
|
8
|
+
fixtureIds: string[];
|
|
9
|
+
mappingConfig: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
/** Snapshot of the stage fixture-group topology used during runtime. */
|
|
12
|
+
export interface FixtureGroupsState {
|
|
13
|
+
groups: FixtureGroup[];
|
|
14
|
+
fetchedAt: Date;
|
|
15
|
+
}
|
|
16
|
+
interface FixtureGroupsApiResponse {
|
|
17
|
+
fixtureGroup?: unknown;
|
|
18
|
+
}
|
|
19
|
+
/** Reader for `/api/v1/fixture_groups`. */
|
|
20
|
+
export declare class FixtureGroupsApi extends ReadApi<FixtureGroupsState, FixtureGroupsApiResponse> {
|
|
21
|
+
constructor(httpClient: HttpClient);
|
|
22
|
+
protected getPath(): string;
|
|
23
|
+
protected normalizeResponse(response: FixtureGroupsApiResponse, fetchedAt: Date): FixtureGroupsState;
|
|
24
|
+
/** Finds a fixture group by internal group id. */
|
|
25
|
+
getGroupById(id: string, snapshot?: FixtureGroupsState): FixtureGroup | undefined;
|
|
26
|
+
/** Finds a fixture group by MaestroDMX param-group name or numeric index. */
|
|
27
|
+
getGroupByParamGroup(paramGroup: string | number, snapshot?: FixtureGroupsState): FixtureGroup | undefined;
|
|
28
|
+
/** Returns all fixture ids associated with one fixture group. */
|
|
29
|
+
listFixtureIds(group: string | number, snapshot?: FixtureGroupsState): string[];
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { isRecord, normalizeArray, toOptionalString } from "../internal/normalize.js";
|
|
2
|
+
import { HttpClient } from "../transport/http.js";
|
|
3
|
+
import { ReadApi } from "./read-api.js";
|
|
4
|
+
import { normalizeParamGroup } from "./shared.js";
|
|
5
|
+
function normalizeFixtureGroup(value) {
|
|
6
|
+
if (!isRecord(value)) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const id = toOptionalString(value.id);
|
|
10
|
+
if (!id) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
id,
|
|
15
|
+
name: toOptionalString(value.name),
|
|
16
|
+
paramGroup: toOptionalString(value.paramGroup),
|
|
17
|
+
fixtureIds: Array.isArray(value.fixtureId)
|
|
18
|
+
? value.fixtureId.filter((entry) => typeof entry === "string")
|
|
19
|
+
: [],
|
|
20
|
+
mappingConfig: isRecord(value.mappingConfig) ? value.mappingConfig : {}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Reader for `/api/v1/fixture_groups`. */
|
|
24
|
+
export class FixtureGroupsApi extends ReadApi {
|
|
25
|
+
constructor(httpClient) {
|
|
26
|
+
super(httpClient);
|
|
27
|
+
}
|
|
28
|
+
getPath() {
|
|
29
|
+
return "/api/v1/fixture_groups";
|
|
30
|
+
}
|
|
31
|
+
normalizeResponse(response, fetchedAt) {
|
|
32
|
+
return {
|
|
33
|
+
groups: normalizeArray(response.fixtureGroup, normalizeFixtureGroup),
|
|
34
|
+
fetchedAt
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** Finds a fixture group by internal group id. */
|
|
38
|
+
getGroupById(id, snapshot) {
|
|
39
|
+
return this.resolveSnapshot(snapshot)?.groups.find((group) => group.id === id);
|
|
40
|
+
}
|
|
41
|
+
/** Finds a fixture group by MaestroDMX param-group name or numeric index. */
|
|
42
|
+
getGroupByParamGroup(paramGroup, snapshot) {
|
|
43
|
+
const resolvedSnapshot = this.resolveSnapshot(snapshot);
|
|
44
|
+
const normalizedParamGroup = normalizeParamGroup(paramGroup);
|
|
45
|
+
if (!resolvedSnapshot || !normalizedParamGroup) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
return resolvedSnapshot.groups.find((group) => group.paramGroup === normalizedParamGroup);
|
|
49
|
+
}
|
|
50
|
+
/** Returns all fixture ids associated with one fixture group. */
|
|
51
|
+
listFixtureIds(group, snapshot) {
|
|
52
|
+
return this.getGroupByParamGroup(group, snapshot)?.fixtureIds
|
|
53
|
+
?? this.getGroupById(typeof group === "string" ? group : "", snapshot)?.fixtureIds
|
|
54
|
+
?? [];
|
|
55
|
+
}
|
|
56
|
+
}
|