-
Notifications
You must be signed in to change notification settings - Fork 0
/
divisible_room_secondary.js
2734 lines (2248 loc) · 109 KB
/
divisible_room_secondary.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
Copyright (c) 2023 Cisco and/or its affiliates.
This software is licensed to you under the terms of the Cisco Sample
Code License, Version 1.1 (the "License"). You may obtain a copy of the
License at
https://developer.cisco.com/docs/licenses
All use of the material herein must be in accordance with the terms of
the License. All rights not expressly granted by the License are
reserved. Unless required by applicable law or agreed to separately in
writing, software distributed under the License is distributed on an "AS
IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied.
*
*
* Repository: gve_devnet_divisible_conference_rooms_webex_devices_macros
* Macro file: divisible_room_secondary
* Version: 1.0.1
* Released: May 7, 2024
* Latest RoomOS version tested: 11.15.1.6
*
* Macro Author: Gerardo Chaves
* Technical Solutions Architect
* Cisco Systems
*
* Consulting Engineer: Robert(Bobby) McGonigle Jr
* Technical Marketing Engineer
* Cisco Systems
*
*
*
* As a macro, the features and functions of this webex n-way divisibe conference
* rooms macro are not supported by Cisco TAC
*
* Hardware and Software support are provided by their respective manufacturers
* and the service agreements they offer
*
* Should you need assistance with this macro, reach out to your Cisco sales representative
* so they can engage the GVE DevNet team.
*/
import xapi from 'xapi';
import { GMM } from './GMM_Lib'
const minOS10Version = '10.17.1.0';
const minOS11Version = '11.2.1.0';
const JS_PRIMARY = 1, JS_SECONDARY = 2, JS_AUXILIARY = 3, JS_LOCAL = 0
/*
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ SECTION 1 - SECTION 1 - SECTION 1 - SECTION 1 - SECTION 1 - SECTION 1 +
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
// set SECONDARY_MULTI_CAM to true if you intend to allow local switching of cameras on a secondary
// codec, with or without the assistance of local auxiliary codecs
const SECONDARY_MULTI_CAM = false;
// In this section, write in the values for the constants below.
// If you wired your rooms different from what is indicated in the Version_3_Two-way_System_Drawing.pdf document
// you can modify the SECONDARY_VIDEO_TIELINE_OUTPUT_TO_PRI_SEC_ID
// SECONDARY_VIDEO_TIELINE_INPUT_M1_FROM_PRI_ID and SECONDARY_VIDEO_TIELINE_INPUT_M2_FROM_PRI_ID constants
// to match your setup.
// For PRIMARY_CODEC_ADDRESS enter the IP address for the Primary Codec.
const SECONDARY_VIDEO_TIELINE_OUTPUT_TO_PRI_SEC_ID = 3; // change only for non-standard singe screen setups
const SECONDARY_AUDIO_TIELINE_OUTPUT_TO_PRI_ID = 5; // change only if non standard (i.e. codec EQ)
const SECONDARY_VIDEO_TIELINE_INPUT_M1_FROM_PRI_ID = 3; // change only for non-standard singe screen setups
const SECONDARY_VIDEO_TIELINE_INPUT_M2_FROM_PRI_ID = 4; // change only for non-standard singe screen setups
const PRIMARY_CODEC_ADDRESS = '10.0.0.100'; // IP address or webex codec ID (if PRIMARY_BOT_TOKEN is set) for primary codec. To obtain codec ID: xStatus Webex DeveloperId
// If you fill out the PRIMARY_CODEC_USER and PRIMARY_CODEC_PASSWORD with the credentials to be able to log
// into the the Secondary codec (if configuring Primary) or Primary codec (if configuring Secondary)
// they will be used to establish an HTTP connection with that other codec, but these credentials will be
// stored clear text in the macro.
// If you wish to slightly obfuscate the credentials, use a Base64 encoded string for PRIMARY_CODEC_USER and
// leave PRIMARY_CODEC_PASSWORD blank. If you do that, you would need to combine the username and password in one string
// separated by a colon (i.e. "username:password") before Base64 encoding with a tool such as https://www.base64encode.org/
// Instructions for creating these admin accounts are in the "Installation Instructions" document.
const PRIMARY_CODEC_USER = '';
const PRIMARY_CODEC_PASSWORD = '';
// You can fill out the PRIMARY_BOT_TOKEN value intead of PRIMARY_CODEC_USER/PRIMARY_CODEC_PASSWORD to use the Webex cloud to
// communicate with other codecs in the system. it should contain the Bot access token you wish to use to have the codec use
// when sending commands to the other codecs by using Webex messaging.
// NOTE: You must add the Bot that corresponds to the bot token you intend to use to the API access list in the Workspace where the device is configured
// To do so in Control Hub, go to the Workspace for each device, click on the "Edit API Access" button and add the bot to the list (search for it by name)
// with "Full Access" access level.
const PRIMARY_BOT_TOKEN = '';
/*
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ SECTION 2 - SECTION 2 - SECTION 2 - SECTION 2 - SECTION 2 - SECTION 2
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
// For more reliability when combining and dividing rooms you can use a custom cable connecting the
// GPIO pins 2-4 between the primary codec and secondary codecs. This cable cannot be used if you have
// a setup where you need to "promote" a secondary room to primary to accomodate specific room layouts
// in which case the value should be false.
const USE_GPIO_INTERCODEC = false;
// CHK_VUMETER_LOUDSPEAKER specifies if we check the LoudspeakerActivity flag from the VuMeter events
// to ignore any microphone activity while the loudspeakers are active to reduce the possibility of
// switching due to sound coming in from remote participants in the meeting if the AfterAEC setting
// is not being effective. Set to true to perform the check for each microphone activity event.
const CHK_VUMETER_LOUDSPEAKER = false;
/*
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ SECTION 3 - SECTION 3 - SECTION 3 - SECTION 3 - SECTION 3 - SECTION 3 +
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
// Change SECONDARY_COMBINED_VOLUME_CHANGE_STEPS if you want to adjust the volume on the secondary
// codec when switching modes. Each step is equivalent to a 0.5 dB change. Set the value to 0 if you wish
// to simply set the actual volume when combined or standalone by using the SECONDARY_COMBINED_VOLUME_COMBINED and
// SECONDARY_COMBINED_VOLUME_STANDALONE constants below
const SECONDARY_COMBINED_VOLUME_CHANGE_STEPS = 10; //TODO: Default to 0 or eliminate once we implement reading setting from codec
// To set the volume of the secondary codecs to a specific value when combined vs when standalone, set the
// SECONDARY_COMBINED_VOLUME_CHANGE_STEPS to 0 and specific the correct volume you wish to set the codec to using
// the SECONDARY_COMBINED_VOLUME_COMBINED and SECONDARY_COMBINED_VOLUME_STANDALONE constants
const SECONDARY_COMBINED_VOLUME_COMBINED = 0;
const SECONDARY_COMBINED_VOLUME_STANDALONE = 0; //TODO: Implement reading setting from codec
// If you would like to use the speaker on the monitor 1 in the secondary room, set SECONDARY_USE_MONITOR_AUDIO to true
// otherwise the macro will turn off Audio on that connector
const SECONDARY_USE_MONITOR_AUDIO = false;
/*
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ SECTION 4 - SECTION 4 - SECTION 4 - SECTION 4 - SECTION 4 - SECTION 4 +
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
General microphones and video sources for both primary and secondary codecs
The monitorMics, ethernetMics and usbMics arrays refer to locally connected microphones for which the macro will monitor vuMeter levels.
The ID range for monitorMics is 1-8 since it refers to the physical analog mic input connectors on the codec.
The ID range for ethernetMics is 11-18, 21-28 an so forth until 81-88 since we support up to 8 ethernet mics with 8
sub-ids each. So, for example , ethernec mic ID 12 as specified in this array refers to Ethernet Mic 1, sub-ID 2
The ID range for usbMics is 101-104 an maps to USB mic IDs 1-4 even though at the moment just one USB Mic input is supported (101)
The externalMics array refers to externally connected microphones where a controller sends the codec text messages over SSH or
serial interface indicating which of those external microphones is currently active.
The text message should be sent by the controller in the format “MIC_ACTIVE_XX” where XX is a distinct
“microphone” id from 01 o 99. We are reserving 00 to indicate that there is relative silence in the room or that mute is active.
Even though the receiving of unformatted “MIC_ACTIVE_XX” type strings is supported, for better logging it is strongly
recommended that the controller sends the message wrapped as an object as shown in the following examples.
sending the MIC_ACTIVE_01 message via serial:
xCommand Message Send Text: "{\x5C"App\x5C":\x5C"Crestron\x5C",\x5C"Source\x5C":{},\x5C"Type\x5C":\x5C"Command\x5C",\x5C"Value\x5C":\x5C"MIC_ACTIVE_01\x5C"}"\x0D\x0A
sending the MIC_ACTIVE_01 message via SSH:
xCommand Message Send Text: "{\"App\":\"Crestron\",\"Source\":{},\"Type\":\"Command\",\"Value\":\"MIC_ACTIVE_01\"}"
NOTE: Any combination of microphone types specified in the monitorMics, ethernetMics , usbMics and externalMics is supported by
the macro, but given the differences in echo cancellation processing perfomed by the different microphone categories it is strongly
advised to stick to only one type of microphone to use for each installation.
NOTE: See section 6 for PresenterTrack QA mode configuration and the PRESENTER_QA_AUDIENCE_MIC_IDS array
*/
const config = {
monitorMics: [1, 2], // (ex: [1, 2, 3, 4, 5, 6, 7, 8] ) analog input connectors (1-8) associated to microphones monitored
ethernetMics: [], // (ex: 11, 12, 13, 14] ) IDs associated to Ethernet mics, up to 8 mics with 8 sub-ids: e.j. 12 is Ethernet Mic 1, sub-ID 2.
usbMics: [], // (ex: [101]) Mic input connectors associated to the USB microphones being used in the main codec: 101 is USB Mic 1
externalMics: [], // (ex: [901, 902]) input ids associated to microphones connected to an external controller received as message format MIC_ACTIVE_XX where XX is an external mic id 01-99
compositions: [ // Create your array of compositions, if room role is JS_SECONDARY, these are for local cameras and AUX codecs only, no JS_SECONDARY source compositions allowed
{ // example for quadcam directly connected to connector 1 in main room
name: 'RoomMain', // Name for your composition.
codecIP: '', // No CodecIP needed if source is JS_LOCAL
mics: [1, 2], // Mics you want to associate with this composition. Example: [1, 2, 3]
connectors: [1], // Video input connector Ids to use
source: JS_LOCAL, // Always use JS_LOCAL in Primary or Secondary when referring to locally connected camera
layout: 'Prominent',// Layout to use
preset: 0 // use a camera preset instead of a layout with specific connectors.
},
{
// NOTE: If you want to always show and overview shot irrespective of microphone input, just
// set SIDE_BY_SIDE_TIME in section 5 below to 0
// Also, if you wish to show several presets in a composition or a combination of presets and
// non-preset camera or tie line video inputs, specify the presets to use in the preset key below
// as an array (i.e. [11,12]) but also include the video connector ID for the cameras for those
// presets in the connectors array below in the right order so the macro knows how to lay them out in the composition
// (i.e. connectors:[2,3,1,4] if the connectorID for the camera associated for preset 11 is 2,
// the connectorID for the camera associated for preset 12 is 3 and you want to also include input from quadcam
// at connector 1 and video from tieline from secondary in connector 4 as the overview shot.)
name: 'Overview', // IMPORTANT: There needs to be an overview compositino with mics: [0]
codecIP: '', // No CodecIP needed if source is JS_LOCAL
mics: [0], // always just [0] for overview compositions
connectors: [1], // Specify here the video inputs and order to use to compose the "overview" shot. Ex: [2,1] including those for preset related cameras
source: JS_LOCAL, // Overview composition always has source JS_LOCAL
layout: 'Equal', // Layout to use
preset: 0 // use a camera preset instead of a layout with specific connectors. Specify a single preset or an array of preset Ids
// NOTE: do not set preset to just one integer if you want more than one video input to be layed out, if you only
// have one preset but still want to specify other connectos in the layout then specify and array of just one preset
// (i.e. preset: [11] if only preset 11 will be used and connectors:[2,1,4] if you want to compose it input from the
// camera doing the preset with connectors 1 and 4 as well.)
// Setting preset to just one integeter will force it to ignore the connectors value
// Set preset to 0 if no presets will be used.
}
]
};
// If you are using a SpeakerTrack 60, set QUAD_CAM_ID to the connector ID where the first camera of the array is connected
// and also use that ID in the connetors array in the compositions above
// If you are using a QuadCam, set this value to the connector ID being used for it.
// If you do not have any speakertracking capable cameras, just set this value to 0
const QUAD_CAM_ID = 1;
// In RoomOS 11 there are multiple SpeakerTrack default behaviors to choose from on the navigator or
// Touch10 device. Set ST_DEFAULT_BEHAVIOR to the one you want this macro to use from these choices:
// Auto: The same as BestOverview.
// BestOverview: The default framing mode is Best overview.
// Closeup: The default framing mode is Closeup (speaker tracking).
// Current: The framing mode is kept unchanged when leaving a call.
// Frames: The default framing mode is Frames.
const ST_DEFAULT_BEHAVIOR = 'Closeup';
/*
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ SECTION 5 - SECTION 5 - SECTION 5 - SECTION 5 - SECTION 5 - SECTION 5 +
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TIMERS and THRESHOLDS
*/
// Time to wait for silence before setting Speakertrack Side-by-Side (Overview) mode
// set SIDE_BY_SIDE_TIME to 0 if you always want to show that mode
const SIDE_BY_SIDE_TIME = 10000; // 10 seconds
// Time to wait before switching to a new speaker
const NEW_SPEAKER_TIME = 2000; // 2 seconds
// Time to wait before activating automatic mode at the beginning of a call
const INITIAL_CALL_TIME = 15000; // 15 seconds
// WEBRTC_VIDEO_UNMUTE_WAIT_TIME only applies to RoomOS version 10 since
// have to to implement a woraround there to be able to switch cameras
// while in a WebRTC call. Values less than 1500 ms do not seem to work, but
// if you are having trouble getting switching to work in WebRTC calls you can increase
// this value although that will affect the overall experience since during this time
// the remote participants just see a black screen instead of the video feed.
const WEBRTC_VIDEO_UNMUTE_WAIT_TIME = 1500;
// Microphone High/Low Thresholds
const MICROPHONELOW = 6;
const MICROPHONEHIGH = 25;
/*
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ SECTION 6 - SECTION 6 - SECTION 6 - SECTION 6 - SECTION 6 - SECTION 6 +
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Presenter Track Q&A Mode
*/
// ALLOW_PRESENTER_QA_MODE controls if the custom panel for activating PresenterTrack with or without
// Q&A Mode is shown in the Touch10 or Navigator. Without it, you cannot activate PresenterTrack Q&A mode
const ALLOW_PRESENTER_QA_MODE = false;
//PRESENTER_QA_AUDIENCE_MIC_IDS is an array for Mic IDs that are being used for the audience.
const PRESENTER_QA_AUDIENCE_MIC_IDS = [1, 2];
// PRESENTER_QA_KEEP_COMPOSITION_TIME is the time in ms that the macro will keep sending
// a composed image of the presenter and an audience member asking a question after the question
// has been asked by any audience member. If different audience members ask questions while the composition
// is being shown after NEW_SPEAKER_TIME milliseconds have passed, the composition will change
// to use that new audience member instead of the original. This will continue until no other audience members have
// spoken for PRESENTER_QA_KEEP_COMPOSITION_TIME milliseconds and then the code will resume sending only the
// full video feed from the Presenter camera
const PRESENTER_QA_KEEP_COMPOSITION_TIME = 7000
/*
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ DO NOT EDIT ANYTHING BELOW THIS LINE +
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
async function isCodecPro() {
let ProductPlatform = await xapi.Status.SystemUnit.ProductPlatform.get()
return (ProductPlatform == "Codec Pro")
}
async function secSendKeepAliveResponse() {
// send message "VTC_KA_OK" to primary when received "VTC_KA_req"
await sendIntercodecMessage("VTC_KA_OK");
}
// Validate config settings
async function validate_config() {
let hasOverview = true;
// only allow CodecPro or CodecEQ with advanced AV integrator option key
const ProductPlatform = await xapi.Status.SystemUnit.ProductPlatform.get()
if (ProductPlatform == "Room Kit EQ") {
try {
console.log(`Is Codec EQ`);
const hasAVOptionInstalled = await xapi.Status.SystemUnit.Software.OptionKeys.AVIntegrator.get()
if (hasAVOptionInstalled != 'True') {
await disableMacro(`config validation fail: Platform ${ProductPlatform} without AV Integrator Option key not supported.`);
}
}
catch (e) {
await disableMacro(`config validation fail: Platform ${ProductPlatform} could not validate AV Option key.`);
}
}
else if (ProductPlatform == "Codec Pro") {
console.log(`Is Codec Pro`)
}
else {
await disableMacro(`config validation fail: Platform ${ProductPlatform} not supported.`);
}
if (module.name.replace('./', '') != 'divisible_room_secondary')
await disableMacro(`config validation fail: macro name has changed to: ${module.name.replace('./', '')}. Please set back to: divisible_room_secondary`);
if (PRIMARY_CODEC_USER == '')
await disableMacro(`config validation fail: OTHER_CODEC credentials must be set. Current values: PRIMARY_CODEC_USER: ${PRIMARY_CODEC_USER} PRIMARY_CODEC_PASSWORD= ${PRIMARY_CODEC_PASSWORD}`);
// allow up to 8 analog mics
let allowedMics = [1, 2, 3, 4, 5, 6, 7, 8];
let allowedEthernetMics = []
// allow up to 8 ethernet mics with 8 lobes each
for (let i = 1; i <= 8; i++) {
for (let j = 1; j <= 8; j++) {
allowedEthernetMics.push((i * 10) + j)
}
}
let allowedUSBMics = []
// allow up to 4 USB mics
for (let i = 1; i <= 4; i++) {
allowedUSBMics.push(100 + i)
}
let allowedExternalMics = []
// allow up to 99 External mics
for (let i = 1; i <= 99; i++) {
allowedExternalMics.push(900 + i)
}
// only allow up to 8 analog microphones
if (config.monitorMics.length > 8)
await disableMacro(`config validation fail: config.monitorMics can only have up to 8 entries. Current value: ${config.MonitorMics} `);
// only allow up to 8 analog microphones
if (config.ethernetMics.length > 64)
await disableMacro(`config validation fail: config.ethernetMics can only have up to 64 entries. Current value: ${config.ethernetMics} `);
// only allow up to 8 analog microphones
if (config.usbMics.length > 4)
await disableMacro(`config validation fail: config.usbMics can only have up to 4 entries. Current value: ${config.usbMics} `);
if (config.externalMics.length > 99)
await disableMacro(`config validation fail: config.externalMics can only have up to 99 entries. Current value: ${config.ethernetMics} `);
if ((config.monitorMics.length + config.ethernetMics + config.usbMics.length + config.externalMics.length) < 1)
await disableMacro(`config validation fail: there must be at least one microphone configured between config.monitorMics, config.ethernetMics and config.usbMics.`);
// Check if using USB mic/input, that Echo control is turned on
if (config.usbMics.length > 0) {
const usbEchoControl = await xapi.config.Audio.Input.USBInterface[1].EchoControl.Mode.get()
if (usbEchoControl != 'On')
await disableMacro(`config validation fail: when using USB microphone input, Echo Control needs to be enabled. Only asynchronous USB devices are supported. Please enable and re-activate macro`);
}
// make sure the mics are within those specified in the monitorMics array
if (!config.monitorMics.every(r => allowedMics.includes(r)))
await disableMacro(`config validation fail: config.monitorMics can only have analog mic ids 1-8. Current value: ${config.monitorMics} `);
if (!config.ethernetMics.every(r => allowedEthernetMics.includes(r)))
await disableMacro(`config validation fail: config.ethernetMics can only include Ethernet mics 1-8(8 lobes each). Current value: ${config.ethernetMics} `);
if (!config.usbMics.every(r => allowedUSBMics.includes(r)))
await disableMacro(`config validation fail: config.usbMics can only include USB mics 1-4 (values 101-104). Current value: ${config.usbMics} `);
if (!config.externalMics.every(r => allowedExternalMics.includes(r)))
await disableMacro(`config validation fail: config.externalMics can only include external mics 01-99 (values 901-999). Current value: ${config.externalMics} `);
// check for duplicates in config.monitorMics
if (new Set(config.monitorMics).size !== config.monitorMics.length)
await disableMacro(`config validation fail: config.monitorMics cannot have duplicates. Current value: ${config.monitorMics} `);
if (new Set(config.ethernetMics).size !== config.ethernetMics.length)
await disableMacro(`config validation fail: config.ethernetMics cannot have duplicates. Current value: ${config.ethernetMics} `);
if (new Set(config.usbMics).size !== config.usbMics.length)
await disableMacro(`config validation fail: config.usbMics cannot have duplicates. Current value: ${config.usbMics} `);
// Check for valid audience mics CONFIGURED for the Presenter QA Mode feature
if (ALLOW_PRESENTER_QA_MODE)
if (!PRESENTER_QA_AUDIENCE_MIC_IDS.every(r => config.monitorMics.includes(r)) &&
!PRESENTER_QA_AUDIENCE_MIC_IDS.every(r => config.ethernetMics.includes(r)) &&
!PRESENTER_QA_AUDIENCE_MIC_IDS.every(r => config.externalMics.includes(r)) &&
!PRESENTER_QA_AUDIENCE_MIC_IDS.every(r => config.usbMics.includes(r)))
await disableMacro(`config validation fail: PRESENTER_QA_AUDIENCE_MIC_IDS can only specify values contained in config.monitorMics, config.ethernetMics or config.usbMics . Current values PRESENTER_QA_AUDIENCE_MIC_IDS: ${PRESENTER_QA_AUDIENCE_MIC_IDS}`);
// all went well, can return true!
return true;
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function disableMacro(reason = 'N/A') {
console.warn(reason)
let act = `Disabling [${module.name.replace('./', '')}] in 10 seconds`
console.error({ Error: reason, Action: act })
await xapi.Command.UserInterface.Message.Alert.Display({ Title: '⚠️ Macro Error ⚠️', Text: `${reason}<p>${act}`, Duration: 9 });
await delay(10000);
await xapi.Command.Macros.Macro.Deactivate({ Name: module.name.replace('./', '') });
await delay(100);
await xapi.Command.Macros.Runtime.Restart();
}
async function checkOverviewPreset() {
console.log('Checking for existence of preset 30')
let pre_list = await xapi.Command.Camera.Preset.List(
{ CameraId: 1 })
let pre_exists = false;
if ('Preset' in pre_list) {
pre_list.Preset.forEach(preObj => {
if (preObj.PresetId == '30') pre_exists = true;
})
}
if (!pre_exists) {
console.log('Preset 30 does not exist, need to create it....')
await xapi.Command.Camera.PositionReset({ CameraId: 1 });
await delay(1000);
await xapi.Command.Camera.Preset.Store(
{ CameraId: 1, Name: "Overview", PresetId: 30 });
console.log('Preset 30 created')
}
}
//Declare your object for GMM communication
var primaryCodec = {};
//Run your init script asynchronously
async function init_intercodec() {
if (PRIMARY_CODEC_USER != '') {
if (PRIMARY_BOT_TOKEN == '')
primaryCodec = new GMM.Connect.IP(PRIMARY_CODEC_USER, PRIMARY_CODEC_PASSWORD, PRIMARY_CODEC_ADDRESS)
else
primaryCodec = new GMM.Connect.Webex(PRIMARY_BOT_TOKEN, PRIMARY_CODEC_ADDRESS)
}
}
const localCallout = new GMM.Connect.Local(module.name.replace('./', ''))
/////////////////////////////////////////////////////////////////////////////////////////
// VARIABLES
/////////////////////////////////////////////////////////////////////////////////////////
// roomCombined keeps the current state of join/split for the codec. It is normally also reflected in
// permanent storage (GMMMemory macro) in the JoinSplit_combinedState global
var roomCombined = false;
// on a secondary codec, secondarySelected reflects if it has been selected for joining from the primary or not. It is kept in persistent
// storage in the JoinSplit_secondarySelected key
var secondarySelected = true;
// below we are just initializing JoinSplit_secondary_settings, no need to fill out values
var JoinSplit_secondary_settings = {
UltrasoundMax: 0,
WakeupOnMotionDetection: '',
StandbyControl: '',
VideoMonitors: ''
}
let micArrays = {};
for (var i in config.monitorMics) {
micArrays[config.monitorMics[i].toString()] = [0, 0, 0, 0];
}
for (var i in config.ethernetMics) {
micArrays[config.ethernetMics[i].toString()] = [0, 0, 0, 0];
}
for (var i in config.usbMics) {
micArrays[config.usbMics[i].toString()] = [0, 0, 0, 0];
}
let lowWasRecalled = false;
let lastActiveHighInput = 0;
let lastSourceDict = { SourceID: '1' }
let allowSideBySide = true;
let sideBySideTimer = null;
let InitialCallTimer = null;
let allowCameraSwitching = false;
let allowNewSpeaker = true;
let newSpeakerTimer = null;
let manual_mode = true;
let primarySingleScreen = false;
let micHandler = () => void 0;
let micHandlerEthernet = () => void 0;
let micHandlerUSB = () => void 0;
let overviewShowDouble = true; //Always setting overviewShowDouble to true so we always evaluate the overview composition now
let inSideBySide = false;
let presenterTracking = false;
let presenterDetected = true;
let presenterTrackConfigured = false;
let presenterQAKeepComposition = false;
let qaCompositionTimer = null;
let usb_mode = false;
let webrtc_mode = false;
let primaryInCall = false;
let primaryInPreview = false;
let secondariesStatus = {};
let connector_to_codec_map = {}
let PRESENTER_QA_MODE = false
let ST_ACTIVE_CONNECTOR = 0;
let macroTurnedOnST = false;
let macroTurnedOffST = false;
let isOSTen = false;
let isOSEleven = false;
// Initial check for the Video Monitor configuration
async function check4_Video_Monitor_Config() {
const videoMonitorConfig = await xapi.Config.Video.Monitors.get()
return new Promise((resolve, reject) => {
if (videoMonitorConfig != 'Auto' && (videoMonitorConfig != 'Triple' && videoMonitorConfig != 'TriplePresentationOnly')) {
resolve(videoMonitorConfig)
} else {
reject(new Error('xConfiguration Video Monitors can not be set to Auto, Triple or TriplePresentationOnly for the Join/Split macro to work properly'))
}
})
}
async function getPresetCamera(prID) {
const value = await xapi.Command.Camera.Preset.Show({ PresetId: prID });
return (value.CameraId)
}
async function check4_Minimum_Version_Required(minimumOs) {
const reg = /^\D*(?<MAJOR>\d*)\.(?<MINOR>\d*)\.(?<EXTRAVERSION>\d*)\.(?<BUILDID>\d*).*$/i;
const minOs = minimumOs;
const os = await xapi.Status.SystemUnit.Software.Version.get();
console.log(os)
const x = (reg.exec(os)).groups;
const y = (reg.exec(minOs)).groups;
if (parseInt(x.MAJOR) > parseInt(y.MAJOR)) return true;
if (parseInt(x.MAJOR) < parseInt(y.MAJOR)) return false;
if (parseInt(x.MINOR) > parseInt(y.MINOR)) return true;
if (parseInt(x.MINOR) < parseInt(y.MINOR)) return false;
if (parseInt(x.EXTRAVERSION) > parseInt(y.EXTRAVERSION)) return true;
if (parseInt(x.EXTRAVERSION) < parseInt(y.EXTRAVERSION)) return false;
if (parseInt(x.BUILDID) > parseInt(y.BUILDID)) return true;
if (parseInt(x.BUILDID) < parseInt(y.BUILDID)) return false;
return false;
}
async function setCombinedMode(combinedValue) {
roomCombined = combinedValue;
await GMM.write.global('JoinSplit_combinedState', roomCombined).then(() => {
console.log({ Message: 'ChangeState', Action: 'Combined state stored.' })
})
}
async function storeSecondarySettings(ultraSoundMaxValue, wState, sState) {
let currentVideoMonitors = await xapi.Config.Video.Monitors.get();
if (currentVideoMonitors != 'Triple') { // only store if going from split to combined... if currentVideoMonitors=='Triple' then rooms are combined!!
JoinSplit_secondary_settings.UltrasoundMax = ultraSoundMaxValue;
JoinSplit_secondary_settings.WakeupOnMotionDetection = wState;
JoinSplit_secondary_settings.StandbyControl = sState;
JoinSplit_secondary_settings.VideoMonitors = currentVideoMonitors;
await GMM.write.global('JoinSplit_secondary_settings', JoinSplit_secondary_settings).then(() => {
console.log({ Message: 'ChangeState', Action: 'secondary settings for Ultrasound, WakeupOnMotionDetection , StandbyControl and VideoMonitors stored.' })
});
}
}
/**
* This will initialize the room state to Combined or Divided based on the setting in Memory Macro (persistent storage)
**/
async function initialCombinedJoinState() {
// Change all these to whatever is needed to trigger on the Primary when it goes into combined
if (roomCombined) {
console.log('Room is in Combined Mode');
setCombinedMode(true);
} else {
console.log('Room is in Divided Mode');
setCombinedMode(false);
}
}
/**
* This will initialize the room state to Combined or Divided based on the Pin 4 set by Primary
**/
async function checkCombinedStateSecondary() {
if (USE_GPIO_INTERCODEC) Promise.all([xapi.status.get('GPIO Pin 4')]).then(promises => {
let [pin4] = promises;
console.log('Pin4: ' + pin4.State);
// Change all these to whatever is needed to trigger on the Secondary when it goes into combined
if (pin4.State === 'Low' && (!secondarySelected)) {
console.log('Secondary Room is in Combined Mode');
setSecondaryToCombined();
} else {
if (!secondarySelected) console.log('Secondary Room is not selected in Primary...');
console.log('Secondary Room is in Divided Mode');
setSecondaryToSplit();
// since we are in secondary codec and in split configuration, we need to
// prepare to do basic switching to support PresenterTrack QA mode.
init_switching();
}
}).catch(e => console.debug(e));
else { // not using GPIO PINs
if (roomCombined) {
console.log('Secondary was set to Combined Mode in permanent storage');
setSecondaryToCombined();
}
else {
console.log('Secondary set to Divided Mode in permanent storage');
setSecondaryToSplit();
}
}
}
/**
* The following functions display a message on the touch panel to alert the users
* that the rooms are either being separated or joined together
**/
function alertJoinedScreen() {
xapi.command('UserInterface Message Alert Display', {
Title: 'Combining Rooms ...',
Text: 'Please wait',
Duration: 10,
});
}
function alertSplitScreen() {
xapi.command('UserInterface Message Alert Display', {
Title: 'Dividing Rooms ...',
Text: 'Please wait',
Duration: 10,
});
}
/////////////////////////////////////////////////////////////////////////////////////////
// DEFAULT ENDPOINT CONFIGURATIONS
// UPON SYSTEM STARTUP, these configurations should be run, They set a baseline for
// settings that we do not want the users to change.
/////////////////////////////////////////////////////////////////////////////////////////
async function setSecondaryDefaultConfig() {
console.log("Secondary default config being run");
// no longer will we store the VideoMonitors settings and others below when
// setting default config since that was storing wrong values when initializing in combined mode.
// we only store them when we are going to combined Mode to keep track of what they were initially when split
/*
//grab current secondary settings to store away in GMM
let ultraSoundMaxValue = await xapi.Config.Audio.Ultrasound.MaxVolume.get()
let standbyWakeupMotionValue=await xapi.Config.Standby.WakeupOnMotionDetection.get()
let standbyControlValue=await xapi.Config.Standby.Control.get()
// store it them in persistent storage, this also reads
// current JoinSplit_secondary_settings.VideoMonitors from codec
await storeSecondarySettings(ultraSoundMaxValue, standbyWakeupMotionValue, standbyControlValue);
*/
if (await isCodecPro()) {
xapi.config.set('Audio Input ARC 1 Mode', 'Off')
.catch((error) => { console.error("1" + error); });
xapi.config.set('Audio Input ARC 2 Mode', 'Off')
.catch((error) => { console.error("2" + error); });
xapi.config.set('Audio Input ARC 3 Mode', 'Off')
.catch((error) => { console.error("3" + error); });
}
// HDMI AUDIO SECTION
if (await isCodecPro()) xapi.Config.Audio.Output.ConnectorSetup.set('Manual');
xapi.config.set('Audio Input HDMI 1 Mode', 'Off')
.catch((error) => { console.error("4" + error); });
xapi.config.set('Audio Input HDMI 2 Mode', 'Off')
.catch((error) => { console.error("5" + error); });
xapi.Config.Audio.Input.HDMI[SECONDARY_VIDEO_TIELINE_INPUT_M1_FROM_PRI_ID].Mode.set('On')
.catch((error) => { console.error("5" + error); });;
// This allows us of USB Passthrough
// SET MICROPHONES
// MICROPHONES 1 THRU 8 ARE USER CONFIGURABLE
// THIS NEW VERSION 2 DESIGN USES EMBEDDED HDMI AUDIO FROM PRIMARY TO SECONDARY
// MUTE
xapi.config.set('Audio Microphones Mute Enabled', 'True')
.catch((error) => { console.error("21" + error); });
// OUTPUT ARC SECTION (FOR QUAD CAMERA ONLY)
xapi.config.set('Audio Output ARC 1 Mode', 'On')
.catch((error) => { console.error("22" + error); }); //TODO: This need to be turned off if you do not want to use the speakers on the QuadCam
// HDMI AUDIO OUTPUT
if (!SECONDARY_USE_MONITOR_AUDIO)
xapi.config.set('Audio Output HDMI 1 Mode', 'Off')
.catch((error) => { console.error("23" + error); });
xapi.config.set('Audio Output HDMI 2 Mode', 'Off')
.catch((error) => { console.error("24" + error); });
xapi.config.set('Audio Output HDMI 3 Mode', 'On')
.catch((error) => { console.error("25" + error); });
// This allows use of USB Passthrough
// CONFERENCE
xapi.config.set('Conference AutoAnswer Mode', 'Off')
.catch((error) => { console.error("36" + error); });
if (await isCodecPro()) {
// GPIO
if (USE_GPIO_INTERCODEC) {
xapi.config.set('GPIO Pin 2 Mode', 'InputNoAction')
.catch((error) => { console.error("39" + error); });
xapi.config.set('GPIO Pin 3 Mode', 'InputNoAction')
.catch((error) => { console.error("40" + error); });
xapi.config.set('GPIO Pin 4 Mode', 'InputNoAction')
.catch((error) => { console.error("41" + error); });
}
}
// PERIPHERALS
xapi.config.set('Peripherals Profile Cameras', 'Minimum1')
.catch((error) => { console.error("44" + error); });
xapi.config.set('Peripherals Profile TouchPanels', 'Minimum1')
.catch((error) => { console.error("45" + error); });
// SERIAL PORT
xapi.config.set('SerialPort LoginRequired', 'Off')
.catch((error) => { console.error("46" + error); });
xapi.config.set('SerialPort Mode', 'On')
.catch((error) => { console.error("47" + error); });
// VIDEO
xapi.config.set('Video DefaultMainSource', '1')
.catch((error) => { console.error("50" + error); });
//xapi.config.set('Video Monitors', JoinSplit_secondary_settings.VideoMonitors)
// .catch((error) => { console.error("51"+error); });
xapi.command('Video Input SetMainVideoSource', { ConnectorID: 1 })
.catch((error) => { console.error("52" + error); });
// VIDEO INPUT SECTION
// HDMI INPUT 1
xapi.config.set('Video Input Connector 1 CameraControl CameraId', '1')
.catch((error) => { console.error("54" + error); });
xapi.config.set('Video Input Connector 1 CameraControl Mode', 'On')
.catch((error) => { console.error("55" + error); });
xapi.config.set('Video Input Connector 1 InputSourceType', 'camera')
.catch((error) => { console.error("56" + error); });
xapi.config.set('Video Input Connector 1 Name', 'Quad Camera')
.catch((error) => { console.error("57" + error); });
xapi.config.set('Video Input Connector 1 PreferredResolution', '1920_1080_60')
.catch((error) => { console.error("58" + error); });
xapi.config.set('Video Input Connector 1 PresentationSelection', 'Manual')
.catch((error) => { console.error("59" + error); });
xapi.config.set('Video Input Connector 1 Quality', 'Motion')
.catch((error) => { console.error("60" + error); });
xapi.config.set('Video Input Connector 1 Visibility', 'Never')
.catch((error) => { console.error("61" + error); });
// Usually HDMI INPUTS 3 AND 4
// THESE ARE SCREENS 1 AND 2 FROM THE PRIMARY ROOM
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M1_FROM_PRI_ID + ' HDCP Mode', 'Off')
.catch((error) => { console.error("62" + error); });
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M1_FROM_PRI_ID + ' CameraControl Mode', 'Off')
.catch((error) => { console.error("63" + error); });
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M1_FROM_PRI_ID + ' InputSourceType', 'Other')
.catch((error) => { console.error("64" + error); });
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M1_FROM_PRI_ID + ' Name', 'Main Video Primary')
.catch((error) => { console.error("65" + error); });
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M1_FROM_PRI_ID + ' PreferredResolution', '3840_2160_30')
.catch((error) => { console.error("66" + error); });
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M1_FROM_PRI_ID + ' PresentationSelection', 'Manual')
.catch((error) => { console.error("67" + error); });
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M1_FROM_PRI_ID + ' Quality', 'Sharpness')
.catch((error) => { console.error("68" + error); });
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M1_FROM_PRI_ID + ' Visibility', 'Never')
.catch((error) => { console.error("69" + error); });
if (JoinSplit_secondary_settings.VideoMonitors == 'Dual' || JoinSplit_secondary_settings.VideoMonitors == 'DualPresentationOnly') {
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M2_FROM_PRI_ID + ' HDCP Mode', 'Off')
.catch((error) => { console.error("70" + error); });
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M2_FROM_PRI_ID + ' CameraControl Mode', 'Off')
.catch((error) => { console.error("71" + error); });
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M2_FROM_PRI_ID + ' InputSourceType', 'PC')
.catch((error) => { console.error("72" + error); });
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M2_FROM_PRI_ID + ' Name', 'Content Primary')
.catch((error) => { console.error("73" + error); });
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M2_FROM_PRI_ID + ' PreferredResolution', '3840_2160_30')
.catch((error) => { console.error("74" + error); });
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M2_FROM_PRI_ID + ' PresentationSelection', 'Manual')
.catch((error) => { console.error("75" + error); });
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M2_FROM_PRI_ID + ' Quality', 'Sharpness')
.catch((error) => { console.error("76" + error); });
xapi.config.set('Video Input Connector ' + SECONDARY_VIDEO_TIELINE_INPUT_M2_FROM_PRI_ID + ' Visibility', 'Never')
.catch((error) => { console.error("77" + error); });
}
// HDMI INPUT 2 (PRESENTER CAMERA) and 5 SHOULD BE CONFIGURED FROM THE WEB INTERFACE
// SDI INPUT 6 SHOULD ALSO BE CONFIGURED FROM THE WEB INTERFACE
// SDI INPUT 6 CAN BE USED FOR AN ADDITIONAL PTZ CAMERA (BUT NOT THE PRESENTER CAMERA)
// VIDEO OUTPUT SECTION
// THESE SHOULD NOT BE CONFIGURED BY THE INSTALLER
JoinSplit_secondary_settings.VideoMonitors = await xapi.Config.Video.Monitors.get()
switch (JoinSplit_secondary_settings.VideoMonitors) {
case 'Dual':
xapi.Config.Video.Output.Connector[1].MonitorRole.set('First');
xapi.Config.Video.Output.Connector[2].MonitorRole.set('Second');
break;
case 'DualPresentationOnly':
xapi.Config.Video.Output.Connector[1].MonitorRole.set('First');
xapi.Config.Video.Output.Connector[2].MonitorRole.set('PresentationOnly');
break;
case 'Single':
xapi.Config.Video.Output.Connector[1].MonitorRole.set('First');
xapi.Config.Video.Output.Connector[2].MonitorRole.set('First');
break;
}
xapi.config.set('Video Output Connector 3 MonitorRole', 'Third')
.catch((error) => { console.error("82" + error); });
xapi.config.set('Video Output Connector 3 Resolution', 'Auto')
.catch((error) => { console.error("83" + error); });
xapi.command('Video Matrix Reset')
.catch((error) => { console.error("84" + error); });
}
/////////////////////////////////////////////////////////////////////////////////////////
// START/STOP AUTOMATION FUNCTIONS
/////////////////////////////////////////////////////////////////////////////////////////
async function startAutomation() {
console.log('startAutomation');
//setting overall manual mode to false
manual_mode = false;
allowCameraSwitching = true;
if (inSideBySide) {
var currentSTCameraID = QUAD_CAM_ID;
let sourceDict = { SourceID: '0' }
sourceDict["SourceID"] = currentSTCameraID.toString();
xapi.Command.Video.Input.SetMainVideoSource(sourceDict);
inSideBySide = false;
console.log("cleared out side by side mode....")
}
try {
const webViewType = await xapi.Status.UserInterface.WebView.Type.get()
if (webViewType == 'WebRTCMeeting') webrtc_mode = true;
} catch (e) {
console.log('Unable to read WebView Type.. assuming not in webrtc mode')
}
if (isOSEleven) {
xapi.Config.Cameras.SpeakerTrack.DefaultBehavior.set(ST_DEFAULT_BEHAVIOR);
if (ST_DEFAULT_BEHAVIOR == 'Frames') xapi.Command.Cameras.SpeakerTrack.Frames.Activate();
else {
xapi.Command.Cameras.SpeakerTrack.Frames.Deactivate();
if (ST_DEFAULT_BEHAVIOR == 'Closeup') xapi.Config.Cameras.SpeakerTrack.Closeup.set('On');
}
}
// Always turn on SpeakerTrack when the Automation is started. It is also turned on when a call connects so that
// if it is manually turned off while outside of a call it goes back to the correct state
macroTurnedOnST = true;
if (webrtc_mode) {
setTimeout(() => { xapi.Command.Cameras.SpeakerTrack.Activate().catch(handleError) }, 2000) // in RoomOS11 Beta, if we do not delay turning on ST, something turns it back off
} else xapi.Command.Cameras.SpeakerTrack.Activate().catch(handleError);
// only initialize vumeters if side by side timer (overview timer) is not zero
// because, if zero, that means we will always be showing side by side (overview) mode
// and never need to switch to a specific camera
if (SIDE_BY_SIDE_TIME > 0) {
//registering vuMeter event handler for analog mics
if (config.monitorMics.length > 0) {
micHandler();
micHandler = () => void 0;
micHandler = xapi.event.on('Audio Input Connectors Microphone', (event) => {
if (typeof micArrays[event.id[0]] != 'undefined' && (!CHK_VUMETER_LOUDSPEAKER || event.LoudspeakerActivity < 1)) {
micArrays[event.id[0]].shift();
micArrays[event.id[0]].push(event.VuMeter);
// checking on manual_mode might be unnecessary because in manual mode,
// audio events should not be triggered
if (manual_mode == false) {
// invoke main logic to check mic levels ans switch to correct camera input
checkMicLevelsToSwitchCamera();
}
}
});
}
//registering vuMeter event handler for Ethernet mics
if (config.ethernetMics.length > 0) {
micHandlerEthernet();
micHandlerEthernet = () => void 0;
micHandlerEthernet = xapi.event.on('Audio Input Connectors Ethernet', (event) => {
//console.log(event)
event.SubId.forEach(submic => {
if (typeof micArrays[event.id + submic.id] != 'undefined') {
micArrays[event.id + submic.id].shift();
micArrays[event.id + submic.id].push(submic.VuMeter);
if (manual_mode == false) {
// invoke main logic to check mic levels ans switch to correct camera input
checkMicLevelsToSwitchCamera();
}
}
})
});
}
//registering vuMeter event handler for USB mics
if (config.usbMics.length > 0) {
micHandlerUSB();
micHandlerUSB = () => void 0;
micHandlerUSB = xapi.event.on('Audio Input Connectors USBMicrophone', (event) => {
//console.log(event)
if (typeof micArrays['10' + event.id] != 'undefined') {
micArrays['10' + event.id].shift();
micArrays['10' + event.id].push(event.VuMeter);
// checking on manual_mode might be unnecessary because in manual mode,
// audio events should not be triggered
if (manual_mode == false) {
// invoke main logic to check mic levels ans switch to correct camera input
checkMicLevelsToSwitchCamera();
}
}
});
}
// start VuMeter monitoring
console.log("Turning on VuMeter monitoring...")
for (var i in config.monitorMics) {
xapi.command('Audio VuMeter Start', {
ConnectorId: config.monitorMics[i],
ConnectorType: 'Microphone',