bumble 0.0.154__py3-none-any.whl → 0.0.156__py3-none-any.whl

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.
bumble/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # file generated by setuptools_scm
2
2
  # don't change, don't track in version control
3
- __version__ = version = '0.0.154'
4
- __version_tuple__ = version_tuple = (0, 0, 154)
3
+ __version__ = version = '0.0.156'
4
+ __version_tuple__ = version_tuple = (0, 0, 156)
bumble/a2dp.py CHANGED
@@ -432,6 +432,7 @@ class AacMediaCodecInformation(
432
432
  cls.SAMPLING_FREQUENCY_BITS[x] for x in sampling_frequencies
433
433
  ),
434
434
  channels=sum(cls.CHANNELS_BITS[x] for x in channels),
435
+ rfa=0,
435
436
  vbr=vbr,
436
437
  bitrate=bitrate,
437
438
  )
bumble/apps/pair.py CHANGED
@@ -207,7 +207,7 @@ def on_connection(connection, request):
207
207
 
208
208
  # Listen for pairing events
209
209
  connection.on('pairing_start', on_pairing_start)
210
- connection.on('pairing', on_pairing)
210
+ connection.on('pairing', lambda keys: on_pairing(connection.peer_address, keys))
211
211
  connection.on('pairing_failure', on_pairing_failure)
212
212
 
213
213
  # Listen for encryption changes
@@ -242,9 +242,9 @@ def on_pairing_start():
242
242
 
243
243
 
244
244
  # -----------------------------------------------------------------------------
245
- def on_pairing(keys):
245
+ def on_pairing(address, keys):
246
246
  print(color('***-----------------------------------', 'cyan'))
247
- print(color('*** Paired!', 'cyan'))
247
+ print(color(f'*** Paired! (peer identity={address})', 'cyan'))
248
248
  keys.print(prefix=color('*** ', 'cyan'))
249
249
  print(color('***-----------------------------------', 'cyan'))
250
250
  Waiter.instance.terminate()
