@raphiiko/wavelink-ts 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/PROTOCOL.md +1126 -0
- package/README.md +348 -0
- package/dist/client.d.ts +155 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +462 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +187 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/websocket-adapter.d.ts +33 -0
- package/dist/websocket-adapter.d.ts.map +1 -0
- package/dist/websocket-adapter.js +64 -0
- package/dist/websocket-adapter.js.map +1 -0
- package/package.json +58 -0
package/PROTOCOL.md
ADDED
|
@@ -0,0 +1,1126 @@
|
|
|
1
|
+
# Elgato Wave Link 3.0 Protocol Documentation
|
|
2
|
+
|
|
3
|
+
This document describes the protocol for Wave Link 3.0's RPC.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Wave Link 3.0 uses JSON-RPC 2.0 over WebSocket for all communication.
|
|
8
|
+
|
|
9
|
+
**Connection Details:**
|
|
10
|
+
|
|
11
|
+
- **Protocol**: JSON-RPC 2.0 over WebSocket
|
|
12
|
+
- **Host**: `ws://127.0.0.1`
|
|
13
|
+
- **Port**: 1884 (default, with fallback to ports 1885-1893)
|
|
14
|
+
- **Origin Header**: `streamdeck://`
|
|
15
|
+
- **Authentication**: None required
|
|
16
|
+
|
|
17
|
+
## JSON-RPC 2.0 Format
|
|
18
|
+
|
|
19
|
+
### Request Structure
|
|
20
|
+
|
|
21
|
+
All requests follow the JSON-RPC 2.0 specification:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"id": 1,
|
|
26
|
+
"jsonrpc": "2.0",
|
|
27
|
+
"method": "methodName",
|
|
28
|
+
"params": {}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- `id`: Incrementing number for tracking requests/responses
|
|
33
|
+
- `jsonrpc`: Always `"2.0"`
|
|
34
|
+
- `method`: The RPC method name
|
|
35
|
+
- `params`: Method parameters (can be `null` or `{}` for methods with no parameters)
|
|
36
|
+
|
|
37
|
+
### Response Structure
|
|
38
|
+
|
|
39
|
+
Successful responses:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"jsonrpc": "2.0",
|
|
44
|
+
"id": 1,
|
|
45
|
+
"result": {}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Error responses:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"jsonrpc": "2.0",
|
|
54
|
+
"id": 1,
|
|
55
|
+
"error": {
|
|
56
|
+
"code": -32600,
|
|
57
|
+
"message": "Error description",
|
|
58
|
+
"data": {}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Notification Structure
|
|
64
|
+
|
|
65
|
+
Notifications are server-initiated messages (no `id` field):
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"jsonrpc": "2.0",
|
|
70
|
+
"method": "notificationMethod",
|
|
71
|
+
"params": {}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## RPC Methods
|
|
76
|
+
|
|
77
|
+
These methods are sent from the client to Wave Link.
|
|
78
|
+
|
|
79
|
+
### `getApplicationInfo`
|
|
80
|
+
|
|
81
|
+
Get application information and verify connection.
|
|
82
|
+
|
|
83
|
+
**Request:**
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"id": 1,
|
|
88
|
+
"jsonrpc": "2.0",
|
|
89
|
+
"method": "getApplicationInfo",
|
|
90
|
+
"params": null
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Response:**
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"jsonrpc": "2.0",
|
|
99
|
+
"id": 1,
|
|
100
|
+
"result": {
|
|
101
|
+
"appID": "EWL",
|
|
102
|
+
"name": "Elgato Wave Link",
|
|
103
|
+
"interfaceRevision": 1
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Notes:**
|
|
109
|
+
|
|
110
|
+
- `interfaceRevision` must be >= 1 for compatibility
|
|
111
|
+
- Use this method first to verify connection
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
### `getInputDevices`
|
|
116
|
+
|
|
117
|
+
Get all audio input devices and their properties.
|
|
118
|
+
|
|
119
|
+
**Request:**
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"id": 2,
|
|
124
|
+
"jsonrpc": "2.0",
|
|
125
|
+
"method": "getInputDevices",
|
|
126
|
+
"params": null
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Response:**
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"jsonrpc": "2.0",
|
|
135
|
+
"id": 2,
|
|
136
|
+
"result": {
|
|
137
|
+
"inputDevices": [
|
|
138
|
+
{
|
|
139
|
+
"id": "wave3_usb_microphone",
|
|
140
|
+
"isWaveDevice": true,
|
|
141
|
+
"inputs": [
|
|
142
|
+
{
|
|
143
|
+
"id": "wave3_input_1",
|
|
144
|
+
"isMuted": false,
|
|
145
|
+
"gain": {
|
|
146
|
+
"value": 0.5,
|
|
147
|
+
"maxRange": 100
|
|
148
|
+
},
|
|
149
|
+
"micPcMix": {
|
|
150
|
+
"value": 0.5
|
|
151
|
+
},
|
|
152
|
+
"effects": [
|
|
153
|
+
{
|
|
154
|
+
"id": "clipguard",
|
|
155
|
+
"isEnabled": true
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Field Descriptions:**
|
|
167
|
+
|
|
168
|
+
- `id`: Unique device identifier
|
|
169
|
+
- `isWaveDevice`: Whether this is an Elgato Wave device (has special features)
|
|
170
|
+
- `inputs[]`: Array of input channels on this device
|
|
171
|
+
- `isMuted`: Whether input is muted
|
|
172
|
+
- `gain.value`: Gain level (0.0-1.0, normalized by maxRange)
|
|
173
|
+
- `gain.maxRange`: Maximum gain range for the device
|
|
174
|
+
- `micPcMix.value`: Mix between microphone and PC audio (0.0-1.0)
|
|
175
|
+
- `effects[]`: Available effects and their states (Wave devices only)
|
|
176
|
+
- `dspEffects[]`: Alternative effects array (some devices)
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
### `getOutputDevices`
|
|
181
|
+
|
|
182
|
+
Get all audio output devices and their properties.
|
|
183
|
+
|
|
184
|
+
**Request:**
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"id": 3,
|
|
189
|
+
"jsonrpc": "2.0",
|
|
190
|
+
"method": "getOutputDevices",
|
|
191
|
+
"params": null
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Response:**
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
{
|
|
199
|
+
"jsonrpc": "2.0",
|
|
200
|
+
"id": 3,
|
|
201
|
+
"result": {
|
|
202
|
+
"mainOutput": "speakers_main",
|
|
203
|
+
"outputDevices": [
|
|
204
|
+
{
|
|
205
|
+
"id": "speakers_main",
|
|
206
|
+
"outputs": [
|
|
207
|
+
{
|
|
208
|
+
"id": "output_1",
|
|
209
|
+
"level": 0.8,
|
|
210
|
+
"isMuted": false,
|
|
211
|
+
"mixId": "stream_mix"
|
|
212
|
+
}
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Field Descriptions:**
|
|
221
|
+
|
|
222
|
+
- `mainOutput`: ID of the main output device
|
|
223
|
+
- `outputDevices[]`: Array of output devices
|
|
224
|
+
- `outputs[]`: Output channels on this device
|
|
225
|
+
- `level`: Output volume (0.0-1.0)
|
|
226
|
+
- `isMuted`: Whether output is muted
|
|
227
|
+
- `mixId`: Currently selected mix for this output
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
### `getChannels`
|
|
232
|
+
|
|
233
|
+
Get all channels (audio sources) in the mixer.
|
|
234
|
+
|
|
235
|
+
**Request:**
|
|
236
|
+
|
|
237
|
+
```json
|
|
238
|
+
{
|
|
239
|
+
"id": 4,
|
|
240
|
+
"jsonrpc": "2.0",
|
|
241
|
+
"method": "getChannels",
|
|
242
|
+
"params": null
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Response:**
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"jsonrpc": "2.0",
|
|
251
|
+
"id": 4,
|
|
252
|
+
"result": {
|
|
253
|
+
"channels": [
|
|
254
|
+
{
|
|
255
|
+
"id": "spotify",
|
|
256
|
+
"type": "Software",
|
|
257
|
+
"isMuted": false,
|
|
258
|
+
"level": 0.75,
|
|
259
|
+
"image": {
|
|
260
|
+
"name": "spotify",
|
|
261
|
+
"imgData": "data:image/png;base64,..."
|
|
262
|
+
},
|
|
263
|
+
"apps": [
|
|
264
|
+
{
|
|
265
|
+
"id": "com.spotify.music",
|
|
266
|
+
"name": "Spotify"
|
|
267
|
+
}
|
|
268
|
+
],
|
|
269
|
+
"mixes": [
|
|
270
|
+
{
|
|
271
|
+
"id": "stream_mix",
|
|
272
|
+
"level": 0.8,
|
|
273
|
+
"isMuted": false
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"id": "monitor_mix",
|
|
277
|
+
"level": 0.6,
|
|
278
|
+
"isMuted": false
|
|
279
|
+
}
|
|
280
|
+
]
|
|
281
|
+
}
|
|
282
|
+
]
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**Field Descriptions:**
|
|
288
|
+
|
|
289
|
+
- `id`: Channel identifier
|
|
290
|
+
- `type`: `"Software"` or `"Hardware"`
|
|
291
|
+
- `isMuted`: Overall channel mute
|
|
292
|
+
- `level`: Overall channel volume (0.0-1.0)
|
|
293
|
+
- `image`: Icon/image for the channel
|
|
294
|
+
- `apps[]`: Applications assigned to this channel
|
|
295
|
+
- `effects[]`: Effects available for this channel (optional)
|
|
296
|
+
- `mixes[]`: Per-mix settings for this channel
|
|
297
|
+
- Each channel has separate volume and mute per mix
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
### `getMixes`
|
|
302
|
+
|
|
303
|
+
Get all mixer configurations (Stream Mix, Monitor Mix, etc.).
|
|
304
|
+
|
|
305
|
+
**Request:**
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
{
|
|
309
|
+
"id": 5,
|
|
310
|
+
"jsonrpc": "2.0",
|
|
311
|
+
"method": "getMixes",
|
|
312
|
+
"params": null
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Response:**
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{
|
|
320
|
+
"jsonrpc": "2.0",
|
|
321
|
+
"id": 5,
|
|
322
|
+
"result": {
|
|
323
|
+
"mixes": [
|
|
324
|
+
{
|
|
325
|
+
"id": "stream_mix",
|
|
326
|
+
"name": "Stream Mix",
|
|
327
|
+
"level": 1.0,
|
|
328
|
+
"isMuted": false,
|
|
329
|
+
"image": {
|
|
330
|
+
"name": "stream"
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"id": "monitor_mix",
|
|
335
|
+
"name": "Monitor Mix",
|
|
336
|
+
"level": 0.9,
|
|
337
|
+
"isMuted": false,
|
|
338
|
+
"image": {
|
|
339
|
+
"name": "monitor"
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Field Descriptions:**
|
|
348
|
+
|
|
349
|
+
- `id`: Mix identifier
|
|
350
|
+
- `name`: Human-readable mix name
|
|
351
|
+
- `level`: Master output level for this mix (0.0-1.0)
|
|
352
|
+
- `isMuted`: Master mute for this mix
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
### `setInputDevice`
|
|
357
|
+
|
|
358
|
+
Modify input device properties.
|
|
359
|
+
|
|
360
|
+
**Request:**
|
|
361
|
+
|
|
362
|
+
```json
|
|
363
|
+
{
|
|
364
|
+
"id": 6,
|
|
365
|
+
"jsonrpc": "2.0",
|
|
366
|
+
"method": "setInputDevice",
|
|
367
|
+
"params": {
|
|
368
|
+
"id": "wave3_usb_microphone",
|
|
369
|
+
"inputs": [
|
|
370
|
+
{
|
|
371
|
+
"id": "wave3_input_1",
|
|
372
|
+
"isMuted": true
|
|
373
|
+
}
|
|
374
|
+
]
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**Example: Adjust Gain**
|
|
380
|
+
|
|
381
|
+
```json
|
|
382
|
+
{
|
|
383
|
+
"id": 7,
|
|
384
|
+
"jsonrpc": "2.0",
|
|
385
|
+
"method": "setInputDevice",
|
|
386
|
+
"params": {
|
|
387
|
+
"id": "wave3_usb_microphone",
|
|
388
|
+
"inputs": [
|
|
389
|
+
{
|
|
390
|
+
"id": "wave3_input_1",
|
|
391
|
+
"gain": {
|
|
392
|
+
"value": 0.75
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
]
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**Example: Toggle Effect**
|
|
401
|
+
|
|
402
|
+
```json
|
|
403
|
+
{
|
|
404
|
+
"id": 8,
|
|
405
|
+
"jsonrpc": "2.0",
|
|
406
|
+
"method": "setInputDevice",
|
|
407
|
+
"params": {
|
|
408
|
+
"id": "wave3_usb_microphone",
|
|
409
|
+
"inputs": [
|
|
410
|
+
{
|
|
411
|
+
"id": "wave3_input_1",
|
|
412
|
+
"effects": [
|
|
413
|
+
{
|
|
414
|
+
"id": "clipguard",
|
|
415
|
+
"isEnabled": false
|
|
416
|
+
}
|
|
417
|
+
]
|
|
418
|
+
}
|
|
419
|
+
]
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**Notes:**
|
|
425
|
+
|
|
426
|
+
- Only include properties you want to change
|
|
427
|
+
- Response is typically empty on success
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
### `setOutputDevice`
|
|
432
|
+
|
|
433
|
+
Modify output device properties.
|
|
434
|
+
|
|
435
|
+
**Request:**
|
|
436
|
+
|
|
437
|
+
```json
|
|
438
|
+
{
|
|
439
|
+
"id": 9,
|
|
440
|
+
"jsonrpc": "2.0",
|
|
441
|
+
"method": "setOutputDevice",
|
|
442
|
+
"params": {
|
|
443
|
+
"outputDevice": {
|
|
444
|
+
"id": "speakers_main",
|
|
445
|
+
"outputs": [
|
|
446
|
+
{
|
|
447
|
+
"id": "output_1",
|
|
448
|
+
"level": 0.5
|
|
449
|
+
}
|
|
450
|
+
]
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
**Example: Switch Mix**
|
|
457
|
+
|
|
458
|
+
```json
|
|
459
|
+
{
|
|
460
|
+
"id": 10,
|
|
461
|
+
"jsonrpc": "2.0",
|
|
462
|
+
"method": "setOutputDevice",
|
|
463
|
+
"params": {
|
|
464
|
+
"outputDevice": {
|
|
465
|
+
"id": "speakers_main",
|
|
466
|
+
"outputs": [
|
|
467
|
+
{
|
|
468
|
+
"id": "output_1",
|
|
469
|
+
"mixId": "monitor_mix"
|
|
470
|
+
}
|
|
471
|
+
]
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
**Example: Mute Output**
|
|
478
|
+
|
|
479
|
+
```json
|
|
480
|
+
{
|
|
481
|
+
"id": 11,
|
|
482
|
+
"jsonrpc": "2.0",
|
|
483
|
+
"method": "setOutputDevice",
|
|
484
|
+
"params": {
|
|
485
|
+
"outputDevice": {
|
|
486
|
+
"id": "speakers_main",
|
|
487
|
+
"outputs": [
|
|
488
|
+
{
|
|
489
|
+
"id": "output_1",
|
|
490
|
+
"isMuted": true
|
|
491
|
+
}
|
|
492
|
+
]
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
### `setChannel`
|
|
501
|
+
|
|
502
|
+
Modify channel (audio source) properties.
|
|
503
|
+
|
|
504
|
+
**Request:**
|
|
505
|
+
|
|
506
|
+
```json
|
|
507
|
+
{
|
|
508
|
+
"id": 12,
|
|
509
|
+
"jsonrpc": "2.0",
|
|
510
|
+
"method": "setChannel",
|
|
511
|
+
"params": {
|
|
512
|
+
"id": "spotify",
|
|
513
|
+
"isMuted": true
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
**Example: Set Volume for Specific Mix**
|
|
519
|
+
|
|
520
|
+
```json
|
|
521
|
+
{
|
|
522
|
+
"id": 13,
|
|
523
|
+
"jsonrpc": "2.0",
|
|
524
|
+
"method": "setChannel",
|
|
525
|
+
"params": {
|
|
526
|
+
"id": "spotify",
|
|
527
|
+
"mixes": [
|
|
528
|
+
{
|
|
529
|
+
"id": "stream_mix",
|
|
530
|
+
"level": 0.5
|
|
531
|
+
}
|
|
532
|
+
]
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
**Example: Set Overall Volume**
|
|
538
|
+
|
|
539
|
+
```json
|
|
540
|
+
{
|
|
541
|
+
"id": 14,
|
|
542
|
+
"jsonrpc": "2.0",
|
|
543
|
+
"method": "setChannel",
|
|
544
|
+
"params": {
|
|
545
|
+
"id": "spotify",
|
|
546
|
+
"level": 0.8
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
**Example: Mute in Specific Mix**
|
|
552
|
+
|
|
553
|
+
```json
|
|
554
|
+
{
|
|
555
|
+
"id": 15,
|
|
556
|
+
"jsonrpc": "2.0",
|
|
557
|
+
"method": "setChannel",
|
|
558
|
+
"params": {
|
|
559
|
+
"id": "spotify",
|
|
560
|
+
"mixes": [
|
|
561
|
+
{
|
|
562
|
+
"id": "stream_mix",
|
|
563
|
+
"isMuted": true
|
|
564
|
+
}
|
|
565
|
+
]
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
**Notes:**
|
|
571
|
+
|
|
572
|
+
- `level`: Overall channel volume
|
|
573
|
+
- `mixes[]`: Per-mix volume and mute settings
|
|
574
|
+
- Only include the properties/mixes you want to change
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
### `setMix`
|
|
579
|
+
|
|
580
|
+
Modify mixer configuration properties.
|
|
581
|
+
|
|
582
|
+
**Request:**
|
|
583
|
+
|
|
584
|
+
```json
|
|
585
|
+
{
|
|
586
|
+
"id": 16,
|
|
587
|
+
"jsonrpc": "2.0",
|
|
588
|
+
"method": "setMix",
|
|
589
|
+
"params": {
|
|
590
|
+
"id": "stream_mix",
|
|
591
|
+
"level": 0.9
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
**Example: Mute Mix**
|
|
597
|
+
|
|
598
|
+
```json
|
|
599
|
+
{
|
|
600
|
+
"id": 17,
|
|
601
|
+
"jsonrpc": "2.0",
|
|
602
|
+
"method": "setMix",
|
|
603
|
+
"params": {
|
|
604
|
+
"id": "stream_mix",
|
|
605
|
+
"isMuted": true
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
---
|
|
611
|
+
|
|
612
|
+
### `addToChannel`
|
|
613
|
+
|
|
614
|
+
Add an application to a specific channel.
|
|
615
|
+
|
|
616
|
+
**Request:**
|
|
617
|
+
|
|
618
|
+
```json
|
|
619
|
+
{
|
|
620
|
+
"id": 18,
|
|
621
|
+
"jsonrpc": "2.0",
|
|
622
|
+
"method": "addToChannel",
|
|
623
|
+
"params": {
|
|
624
|
+
"appId": "com.discord.app",
|
|
625
|
+
"channelId": "comms"
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
---
|
|
631
|
+
|
|
632
|
+
### `setSubscription`
|
|
633
|
+
|
|
634
|
+
Subscribe to notifications/events.
|
|
635
|
+
|
|
636
|
+
**Request: Subscribe to Focused App Changes**
|
|
637
|
+
|
|
638
|
+
```json
|
|
639
|
+
{
|
|
640
|
+
"id": 19,
|
|
641
|
+
"jsonrpc": "2.0",
|
|
642
|
+
"method": "setSubscription",
|
|
643
|
+
"params": {
|
|
644
|
+
"focusedAppChanged": {
|
|
645
|
+
"isEnabled": true
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
**Request: Subscribe to Level Meter Updates**
|
|
652
|
+
|
|
653
|
+
```json
|
|
654
|
+
{
|
|
655
|
+
"id": 20,
|
|
656
|
+
"jsonrpc": "2.0",
|
|
657
|
+
"method": "setSubscription",
|
|
658
|
+
"params": {
|
|
659
|
+
"levelMeterChanged": {
|
|
660
|
+
"type": "channel",
|
|
661
|
+
"id": "spotify",
|
|
662
|
+
"isEnabled": true
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
**Level Meter Types:**
|
|
669
|
+
|
|
670
|
+
- `"input"`: Input device level meters
|
|
671
|
+
- `"output"`: Output device level meters
|
|
672
|
+
- `"channel"`: Channel level meters
|
|
673
|
+
- `"mix"`: Mix level meters
|
|
674
|
+
|
|
675
|
+
**Response:**
|
|
676
|
+
|
|
677
|
+
```json
|
|
678
|
+
{
|
|
679
|
+
"jsonrpc": "2.0",
|
|
680
|
+
"id": 20,
|
|
681
|
+
"result": {
|
|
682
|
+
"levelMeterChanged": {
|
|
683
|
+
"type": "channel",
|
|
684
|
+
"id": "spotify",
|
|
685
|
+
"isEnabled": true
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
---
|
|
692
|
+
|
|
693
|
+
## Notifications
|
|
694
|
+
|
|
695
|
+
Notifications are sent from Wave Link to the client without a request `id`.
|
|
696
|
+
|
|
697
|
+
### `inputDevicesChanged`
|
|
698
|
+
|
|
699
|
+
All input devices changed (complete list).
|
|
700
|
+
|
|
701
|
+
**Notification:**
|
|
702
|
+
|
|
703
|
+
```json
|
|
704
|
+
{
|
|
705
|
+
"jsonrpc": "2.0",
|
|
706
|
+
"method": "inputDevicesChanged",
|
|
707
|
+
"params": {
|
|
708
|
+
"inputDevices": [
|
|
709
|
+
// Same structure as getInputDevices response
|
|
710
|
+
]
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
---
|
|
716
|
+
|
|
717
|
+
### `inputDeviceChanged`
|
|
718
|
+
|
|
719
|
+
A specific input device changed.
|
|
720
|
+
|
|
721
|
+
**Notification:**
|
|
722
|
+
|
|
723
|
+
```json
|
|
724
|
+
{
|
|
725
|
+
"jsonrpc": "2.0",
|
|
726
|
+
"method": "inputDeviceChanged",
|
|
727
|
+
"params": {
|
|
728
|
+
"id": "wave3_usb_microphone",
|
|
729
|
+
"inputs": [
|
|
730
|
+
{
|
|
731
|
+
"id": "wave3_input_1",
|
|
732
|
+
"isMuted": true
|
|
733
|
+
// Only changed properties are included
|
|
734
|
+
}
|
|
735
|
+
]
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
---
|
|
741
|
+
|
|
742
|
+
### `outputDevicesChanged`
|
|
743
|
+
|
|
744
|
+
All output devices changed (complete list).
|
|
745
|
+
|
|
746
|
+
**Notification:**
|
|
747
|
+
|
|
748
|
+
```json
|
|
749
|
+
{
|
|
750
|
+
"jsonrpc": "2.0",
|
|
751
|
+
"method": "outputDevicesChanged",
|
|
752
|
+
"params": [
|
|
753
|
+
"speakers_main",
|
|
754
|
+
[
|
|
755
|
+
// Array of output devices (same structure as getOutputDevices)
|
|
756
|
+
]
|
|
757
|
+
]
|
|
758
|
+
}
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
**Note:** The params is an array: `[mainOutput, outputDevices]`
|
|
762
|
+
|
|
763
|
+
---
|
|
764
|
+
|
|
765
|
+
### `outputDeviceChanged`
|
|
766
|
+
|
|
767
|
+
A specific output device changed.
|
|
768
|
+
|
|
769
|
+
**Notification:**
|
|
770
|
+
|
|
771
|
+
```json
|
|
772
|
+
{
|
|
773
|
+
"jsonrpc": "2.0",
|
|
774
|
+
"method": "outputDeviceChanged",
|
|
775
|
+
"params": {
|
|
776
|
+
"id": "speakers_main"
|
|
777
|
+
// Only changed properties are included
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
---
|
|
783
|
+
|
|
784
|
+
### `channelsChanged`
|
|
785
|
+
|
|
786
|
+
All channels changed (complete list).
|
|
787
|
+
|
|
788
|
+
**Notification:**
|
|
789
|
+
|
|
790
|
+
```json
|
|
791
|
+
{
|
|
792
|
+
"jsonrpc": "2.0",
|
|
793
|
+
"method": "channelsChanged",
|
|
794
|
+
"params": {
|
|
795
|
+
"channels": [
|
|
796
|
+
// Same structure as getChannels response
|
|
797
|
+
]
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
---
|
|
803
|
+
|
|
804
|
+
### `channelChanged`
|
|
805
|
+
|
|
806
|
+
A specific channel changed.
|
|
807
|
+
|
|
808
|
+
**Notification:**
|
|
809
|
+
|
|
810
|
+
```json
|
|
811
|
+
{
|
|
812
|
+
"jsonrpc": "2.0",
|
|
813
|
+
"method": "channelChanged",
|
|
814
|
+
"params": {
|
|
815
|
+
"id": "spotify",
|
|
816
|
+
"isMuted": true
|
|
817
|
+
// Only changed properties are included
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
---
|
|
823
|
+
|
|
824
|
+
### `mixesChanged`
|
|
825
|
+
|
|
826
|
+
All mixes changed (complete list).
|
|
827
|
+
|
|
828
|
+
**Notification:**
|
|
829
|
+
|
|
830
|
+
```json
|
|
831
|
+
{
|
|
832
|
+
"jsonrpc": "2.0",
|
|
833
|
+
"method": "mixesChanged",
|
|
834
|
+
"params": {
|
|
835
|
+
"mixes": [
|
|
836
|
+
// Same structure as getMixes response
|
|
837
|
+
]
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
---
|
|
843
|
+
|
|
844
|
+
### `mixChanged`
|
|
845
|
+
|
|
846
|
+
A specific mix changed.
|
|
847
|
+
|
|
848
|
+
**Notification:**
|
|
849
|
+
|
|
850
|
+
```json
|
|
851
|
+
{
|
|
852
|
+
"jsonrpc": "2.0",
|
|
853
|
+
"method": "mixChanged",
|
|
854
|
+
"params": {
|
|
855
|
+
"id": "stream_mix",
|
|
856
|
+
"level": 0.8
|
|
857
|
+
// Only changed properties are included
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
---
|
|
863
|
+
|
|
864
|
+
### `levelMeterChanged`
|
|
865
|
+
|
|
866
|
+
Level meter values updated (requires subscription via `setSubscription`).
|
|
867
|
+
|
|
868
|
+
**Notification:**
|
|
869
|
+
|
|
870
|
+
```json
|
|
871
|
+
{
|
|
872
|
+
"jsonrpc": "2.0",
|
|
873
|
+
"method": "levelMeterChanged",
|
|
874
|
+
"params": [
|
|
875
|
+
[], // Input devices meters
|
|
876
|
+
[], // Output devices meters
|
|
877
|
+
[
|
|
878
|
+
// Channel meters
|
|
879
|
+
{
|
|
880
|
+
"id": "spotify",
|
|
881
|
+
"left": 0.5,
|
|
882
|
+
"right": 0.5
|
|
883
|
+
}
|
|
884
|
+
],
|
|
885
|
+
[] // Mix meters
|
|
886
|
+
]
|
|
887
|
+
}
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
**Notes:**
|
|
891
|
+
|
|
892
|
+
- The params is an array of 4 arrays: `[inputs, outputs, channels, mixes]`
|
|
893
|
+
- Each meter object format varies by type
|
|
894
|
+
- This notification fires frequently (real-time audio levels)
|
|
895
|
+
|
|
896
|
+
---
|
|
897
|
+
|
|
898
|
+
### `focusedAppChanged`
|
|
899
|
+
|
|
900
|
+
The currently focused application changed (requires subscription).
|
|
901
|
+
|
|
902
|
+
**Notification:**
|
|
903
|
+
|
|
904
|
+
```json
|
|
905
|
+
{
|
|
906
|
+
"jsonrpc": "2.0",
|
|
907
|
+
"method": "focusedAppChanged",
|
|
908
|
+
"params": [
|
|
909
|
+
"com.spotify.music",
|
|
910
|
+
"Spotify",
|
|
911
|
+
{
|
|
912
|
+
"id": "spotify"
|
|
913
|
+
}
|
|
914
|
+
]
|
|
915
|
+
}
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
**Note:** The params is an array: `[appId, appName, { id: channelId }]`
|
|
919
|
+
|
|
920
|
+
---
|
|
921
|
+
|
|
922
|
+
## Common Patterns
|
|
923
|
+
|
|
924
|
+
### Initial Connection
|
|
925
|
+
|
|
926
|
+
1. Connect to WebSocket: `ws://127.0.0.1:1884`
|
|
927
|
+
2. Call `getApplicationInfo` to verify connection
|
|
928
|
+
3. Call `getInputDevices`, `getOutputDevices`, `getChannels`, `getMixes` to get current state
|
|
929
|
+
4. Subscribe to relevant notifications
|
|
930
|
+
|
|
931
|
+
**Example Connection Flow:**
|
|
932
|
+
|
|
933
|
+
```javascript
|
|
934
|
+
const ws = new WebSocket("ws://127.0.0.1:1884", { origin: "streamdeck://" });
|
|
935
|
+
|
|
936
|
+
ws.on("open", () => {
|
|
937
|
+
// 1. Verify connection
|
|
938
|
+
send({ id: 1, jsonrpc: "2.0", method: "getApplicationInfo", params: null });
|
|
939
|
+
|
|
940
|
+
// 2. Get initial state
|
|
941
|
+
send({ id: 2, jsonrpc: "2.0", method: "getInputDevices", params: null });
|
|
942
|
+
send({ id: 3, jsonrpc: "2.0", method: "getOutputDevices", params: null });
|
|
943
|
+
send({ id: 4, jsonrpc: "2.0", method: "getChannels", params: null });
|
|
944
|
+
send({ id: 5, jsonrpc: "2.0", method: "getMixes", params: null });
|
|
945
|
+
|
|
946
|
+
// 3. Subscribe to changes
|
|
947
|
+
send({
|
|
948
|
+
id: 6,
|
|
949
|
+
jsonrpc: "2.0",
|
|
950
|
+
method: "setSubscription",
|
|
951
|
+
params: { focusedAppChanged: { isEnabled: true } },
|
|
952
|
+
});
|
|
953
|
+
});
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
### Volume Control
|
|
957
|
+
|
|
958
|
+
All volume/level values are in the range 0.0 to 1.0:
|
|
959
|
+
|
|
960
|
+
- `0.0` = Silent (0%)
|
|
961
|
+
- `0.5` = 50%
|
|
962
|
+
- `1.0` = Maximum (100%)
|
|
963
|
+
|
|
964
|
+
### Gain Control
|
|
965
|
+
|
|
966
|
+
Input gain is normalized by the device's `maxRange`:
|
|
967
|
+
|
|
968
|
+
- Get current gain: `gain.value` (0.0-1.0)
|
|
969
|
+
- Get max range: `gain.maxRange`
|
|
970
|
+
- Actual dB or raw value: `gain.value * gain.maxRange`
|
|
971
|
+
|
|
972
|
+
### Partial Updates
|
|
973
|
+
|
|
974
|
+
When calling setter methods (`setChannel`, `setMix`, etc.), you only need to include the properties you want to change. Other properties remain unchanged.
|
|
975
|
+
|
|
976
|
+
**Example: Only mute a channel without changing volume**
|
|
977
|
+
|
|
978
|
+
```json
|
|
979
|
+
{
|
|
980
|
+
"id": 100,
|
|
981
|
+
"jsonrpc": "2.0",
|
|
982
|
+
"method": "setChannel",
|
|
983
|
+
"params": {
|
|
984
|
+
"id": "spotify",
|
|
985
|
+
"isMuted": true
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
### Per-Mix Control
|
|
991
|
+
|
|
992
|
+
Channels have both overall settings and per-mix settings:
|
|
993
|
+
|
|
994
|
+
- `level`: Overall volume (affects all mixes proportionally)
|
|
995
|
+
- `isMuted`: Overall mute (mutes in all mixes)
|
|
996
|
+
- `mixes[].level`: Volume for specific mix
|
|
997
|
+
- `mixes[].isMuted`: Mute for specific mix
|
|
998
|
+
|
|
999
|
+
**Example: Channel at 100% overall, but 50% in Stream Mix, 80% in Monitor Mix**
|
|
1000
|
+
|
|
1001
|
+
```json
|
|
1002
|
+
{
|
|
1003
|
+
"id": 101,
|
|
1004
|
+
"jsonrpc": "2.0",
|
|
1005
|
+
"method": "setChannel",
|
|
1006
|
+
"params": {
|
|
1007
|
+
"id": "spotify",
|
|
1008
|
+
"level": 1.0,
|
|
1009
|
+
"mixes": [
|
|
1010
|
+
{ "id": "stream_mix", "level": 0.5 },
|
|
1011
|
+
{ "id": "monitor_mix", "level": 0.8 }
|
|
1012
|
+
]
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
---
|
|
1018
|
+
|
|
1019
|
+
## Error Handling
|
|
1020
|
+
|
|
1021
|
+
### Connection Errors
|
|
1022
|
+
|
|
1023
|
+
- If port 1884 is unavailable, try ports 1885-1893
|
|
1024
|
+
- Use the origin header `streamdeck://` for compatibility
|
|
1025
|
+
- Wave Link must be running for connection to succeed
|
|
1026
|
+
|
|
1027
|
+
### Common Error Codes
|
|
1028
|
+
|
|
1029
|
+
JSON-RPC 2.0 standard error codes:
|
|
1030
|
+
|
|
1031
|
+
- `-32700`: Parse error (invalid JSON)
|
|
1032
|
+
- `-32600`: Invalid request
|
|
1033
|
+
- `-32601`: Method not found
|
|
1034
|
+
- `-32602`: Invalid params
|
|
1035
|
+
- `-32603`: Internal error
|
|
1036
|
+
|
|
1037
|
+
---
|
|
1038
|
+
|
|
1039
|
+
## Implementation Notes
|
|
1040
|
+
|
|
1041
|
+
1. **Request IDs**: Should increment sequentially for easier tracking
|
|
1042
|
+
2. **Throttling**: Some notifications are throttled (100ms for channel/input changes)
|
|
1043
|
+
3. **Reconnection**: Implement automatic reconnection with exponential backoff
|
|
1044
|
+
4. **State Management**: Cache the state from `get*` methods and update with notifications
|
|
1045
|
+
5. **Level Meters**: Subscribe only when needed (generates high-frequency notifications)
|
|
1046
|
+
6. **Focus Tracking**: `focusedAppChanged` is useful for auto-switching channel volumes
|
|
1047
|
+
|
|
1048
|
+
---
|
|
1049
|
+
|
|
1050
|
+
## Example Use Cases
|
|
1051
|
+
|
|
1052
|
+
### Simple Channel Mute Toggle
|
|
1053
|
+
|
|
1054
|
+
```javascript
|
|
1055
|
+
// Get current state
|
|
1056
|
+
const channelsResponse = await call("getChannels", null);
|
|
1057
|
+
const channel = channelsResponse.result.channels.find(
|
|
1058
|
+
(c) => c.id === "spotify",
|
|
1059
|
+
);
|
|
1060
|
+
|
|
1061
|
+
// Toggle mute
|
|
1062
|
+
await call("setChannel", {
|
|
1063
|
+
id: "spotify",
|
|
1064
|
+
isMuted: !channel.isMuted,
|
|
1065
|
+
});
|
|
1066
|
+
```
|
|
1067
|
+
|
|
1068
|
+
### Stream Mix vs Monitor Mix
|
|
1069
|
+
|
|
1070
|
+
Many users have two mixes:
|
|
1071
|
+
|
|
1072
|
+
- **Stream Mix**: What viewers hear (game loud, music quiet)
|
|
1073
|
+
- **Monitor Mix**: What you hear (game quiet, music loud, comms loud)
|
|
1074
|
+
|
|
1075
|
+
```javascript
|
|
1076
|
+
// Make music loud in headphones, quiet in stream
|
|
1077
|
+
await call("setChannel", {
|
|
1078
|
+
id: "music",
|
|
1079
|
+
mixes: [
|
|
1080
|
+
{ id: "stream_mix", level: 0.2 }, // 20% in stream
|
|
1081
|
+
{ id: "monitor_mix", level: 0.8 }, // 80% in headphones
|
|
1082
|
+
],
|
|
1083
|
+
});
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
### Auto-Duck Music When Discord is Active
|
|
1087
|
+
|
|
1088
|
+
```javascript
|
|
1089
|
+
// Subscribe to focused app
|
|
1090
|
+
await call('setSubscription', {
|
|
1091
|
+
focusedAppChanged: { isEnabled: true }
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
// Listen for notifications
|
|
1095
|
+
ws.on('message', (data) => {
|
|
1096
|
+
const msg = JSON.parse(data);
|
|
1097
|
+
|
|
1098
|
+
if (msg.method === 'focusedAppChanged') {
|
|
1099
|
+
const [appId, appName, channel] = msg.params;
|
|
1100
|
+
|
|
1101
|
+
if (appId === 'com.discord.app') {
|
|
1102
|
+
// Duck music when Discord is active
|
|
1103
|
+
await call('setChannel', {
|
|
1104
|
+
id: 'music',
|
|
1105
|
+
mixes: [{ id: 'stream_mix', level: 0.3 }]
|
|
1106
|
+
});
|
|
1107
|
+
} else {
|
|
1108
|
+
// Restore music
|
|
1109
|
+
await call('setChannel', {
|
|
1110
|
+
id: 'music',
|
|
1111
|
+
mixes: [{ id: 'stream_mix', level: 0.7 }]
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
});
|
|
1116
|
+
```
|
|
1117
|
+
|
|
1118
|
+
---
|
|
1119
|
+
|
|
1120
|
+
## Protocol Version
|
|
1121
|
+
|
|
1122
|
+
This documentation is based on Wave Link 3.0 Beta. The `interfaceRevision` from `getApplicationInfo` indicates protocol compatibility.
|
|
1123
|
+
|
|
1124
|
+
**Compatibility:**
|
|
1125
|
+
|
|
1126
|
+
- `interfaceRevision >= 1`: This protocol is supported
|