@@ -283,17 +283,6 @@ async def pair(
283
283
  # Create a device to manage the host
284
284
  device = Device.from_config_file_with_hci(device_config, hci_source, hci_sink)
285
285
 
286
- # Set a custom keystore if specified on the command line
287
- if keystore_file:
288
- device.keystore = JsonKeyStore(namespace=None, filename=keystore_file)
289
-
290
- # Print the existing keys before pairing
291
- if print_keys and device.keystore:
292
- print(color('@@@-----------------------------------', 'blue'))
293
- print(color('@@@ Pairing Keys:', 'blue'))
294
- await device.keystore.print(prefix=color('@@@ ', 'blue'))
295
- print(color('@@@-----------------------------------', 'blue'))
296
-
297
286
  # Expose a GATT characteristic that can be used to trigger pairing by
298
287
  # responding with an authentication error when read
299
288
  if mode == 'le':
@@ -323,6 +312,17 @@ async def pair(
323
312
  # Get things going
324
313
  await device.power_on()
325
314
 
315
+ # Set a custom keystore if specified on the command line
316
+ if keystore_file:
317
+ device.keystore = JsonKeyStore.from_device(device, filename=keystore_file)
318
+
319
+ # Print the existing keys before pairing
320
+ if print_keys and device.keystore:
321
+ print(color('@@@-----------------------------------', 'blue'))
322
+ print(color('@@@ Pairing Keys:', 'blue'))
323
+ await device.keystore.print(prefix=color('@@@ ', 'blue'))
324
+ print(color('@@@-----------------------------------', 'blue'))
325
+
326
326
  # Set up a pairing config factory
327
327
  device.pairing_config_factory = lambda connection: PairingConfig(
328
328
  sc, mitm, bond, Delegate(mode, connection, io, prompt)
bumble/apps/scan.py CHANGED
@@ -133,15 +133,16 @@ async def scan(
133
133
  'Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink
134
134
  )
135
135
 
136
+ await device.power_on()
137
+
136
138
  if keystore_file:
137
- keystore = JsonKeyStore(namespace=None, filename=keystore_file)
138
- device.keystore = keystore
139
- else:
140
- resolver = None
139
+ device.keystore = JsonKeyStore.from_device(device, filename=keystore_file)
141
140
 
142
141
  if device.keystore:
143
142
  resolving_keys = await device.keystore.get_resolving_keys()
144
143
  resolver = AddressResolver(resolving_keys)
144
+ else:
145
+ resolver = None
145
146
 
146
147
  printer = AdvertisementPrinter(min_rssi, resolver)
147
148
  if raw:
@@ -149,8 +150,6 @@ async def scan(
149
150
  else:
150
151
  device.on('advertisement', printer.on_advertisement)
151
152
 
152
- await device.power_on()
153
-
154
153
  if phy is None:
155
154
  scanning_phys = [HCI_LE_1M_PHY, HCI_LE_CODED_PHY]
156
155
  else:
File without changes
@@ -0,0 +1,42 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Vectornator for iOS (http://vectornator.io/) --><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
2
+ <svg height="100%" style="fill-rule:nonzero;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" xmlns:vectornator="http://vectornator.io" version="1.1" viewBox="0 0 745 744.634">
3
+ <metadata>
4
+ <vectornator:setting key="DimensionsVisible" value="1"/>
5
+ <vectornator:setting key="PencilOnly" value="0"/>
6
+ <vectornator:setting key="SnapToPoints" value="0"/>
7
+ <vectornator:setting key="OutlineMode" value="0"/>
8
+ <vectornator:setting key="CMYKEnabledKey" value="0"/>
9
+ <vectornator:setting key="RulersVisible" value="1"/>
10
+ <vectornator:setting key="SnapToEdges" value="0"/>
11
+ <vectornator:setting key="GuidesVisible" value="1"/>
12
+ <vectornator:setting key="DisplayWhiteBackground" value="0"/>
13
+ <vectornator:setting key="doHistoryDisabled" value="0"/>
14
+ <vectornator:setting key="SnapToGuides" value="1"/>
15
+ <vectornator:setting key="TimeLapseWatermarkDisabled" value="0"/>
16
+ <vectornator:setting key="Units" value="Pixels"/>
17
+ <vectornator:setting key="DynamicGuides" value="0"/>
18
+ <vectornator:setting key="IsolateActiveLayer" value="0"/>
19
+ <vectornator:setting key="SnapToGrid" value="0"/>
20
+ </metadata>
21
+ <defs/>
22
+ <g id="Layer 1" vectornator:layerName="Layer 1">
23
+ <path stroke="#000000" stroke-width="18.6464" d="M368.753+729.441L58.8847+550.539L58.8848+192.734L368.753+13.8313L678.621+192.734L678.621+550.539L368.753+729.441Z" fill="#0082fc" stroke-linecap="butt" fill-opacity="0.307489" opacity="1" stroke-linejoin="round"/>
24
+ <g opacity="1">
25
+ <g opacity="1">
26
+ <path stroke="#000000" stroke-width="20" d="M292.873+289.256L442.872+289.256L442.872+539.254L292.873+539.254L292.873+289.256Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
27
+ <path stroke="#000000" stroke-width="20" d="M292.873+289.256C292.873+247.835+326.452+214.257+367.873+214.257C409.294+214.257+442.872+247.835+442.872+289.256C442.872+330.677+409.294+364.256+367.873+364.256C326.452+364.256+292.873+330.677+292.873+289.256Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
28
+ <path stroke="#000000" stroke-width="20" d="M292.873+539.254C292.873+497.833+326.452+464.255+367.873+464.255C409.294+464.255+442.872+497.833+442.872+539.254C442.872+580.675+409.294+614.254+367.873+614.254C326.452+614.254+292.873+580.675+292.873+539.254Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
29
+ <path stroke="#0082fc" stroke-width="0.1" d="M302.873+289.073L432.872+289.073L432.872+539.072L302.873+539.072L302.873+289.073Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
30
+ </g>
31
+ <path stroke="#000000" stroke-width="0.1" d="M103.161+309.167L226.956+443.903L366.671+309.604L103.161+309.167Z" fill="#0082fc" stroke-linecap="round" opacity="1" stroke-linejoin="round"/>
32
+ <path stroke="#000000" stroke-width="0.1" d="M383.411+307.076L508.887+440.112L650.5+307.507L383.411+307.076Z" fill="#0082fc" stroke-linecap="round" opacity="1" stroke-linejoin="round"/>
33
+ <path stroke="#000000" stroke-width="20" d="M522.045+154.808L229.559+448.882L83.8397+300.104L653.666+302.936L511.759+444.785L223.101+156.114" fill="none" stroke-linecap="round" opacity="1" stroke-linejoin="round"/>
34
+ <path stroke="#000000" stroke-width="61.8698" d="M295.857+418.738L438.9+418.738" fill="none" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
35
+ <path stroke="#000000" stroke-width="61.8698" d="M295.857+521.737L438.9+521.737" fill="none" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
36
+ <g opacity="1">
37
+ <path stroke="#0082fc" stroke-width="0.1" d="M367.769+667.024L367.821+616.383L403.677+616.336C383.137+626.447+368.263+638.69+367.769+667.024Z" fill="#000000" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
38
+ <path stroke="#0082fc" stroke-width="0.1" d="M367.836+667.024L367.784+616.383L331.928+616.336C352.468+626.447+367.341+638.69+367.836+667.024Z" fill="#000000" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
39
+ </g>
40
+ </g>
41
+ </g>
42
+ </svg>
@@ -0,0 +1,76 @@
1
+ body, h1, h2, h3, h4, h5, h6 {
2
+ font-family: sans-serif;
3
+ }
4
+
5
+ #controlsDiv {
6
+ margin: 6px;
7
+ }
8
+
9
+ #connectionText {
10
+ background-color: rgb(239, 89, 75);
11
+ border: none;
12
+ border-radius: 4px;
13
+ padding: 8px;
14
+ display: inline-block;
15
+ margin: 4px;
16
+ }
17
+
18
+ #startButton {
19
+ padding: 4px;
20
+ margin: 6px;
21
+ }
22
+
23
+ #fftCanvas {
24
+ border-radius: 16px;
25
+ margin: 6px;
26
+ }
27
+
28
+ #bandwidthCanvas {
29
+ border: grey;
30
+ border-style: solid;
31
+ border-radius: 8px;
32
+ margin: 6px;
33
+ }
34
+
35
+ #streamStateText {
36
+ background-color: rgb(93, 165, 93);
37
+ border: none;
38
+ border-radius: 8px;
39
+ padding: 10px 20px;
40
+ display: inline-block;
41
+ margin: 6px;
42
+ }
43
+
44
+ #connectionStateText {
45
+ background-color: rgb(112, 146, 206);
46
+ border: none;
47
+ border-radius: 8px;
48
+ padding: 10px 20px;
49
+ display: inline-block;
50
+ margin: 6px;
51
+ }
52
+
53
+ #propertiesTable {
54
+ border: grey;
55
+ border-style: solid;
56
+ border-radius: 4px;
57
+ padding: 4px;
58
+ margin: 6px;
59
+ margin-left: 0px;
60
+ }
61
+
62
+ th, td {
63
+ padding-left: 6px;
64
+ padding-right: 6px;
65
+ }
66
+
67
+ .properties td:nth-child(even) {
68
+ background-color: #D6EEEE;
69
+ font-family: monospace;
70
+ }
71
+
72
+ .properties td:nth-child(odd) {
73
+ font-weight: bold;
74
+ }
75
+
76
+ .properties tr td:nth-child(2) { width: 150px; }
@@ -0,0 +1,34 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Bumble Speaker</title>
5
+ <script type="text/javascript" src="speaker.js"></script>
6
+ <link rel="stylesheet" href="speaker.css">
7
+ </head>
8
+ <body>
9
+ <h1><img src="logo.svg" width=100 height=100 style="vertical-align:middle" alt=""/>Bumble Virtual Speaker</h1>
10
+ <div id="connectionText"></div>
11
+ <div id="speaker">
12
+ <table><tr>
13
+ <td>
14
+ <table id="propertiesTable" class="properties">
15
+ <tr><td>Codec</td><td><span id="codecText"></span></td></tr>
16
+ <tr><td>Packets</td><td><span id="packetsReceivedText"></span></td></tr>
17
+ <tr><td>Bytes</td><td><span id="bytesReceivedText"></span></td></tr>
18
+ </table>
19
+ </td>
20
+ <td>
21
+ <canvas id="bandwidthCanvas" width="500", height="100">Bandwidth Graph</canvas>
22
+ </td>
23
+ </tr></table>
24
+ <span id="streamStateText">IDLE</span>
25
+ <span id="connectionStateText">NOT CONNECTED</span>
26
+ <div id="controlsDiv">
27
+ <button id="audioOnButton">Audio On</button>
28
+ <span id="audioSupportMessageText"></span>
29
+ </div>
30
+ <canvas id="fftCanvas" width="1024", height="300">Audio Frequencies Animation</canvas>
31
+ <audio id="audio"></audio>
32
+ </div>
33
+ </body>
34
+ </html>
@@ -0,0 +1,315 @@
1
+ (function () {
2
+ 'use strict';
3
+
4
+ const channelUrl = ((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "/channel";
5
+ let channelSocket;
6
+ let connectionText;
7
+ let codecText;
8
+ let packetsReceivedText;
9
+ let bytesReceivedText;
10
+ let streamStateText;
11
+ let connectionStateText;
12
+ let controlsDiv;
13
+ let audioOnButton;
14
+ let mediaSource;
15
+ let sourceBuffer;
16
+ let audioElement;
17
+ let audioContext;
18
+ let audioAnalyzer;
19
+ let audioFrequencyBinCount;
20
+ let audioFrequencyData;
21
+ let packetsReceived = 0;
22
+ let bytesReceived = 0;
23
+ let audioState = "stopped";
24
+ let streamState = "IDLE";
25
+ let audioSupportMessageText;
26
+ let fftCanvas;
27
+ let fftCanvasContext;
28
+ let bandwidthCanvas;
29
+ let bandwidthCanvasContext;
30
+ let bandwidthBinCount;
31
+ let bandwidthBins = [];
32
+
33
+ const FFT_WIDTH = 800;
34
+ const FFT_HEIGHT = 256;
35
+ const BANDWIDTH_WIDTH = 500;
36
+ const BANDWIDTH_HEIGHT = 100;
37
+
38
+ function hexToBytes(hex) {
39
+ return Uint8Array.from(hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
40
+ }
41
+
42
+ function init() {
43
+ initUI();
44
+ initMediaSource();
45
+ initAudioElement();
46
+ initAnalyzer();
47
+
48
+ connect();
49
+ }
50
+
51
+ function initUI() {
52
+ controlsDiv = document.getElementById("controlsDiv");
53
+ controlsDiv.style.visibility = "hidden";
54
+ connectionText = document.getElementById("connectionText");
55
+ audioOnButton = document.getElementById("audioOnButton");
56
+ codecText = document.getElementById("codecText");
57
+ packetsReceivedText = document.getElementById("packetsReceivedText");
58
+ bytesReceivedText = document.getElementById("bytesReceivedText");
59
+ streamStateText = document.getElementById("streamStateText");
60
+ connectionStateText = document.getElementById("connectionStateText");
61
+ audioSupportMessageText = document.getElementById("audioSupportMessageText");
62
+
63
+ audioOnButton.onclick = () => startAudio();
64
+
65
+ setConnectionText("");
66
+
67
+ requestAnimationFrame(onAnimationFrame);
68
+ }
69
+
70
+ function initMediaSource() {
71
+ mediaSource = new MediaSource();
72
+ mediaSource.onsourceopen = onMediaSourceOpen;
73
+ mediaSource.onsourceclose = onMediaSourceClose;
74
+ mediaSource.onsourceended = onMediaSourceEnd;
75
+ }
76
+
77
+ function initAudioElement() {
78
+ audioElement = document.getElementById("audio");
79
+ audioElement.src = URL.createObjectURL(mediaSource);
80
+ // audioElement.controls = true;
81
+ }
82
+
83
+ function initAnalyzer() {
84
+ fftCanvas = document.getElementById("fftCanvas");
85
+ fftCanvas.width = FFT_WIDTH
86
+ fftCanvas.height = FFT_HEIGHT
87
+ fftCanvasContext = fftCanvas.getContext('2d');
88
+ fftCanvasContext.fillStyle = "rgb(0, 0, 0)";
89
+ fftCanvasContext.fillRect(0, 0, FFT_WIDTH, FFT_HEIGHT);
90
+
91
+ bandwidthCanvas = document.getElementById("bandwidthCanvas");
92
+ bandwidthCanvas.width = BANDWIDTH_WIDTH
93
+ bandwidthCanvas.height = BANDWIDTH_HEIGHT
94
+ bandwidthCanvasContext = bandwidthCanvas.getContext('2d');
95
+ bandwidthCanvasContext.fillStyle = "rgb(255, 255, 255)";
96
+ bandwidthCanvasContext.fillRect(0, 0, BANDWIDTH_WIDTH, BANDWIDTH_HEIGHT);
97
+ }
98
+
99
+ function startAnalyzer() {
100
+ // FFT
101
+ if (audioElement.captureStream !== undefined) {
102
+ audioContext = new AudioContext();
103
+ audioAnalyzer = audioContext.createAnalyser();
104
+ audioAnalyzer.fftSize = 128;
105
+ audioFrequencyBinCount = audioAnalyzer.frequencyBinCount;
106
+ audioFrequencyData = new Uint8Array(audioFrequencyBinCount);
107
+ const stream = audioElement.captureStream();
108
+ const source = audioContext.createMediaStreamSource(stream);
109
+ source.connect(audioAnalyzer);
110
+ }
111
+
112
+ // Bandwidth
113
+ bandwidthBinCount = BANDWIDTH_WIDTH / 2;
114
+ bandwidthBins = [];
115
+ }
116
+
117
+ function setConnectionText(message) {
118
+ connectionText.innerText = message;
119
+ if (message.length == 0) {
120
+ connectionText.style.display = "none";
121
+ } else {
122
+ connectionText.style.display = "inline-block";
123
+ }
124
+ }
125
+
126
+ function setStreamState(state) {
127
+ streamState = state;
128
+ streamStateText.innerText = streamState;
129
+ }
130
+
131
+ function onAnimationFrame() {
132
+ // FFT
133
+ if (audioAnalyzer !== undefined) {
134
+ audioAnalyzer.getByteFrequencyData(audioFrequencyData);
135
+ fftCanvasContext.fillStyle = "rgb(0, 0, 0)";
136
+ fftCanvasContext.fillRect(0, 0, FFT_WIDTH, FFT_HEIGHT);
137
+ const barCount = audioFrequencyBinCount;
138
+ const barWidth = (FFT_WIDTH / audioFrequencyBinCount) - 1;
139
+ for (let bar = 0; bar < barCount; bar++) {
140
+ const barHeight = audioFrequencyData[bar];
141
+ fftCanvasContext.fillStyle = `rgb(${barHeight / 256 * 200 + 50}, 50, ${50 + 2 * bar})`;
142
+ fftCanvasContext.fillRect(bar * (barWidth + 1), FFT_HEIGHT - barHeight, barWidth, barHeight);
143
+ }
144
+ }
145
+
146
+ // Bandwidth
147
+ bandwidthCanvasContext.fillStyle = "rgb(255, 255, 255)";
148
+ bandwidthCanvasContext.fillRect(0, 0, BANDWIDTH_WIDTH, BANDWIDTH_HEIGHT);
149
+ bandwidthCanvasContext.fillStyle = `rgb(100, 100, 100)`;
150
+ for (let t = 0; t < bandwidthBins.length; t++) {
151
+ const lineHeight = (bandwidthBins[t] / 1000) * BANDWIDTH_HEIGHT;
152
+ bandwidthCanvasContext.fillRect(t * 2, BANDWIDTH_HEIGHT - lineHeight, 2, lineHeight);
153
+ }
154
+
155
+ // Display again at the next frame
156
+ requestAnimationFrame(onAnimationFrame);
157
+ }
158
+
159
+ function onMediaSourceOpen() {
160
+ console.log(this.readyState);
161
+ sourceBuffer = mediaSource.addSourceBuffer("audio/aac");
162
+ }
163
+
164
+ function onMediaSourceClose() {
165
+ console.log(this.readyState);
166
+ }
167
+
168
+ function onMediaSourceEnd() {
169
+ console.log(this.readyState);
170
+ }
171
+
172
+ async function startAudio() {
173
+ try {
174
+ console.log("starting audio...");
175
+ audioOnButton.disabled = true;
176
+ audioState = "starting";
177
+ await audioElement.play();
178
+ console.log("audio started");
179
+ audioState = "playing";
180
+ startAnalyzer();
181
+ } catch(error) {
182
+ console.error(`play failed: ${error}`);
183
+ audioState = "stopped";
184
+ audioOnButton.disabled = false;
185
+ }
186
+ }
187
+
188
+ function onAudioPacket(packet) {
189
+ if (audioState != "stopped") {
190
+ // Queue the audio packet.
191
+ sourceBuffer.appendBuffer(packet);
192
+ }
193
+
194
+ packetsReceived += 1;
195
+ packetsReceivedText.innerText = packetsReceived;
196
+ bytesReceived += packet.byteLength;
197
+ bytesReceivedText.innerText = bytesReceived;
198
+
199
+ bandwidthBins[bandwidthBins.length] = packet.byteLength;
200
+ if (bandwidthBins.length > bandwidthBinCount) {
201
+ bandwidthBins.shift();
202
+ }
203
+ }
204
+
205
+ function onChannelOpen() {
206
+ console.log('channel OPEN');
207
+ setConnectionText("");
208
+ controlsDiv.style.visibility = "visible";
209
+
210
+ // Handshake with the backend.
211
+ sendMessage({
212
+ type: "hello"
213
+ });
214
+ }
215
+
216
+ function onChannelClose() {
217
+ console.log('channel CLOSED');
218
+ setConnectionText("Connection to CLI app closed, restart it and reload this page.");
219
+ controlsDiv.style.visibility = "hidden";
220
+ }
221
+
222
+ function onChannelError(error) {
223
+ console.log(`channel ERROR: ${error}`);
224
+ setConnectionText(`Connection to CLI app error ({${error}}), restart it and reload this page.`);
225
+ controlsDiv.style.visibility = "hidden";
226
+ }
227
+
228
+ function onChannelMessage(message) {
229
+ if (typeof message.data === 'string' || message.data instanceof String) {
230
+ // JSON message.
231
+ const jsonMessage = JSON.parse(message.data);
232
+ console.log(`channel MESSAGE: ${message.data}`);
233
+
234
+ // Dispatch the message.
235
+ const handlerName = `on${jsonMessage.type.charAt(0).toUpperCase()}${jsonMessage.type.slice(1)}Message`
236
+ const handler = messageHandlers[handlerName];
237
+ if (handler !== undefined) {
238
+ const params = jsonMessage.params;
239
+ if (params === undefined) {
240
+ params = {};
241
+ }
242
+ handler(params);
243
+ } else {
244
+ console.warn(`unhandled message: ${jsonMessage.type}`)
245
+ }
246
+ } else {
247
+ // BINARY audio data.
248
+ onAudioPacket(message.data);
249
+ }
250
+ }
251
+
252
+ function onHelloMessage(params) {
253
+ codecText.innerText = params.codec;
254
+ if (params.codec != "aac") {
255
+ audioOnButton.disabled = true;
256
+ audioSupportMessageText.innerText = "Only AAC can be played, audio will be disabled";
257
+ audioSupportMessageText.style.display = "inline-block";
258
+ } else {
259
+ audioSupportMessageText.innerText = "";
260
+ audioSupportMessageText.style.display = "none";
261
+ }
262
+ if (params.streamState) {
263
+ setStreamState(params.streamState);
264
+ }
265
+ }
266
+
267
+ function onStartMessage(params) {
268
+ setStreamState("STARTED");
269
+ }
270
+
271
+ function onStopMessage(params) {
272
+ setStreamState("STOPPED");
273
+ }
274
+
275
+ function onSuspendMessage(params) {
276
+ setStreamState("SUSPENDED");
277
+ }
278
+
279
+ function onConnectionMessage(params) {
280
+ connectionStateText.innerText = `CONNECTED: ${params.peer_name} (${params.peer_address})`;
281
+ }
282
+
283
+ function onDisconnectionMessage(params) {
284
+ connectionStateText.innerText = "DISCONNECTED";
285
+ }
286
+
287
+ function sendMessage(message) {
288
+ channelSocket.send(JSON.stringify(message));
289
+ }
290
+
291
+ function connect() {
292
+ console.log("connecting to CLI app");
293
+
294
+ channelSocket = new WebSocket(channelUrl);
295
+ channelSocket.binaryType = "arraybuffer";
296
+ channelSocket.onopen = onChannelOpen;
297
+ channelSocket.onclose = onChannelClose;
298
+ channelSocket.onerror = onChannelError;
299
+ channelSocket.onmessage = onChannelMessage;
300
+ }
301
+
302
+ const messageHandlers = {
303
+ onHelloMessage,
304
+ onStartMessage,
305
+ onStopMessage,
306
+ onSuspendMessage,
307
+ onConnectionMessage,
308
+ onDisconnectionMessage
309
+ }
310
+
311
+ window.onload = (event) => {
312
+ init();
313
+ }
314
+
315
+ }());