--Developed for Sennheiser by Forward Thinking Designs --Written by: Addison Burnside November 25, 2018 --Multi Channel Version updated by Drew Corbeil September 18, 2020 PluginInfo = { Name = "Sennheiser~SpeechLine Digital Wireless", Version = "2.0.0", Id = "da224172-09e6-48ba-82ff-30701508b6fc", Description = "Network Control of Sennheiser SpeechLine Digital Wireless Microphones", ShowDebug = false, --Author = "QSC", } function GetProperties() return { { Name = "Model", Type = "enum", Choices = {"Rack","MCR 2","MCR 4"}, Value = "Rack", } } end function RectifyProperties(props) return props end function GetPrettyName(props) return PluginInfo.Name end function GetColor(props) return { 207, 207, 207 } end function GetPins(props) local pins = {} return pins end function GetComponents(props) local comps = {} return comps end function GetWiring(props) local wiring = {} return wiring end function GetControls(props) local numChans = 1 if props["Model"].Value == "MCR 2" then numChans = 2 elseif props["Model"].Value == "MCR 4" then numChans = 4 end local ctrls = { { Name = "DiscoveryMode", Count = 1, ControlType = "Text", TextType = "ComboBox", UserPin = false, }, { Name = "Device", Count = 1, ControlType = "Text", TextType = "ComboBox", UserPin = true, PinStyle = "Both", }, { Name = "IPAddress", Count = 1, ControlType = "Text", TextType = "TextBox", UserPin = true, PinStyle = "Both", }, { Name = "Port", Count = 1, ControlType = "Knob", ControlUnit = "Integer", Min = 1.0, Max = 65535.0, UserPin = true, PinStyle = "Both", }, { Name = "Status", Count = 1, ControlType = "Indicator", IndicatorType = "Status", UserPin = true, PinStyle = "Output", }, { Name = "DeviceLocation", Count = 1, ControlType = "Indicator", IndicatorType = "Text", UserPin = true, PinStyle = "Output", }, { Name = "DeviceName", Count = numChans, ControlType = "Indicator", IndicatorType = "Text", UserPin = true, PinStyle = "Output", }, { Name = "DeviceID", Count = numChans, ControlType = "Button", ButtonType = "Trigger", UserPin = true, PinStyle = "Input", IconType = "Icon", Icon = "Eye", }, { Name = "LinkStatus", Count = numChans, ControlType = "Indicator", IndicatorType = "Led", UserPin = true, PinStyle = "Output" }, { Name = "AudioLevel", Count = numChans, ControlType = "Indicator", IndicatorType = "Meter", UserPin = true, PinStyle = "Output" }, { Name = "AudioSensitivity", Count = numChans, ControlType = "Text", TextType = "ComboBox", UserPin = true, PinStyle = "Both", }, { Name = "MuteMode", Count = numChans, ControlType = "Text", TextType = "ComboBox", UserPin = true, PinStyle = "Both", }, { Name = "RFLevelIcon", Count = 6*numChans, ControlType = "Indicator", IndicatorType = "Led", UserPin = false, }, { Name = "RFLevel", Count = numChans, ControlType = "Knob", ControlUnit = "Integer", Min = 0.0, Max = 6.0, UserPin = true, PinStyle = "Output", }, { Name = "MuteIcon", Count = numChans, ControlType = "Button", ButtonType = "Trigger", UserPin = false, IconType = "Image", Icon = "iVBORw0KGgoAAAANSUhEUgAAAIUAAACFCAYAAAB12js8AAAACXBIWXMAAC4jAAAuIwF4pT92AAAGYmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTExLTEzVDEwOjUzOjQ2LTA1OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOC0xMS0xM1QxMjo1ODoxNi0wNTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOC0xMS0xM1QxMjo1ODoxNi0wNTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpjM2FmNmMwMi0zOTJjLWY4NGUtYWIxOC02OTQ3NzdjYjBlZGIiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDoyMTYzZTc2NC00ZGE1LWY2NDgtOGJhZC1lY2FmMzNmZWIzYWYiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo5NzEyNGQxYi03NDE0LWVjNDEtYTgyOC00YTZlNTdkYWI2ZWUiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjk3MTI0ZDFiLTc0MTQtZWM0MS1hODI4LTRhNmU1N2RhYjZlZSIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0xM1QxMDo1Mzo0Ni0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjb252ZXJ0ZWQiIHN0RXZ0OnBhcmFtZXRlcnM9ImZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmciLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmMzYWY2YzAyLTM5MmMtZjg0ZS1hYjE4LTY5NDc3N2NiMGVkYiIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0xM1QxMjo1ODoxNi0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgWVV7sAAAI8SURBVHic7dxRjtowFIbRm6qrYf9bYT3pw1CV6R8ghIwIvucsILpyPtlP9jTPc8G1X+8egOMRBUEUBFEQREEQBUEUBFEQREEQBUEUBFEQREEQBUEUBFEQREEQBUEUBFEQREH4/e4B/pqmqc7n87vH2Mt8Op2mdw+xlZ1ifx9/Z0IU+/r4IKpEsachgqgSxV6GCaJKFHsYKogqUbxquCCqRPGKIYOoEsVWwwZRJYothg6iShTPGj6IKlE8o0UQVaJYq00QVaJYo1UQVaJ4pF0QVaK4p2UQVaK4pW0QVaJY0jqIKlH8r30QVaK4JogLUXwRxBVRCCJ0j0IQCzpHIYgbukYhiDs6RiGIB7pFIYgVDnNt8MJPO4BuOwUriIIgCoIoCKIgiIIgCoIoCKIgiIIgCoIoCKIgiIIgCoIoCKIgiIIgCoIoCKIgiIIgCoIoCKIgiIIgCoIoCKIgiIIgCoIoCKIgiIJwtOeNph/+vueTVui2U/x0dEPoFkWVMB7qGEWVMO7qGkWVMG7qHEWVMBZ1j6JKGEEUX4RxRRT/CONCFN8Jo0SxpH0YoljWOgxR3NY2DFHc1zIMUTzWLgxRrNMqDFGs1yYMUTynRRiieN7wYYhim6HDEMV2w4YhitcMGYYoXjdcGKLYx1BhiGI/w4Qhin0NEYYo9vfxYUzzfIybdNP08Wv5zVHWdYvDRMFxOD4IoiCIgiAKgigIoiCIgiAKgigIoiCIgiAKgigIoiCIgiAKgigIoiCIgiAKwh/OqC0Ejw6LlgAAAABJRU5ErkJggg==", }, { Name = "BatteryLevel", Count = numChans, ControlType = "Knob", ControlUnit = "Integer", Min = 0.0, Max = 100.0, UserPin = true, PinStyle = "Output", }, { Name = "BatteryIcon", Count = numChans, ControlType = "Button", ButtonType = "Trigger", UserPin = false, IconType = "Image", Icon = "iVBORw0KGgoAAAANSUhEUgAAAJYAAACWCAYAAAA8AXHiAAAACXBIWXMAAC4jAAAuIwF4pT92AAAGYmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTExLTEzVDEwOjUzOjQ2LTA1OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOC0xMS0yN1QxNTowMTo1OC0wNTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOC0xMS0yN1QxNTowMTo1OC0wNTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2MzM5NTQ4ZC1iN2Y3LTY4NGItYTA3MS1lNzkyODI1NGUzOWEiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpjNTAyODYzYi1iN2M0LWVmNGMtYjJmYy1hYjg4ZTc1Njk4NTIiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxYTJkMDkzOC1mMmM0LTkwNGQtOTIyYi1kMWFkZmMxZTk3NjMiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjFhMmQwOTM4LWYyYzQtOTA0ZC05MjJiLWQxYWRmYzFlOTc2MyIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0xM1QxMDo1Mzo0Ni0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjb252ZXJ0ZWQiIHN0RXZ0OnBhcmFtZXRlcnM9ImZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmciLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjYzMzk1NDhkLWI3ZjctNjg0Yi1hMDcxLWU3OTI4MjU0ZTM5YSIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0yN1QxNTowMTo1OC0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PnDVyKgAAAHjSURBVHic7d07joMwFEDR8Sj73x3r8bQpYilkuOR3TktDcfWQwTJjzvkDR/t99g3wmYRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCQuqwtjjDPvgze1OsN2Gda2bU695dquSeNRSEJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSWH6EfZEvE6ztlc4GJRUJYJIRFQlgkhEXi6FUhn2u1mrz5JsDEIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIuHUZO616z9JJhYJYZEQFglhkRAWiaNXhaf8vZPXZ2KREBYJYZEQFglhkRAWCWGREBYJYZEQFglhkRAWiTHn7e/GY+zaicqXWvazugD/4VFIQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSEReIPmnQWrBQNsAsAAAAASUVORK5CYII=", }, { Name = "BatteryTime", Count = numChans, ControlType = "Indicator", IndicatorType = "Text", UserPin = true, PinStyle = "Output", }, { Name = "MuteSwitchActive", Count = numChans, ControlType = "Button", ButtonType = "Toggle", UserPin = true, PinStyle = "Both", }, { Name = "LowCut", Count = numChans, ControlType = "Button", ButtonType = "Toggle", UserPin = true, PinStyle = "Both", }, { Name = "SoundProfile", Count = numChans, ControlType = "Text", TextType = "ComboBox", UserPin = true, PinStyle = "Both", }, { Name = "Pairing", Count = numChans, ControlType = "Button", ButtonType = "Toggle", UserPin = true, PinStyle = "Input", }, { Name = "State", Count = numChans, ControlType = "Indicator", IndicatorType = "Status", UserPin = true, PinStyle = "Output", }, } return ctrls end function GetControlLayout(props) layout = {} local numChans local gbHeight = 642 if props["Model"].Value == "Rack" then numChans = 1 gbHeight = 698 elseif props["Model"].Value == "MCR 2" then numChans = 2 elseif props["Model"].Value == "MCR 4" then numChans = 4 end graphics = { { Type = "GroupBox", Text = "", HTextAlign = "Left", StrokeWidth = 1, CornerRadius = 0, Fill = {34, 36, 37}, Position = { 0, 0 }, Size = { 22 + ((255 * numChans) + (8*(numChans - 1))), gbHeight } }, { Type = "GroupBox", Text = "", HTextAlign = "Left", StrokeWidth = 1, CornerRadius = 0, Fill = {25, 25, 25}, Position = { 0, 0 }, Size = { 22 + ((255 * numChans) + (8*(numChans - 1))), 107 } }, { --Sennheiser Logo Type = "Svg", Image = "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNzUuOTIiIGhlaWdodD0iNDIiIHN0eWxlPSIiPjxyZWN0IGlkPSJiYWNrZ3JvdW5kcmVjdCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgeD0iMCIgeT0iMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsLXJ1bGU6ZXZlbm9kZDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPlNlbm5oZWlzZXJfV2ViTG9nbzwvdGl0bGU+PGcgY2xhc3M9ImN1cnJlbnRMYXllciIgc3R5bGU9IiI+PHRpdGxlPkxheWVyIDE8L3RpdGxlPjxwYXRoIGlkPSJXb3J0bWFya2UiIGQ9Ik0xMDMuMDcsMCBoMiBWMTAuNSBoLTIuNTggbC0zLC00LjgzIGMtMC41MiwtMC44MzAwMDAwMDAwMDAwMDAxIC0xLC0xLjc0MDAwMDAwMDAwMDAwMDIgLTEsLTEuNzQwMDAwMDAwMDAwMDAwMiBoMCBWMTAuNSBoLTIgVjAgaDIuMzggbDMuNTYsNS43MiBhNy42Myw3LjYzIDAgMCAxIDAuNjEwMDAwMDAwMDAwMDAwMSwxLjE4IGgwIFYwIHptMjMuNzksNC4zIGgtMy41MzAwMDAwMDAwMDAwMDAyIFYwIGgtMi41NzAwMDAwMDAwMDAwMDAzIFYxMC40ODk5OTk5OTk5OTk5OTggaDIuNTcwMDAwMDAwMDAwMDAwMyBWNiBoMy41MzAwMDAwMDAwMDAwMDAyIHY0LjQ4IGgyLjU5IFYwIGgtMi41OSB2NC4zIHpNMTE1LjE5OTk5OTk5OTk5OTk5LDYuODk5OTk5OTk5OTk5OTk5IGgwIGE3LjYzLDcuNjMgMCAwIDAgLTAuNjEwMDAwMDAwMDAwMDAwMSwtMS4xOCBMMTExLDAgaC0yLjM4IFYxMC41IGgyIFYzLjk0MDAwMDAwMDAwMDAwMTMgaDAgczAuNDQwMDAwMDAwMDAwMDAwMDYsMC45MSAxLDEuNzQwMDAwMDAwMDAwMDAwMiBsMyw0LjgzIGgyLjU4IFYwIGgtMiBWNi44OTk5OTk5OTk5OTk5OTkgem0tMjYsLTEgaDMuMzUgVjQuMzU5OTk5OTk5OTk5OTk5IGgtMy4zNSBWMS42MjAwMDAwMDAwMDAwMDEgaDQuNDggVjAgaC03LjA2IFYxMC41IGg3LjE0IFY4Ljc1IGgtNC41NjAwMDAwMDAwMDAwMDA1IFY1Ljk0MDAwMDAwMDAwMDAwMSB6bS05LjMsLTMuNTQgYTAuNDcwMDAwMDAwMDAwMDAwMDMsMC40NzAwMDAwMDAwMDAwMDAwMyAwIDAgMSAwLjM5MDAwMDAwMDAwMDAwMDA3LC0wLjc3IGgzLjg0IFYwIEg3OS41MyBhMi4yNCwyLjI0IDAgMCAwIC0yLjMsMi4wOSBhMi40OCwyLjQ4IDAgMCAwIDEsMiBsMy40MiwzLjU4IGEwLjU4MDAwMDAwMDAwMDAwMDEsMC41ODAwMDAwMDAwMDAwMDAxIDAgMCAxIC0wLjM4MDAwMDAwMDAwMDAwMDA2LDEgSDc3IFYxMC41IGg0LjgyIEEyLjQ4LDIuNDggMCAwIDAgODQuMzksOCBhMi42OTAwMDAwMDAwMDAwMDA0LDIuNjkwMDAwMDAwMDAwMDAwNCAwIDAgMCAtMS4wOCwtMi4xMSB6bTcwLjg0LDAgYTAuNDcwMDAwMDAwMDAwMDAwMDMsMC40NzAwMDAwMDAwMDAwMDAwMyAwIDAgMSAwLjM5MDAwMDAwMDAwMDAwMDA3LC0wLjc3IGgzLjg0IFYwIGgtNC41NSBhMi4yNCwyLjI0IDAgMCAwIC0yLjMsMi4wOSBhMi40OCwyLjQ4IDAgMCAwIDEsMiBsMy40MiwzLjU4IGEwLjU4MDAwMDAwMDAwMDAwMDEsMC41ODAwMDAwMDAwMDAwMDAxIDAgMCAxIC0wLjM4MDAwMDAwMDAwMDAwMDA2LDEgaC00LjIzIFYxMC41IGg0LjgyIEEyLjQ4LDIuNDggMCAwIDAgMTU1LjIzLDggYTIuNjkwMDAwMDAwMDAwMDAwNCwyLjY5MDAwMDAwMDAwMDAwMDQgMCAwIDAgLTEuMDgsLTIuMTEgek0xNjAsNS45NDAwMDAwMDAwMDAwMDEgaDMuMzUgVjQuMzU5OTk5OTk5OTk5OTk5IEgxNjAgVjEuNjIwMDAwMDAwMDAwMDAxIGg0LjQ4IFYwIGgtNy4wNiBWMTAuNSBoNy4xNCBWOC43NSBIMTYwIFY1Ljk0MDAwMDAwMDAwMDAwMSB6bTE1LjksNC41NjAwMDAwMDAwMDAwMDA1IGgtMi44IGwtMiwtNC4zMTAwMDAwMDAwMDAwMDA1IEgxNzAgVjEwLjUgaC0yLjU4IFYwIGg0LjcxIGEzLDMgMCAwIDEgMy4zMjAwMDAwMDAwMDAwMDAzLDMgYTMsMyAwIDAgMSAtMiwyLjg0IHptLTIuNzEsLTcuMzggYTEuNDgsMS40OCAwIDAgMCAtMS42NDAwMDAwMDAwMDAwMDAxLC0xLjQ5IEgxNzAgdjMgaDEuNTcgQTEuNDksMS40OSAwIDAgMCAxNzMuMjEsMy4xMjAwMDAwMDAwMDAwMDEgek0xNDIuOTMsMTAuNSBoMi41OCBWMCBoLTIuNTggVjEwLjUgem0tNy4zNywtNC41NjAwMDAwMDAwMDAwMDA1IGgzLjM1IFY0LjM1OTk5OTk5OTk5OTk5OSBoLTMuMzUgVjEuNjIwMDAwMDAwMDAwMDAxIEgxNDAgVjAgSDEzMyBWMTAuNSBoNy4xNCBWOC43NSBoLTQuNTYwMDAwMDAwMDAwMDAwNSBWNS45NDAwMDAwMDAwMDAwMDEgeiIgY2xhc3M9IiIgZmlsbD0iI2ZmZmZmZiIgZmlsbC1vcGFjaXR5PSIxIi8+PHBhdGggaWQ9IkJpbGRtYXJrZSIgY2xhc3M9ImNscy0xIiBkPSJNMCwwIFY0MiBINTYgVjAgSDAgem0zMC44MiwzLjU2IGgwIGMtMy4zMywxLjI4IC02LjQ2LDQuMzQgLTksMTAgbC0yLjg3LDYuMyBDMTMuNzE5OTk5OTk5OTk5OTk5LDMxLjI1IDEwLjgwOTk5OTk5OTk5OTk5OSwzMy43IDMsMzMuNyBWMi44NCBoMjcuNyBBMC4zNzAwMDAwMDAwMDAwMDAwNSwwLjM3MDAwMDAwMDAwMDAwMDA1IDAgMCAxIDMwLjgyLDMuNTU5OTk5OTk5OTk5OTk4NyB6TTUzLDguMyBWMzkuMTYgSDI1LjMyIGEwLjM3MDAwMDAwMDAwMDAwMDA1LDAuMzcwMDAwMDAwMDAwMDAwMDUgMCAwIDEgLTAuMTMsLTAuNzIwMDAwMDAwMDAwMDAwMSBoMCBjMy4zMywtMS4yOCA2LjQ2LC00LjM0IDksLTEwIGwyLjg3LC02LjMgQzQyLjI4LDEwLjc1IDQ1LjE5LDguMyA1Myw4LjMgeiIgZmlsbD0iI2ZmZmZmZiIgZmlsbC1vcGFjaXR5PSIxIi8+PC9nPjwvc3ZnPg==", Position = { 26, 22 }, Size = { 94, 22 } }, { Type = "Label", Position = { 20 , 57 }, Size = { 203 , 36 }, Color = {255,255,255}, Text = "SpeechLine Digital Wireless", TextSize = 16, FontSize = 16, HTextAlign = "Center", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, }, } --Footer Layout--------------------------- if props["Model"].Value == "Rack" then table.insert(graphics, { Type = "Label", Position = { 11 , 583 }, Size = { 108 , 20 }, Color = {255,255,255}, Text = "Connection Mode", TextSize = 11, FontSize = 11, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, }) table.insert(graphics, { Type = "Label", Position = { 11 , 611 }, Size = { 91 , 20 }, Color = {255,255,255}, Text = "Device", TextSize = 11, FontSize = 11, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, }) table.insert(graphics, { Type = "Label", Position = { 11 , 639 }, Size = { 91 , 20 }, Color = {255,255,255}, Text = "IP Address", TextSize = 11, FontSize = 11, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, }) table.insert(graphics, { Type = "Label", Position = { 11 , 667 }, Size = { 61 , 20 }, Color = {255,255,255}, Text = "Location", TextSize = 11, FontSize = 11, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, }) layout["Status"] = { PrettyName = "Status", Position = { 11, 555 }, Size = { 254, 20 }, Color = {120, 123, 125}, Margin = 0, Radius = 2, Style = "Text", TextFontSize = 10, FontSize = 10, WordWrap = true, IsBold = false, IsReadOnly = false, StrokeWidth = 0, } layout["DiscoveryMode"] = { PrettyName = "Discovery Mode", Position = { 119, 583 }, Size = { 147, 20 }, Color = {255,255,255}, Style = "ComboBox", TextBoxStyle = "Normal", TextFontSize = 10, FontSize = 10, HTextAlign = "Center", VTextAlign = "Center", WordWrap = true, IsBold = false, Margin = 0, Radius = 2, IsReadOnly = false, TextColor = {0,0,0}, } layout["Device"] = { PrettyName = "Device", Position = { 119, 611 }, Size = { 147, 20 }, Color = {255,255,255}, Style = "ComboBox", TextBoxStyle = "Normal", TextFontSize = 10, FontSize = 10, HTextAlign = "Center", VTextAlign = "Center", WordWrap = true, IsBold = false, Margin = 0, Radius = 2, IsReadOnly = false, TextColor = {0,0,0}, } layout["IPAddress"] = { PrettyName = "IP Address", Position = { 119, 639 }, Size = { 100, 20 }, Color = {88,90,92}, Style = "TextBox", TextBoxStyle = "Normal", TextFontSize = 10, FontSize = 10, HTextAlign = "Center", VTextAlign = "Center", WordWrap = true, IsBold = false, Margin = 0, Radius = 2, IsReadOnly = false, TextColor = {255,255,255}, StrokeWidth = 0, } layout["Port"] = { PrettyName = "Port", Position = { 225, 639 }, Size = { 41, 20 }, Color = {88,90,92}, Style = "Textbox", TextBoxStyle = "Normal", TextFontSize = 10, FontSize = 10, Margin = 0, Radius = 2, StrokeWidth = 0, IsReadOnly = false, TextColor = {255,255,255}, } layout["DeviceLocation"] = { PrettyName = "Location", Position = { 119, 667 }, Size = { 147, 20 }, Color = {88,90,92}, Margin = 0, Radius = 2, Style = "Text", TextBoxStyle = "Normal", --Updated For v8 StrokeWidth = 0, --Updated For v8 TextFontSize = 10, FontSize = 10, HTextAlign = "Center", VTextAlign = "Center", WordWrap = true, IsBold = false, IsReadOnly = false, UnlinkTextColor = true, --Updated For v8 TextColor = {255,255,255}, } else table.insert(graphics, { Type = "Label", Position = { 11 , 555 }, Size = { 108 , 20 }, Color = {255,255,255}, Text = "Connection Mode", TextSize = 11, FontSize = 11, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, }) table.insert(graphics, { Type = "Label", Position = { 11 , 583 }, Size = { 91 , 20 }, Color = {255,255,255}, Text = "Device", TextSize = 11, FontSize = 11, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, }) table.insert(graphics, { Type = "Label", Position = { 11 , 611 }, Size = { 91 , 20 }, Color = {255,255,255}, Text = "IP Address", TextSize = 11, FontSize = 11, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, }) table.insert(graphics, { Type = "Label", Position = { 274 , 583 }, Size = { 61 , 20 }, Color = {255,255,255}, Text = "Location", TextSize = 11, FontSize = 11, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, }) layout["Status"] = { PrettyName = "Status", Position = { 275, 555 }, Size = { 254, 20 }, Color = {120, 123, 125}, Margin = 0, Radius = 2, Style = "Text", TextFontSize = 10, FontSize = 10, WordWrap = true, IsBold = false, IsReadOnly = false, StrokeWidth = 0, } layout["DiscoveryMode"] = { PrettyName = "Discovery Mode", Position = { 119, 555 }, Size = { 147, 20 }, Color = {255,255,255}, Style = "ComboBox", TextBoxStyle = "Normal", TextFontSize = 10, FontSize = 10, HTextAlign = "Center", VTextAlign = "Center", WordWrap = true, IsBold = false, Margin = 0, Radius = 2, IsReadOnly = false, TextColor = {0,0,0}, } layout["Device"] = { PrettyName = "Device", Position = { 119, 583 }, Size = { 147, 20 }, Color = {255,255,255}, Style = "ComboBox", TextBoxStyle = "Normal", TextFontSize = 10, FontSize = 10, HTextAlign = "Center", VTextAlign = "Center", WordWrap = true, IsBold = false, Margin = 0, Radius = 2, IsReadOnly = false, TextColor = {0,0,0}, } layout["IPAddress"] = { PrettyName = "IP Address", Position = { 119, 611 }, Size = { 100, 20 }, Color = {88,90,92}, Style = "TextBox", TextBoxStyle = "Normal", TextFontSize = 10, FontSize = 10, HTextAlign = "Center", VTextAlign = "Center", WordWrap = true, IsBold = false, Margin = 0, Radius = 2, IsReadOnly = false, TextColor = {255,255,255}, StrokeWidth = 0, } layout["Port"] = { PrettyName = "Port", Position = { 225, 611 }, Size = { 41, 20 }, Color = {88,90,92}, Style = "Textbox", TextBoxStyle = "Normal", TextFontSize = 10, FontSize = 10, Margin = 0, Radius = 2, StrokeWidth = 0, IsReadOnly = false, TextColor = {255,255,255}, } layout["DeviceLocation"] = { PrettyName = "Location", Position = { 335, 583 }, Size = { 194, 20 }, Color = {88,90,92}, Margin = 0, Radius = 2, Style = "Text", TextBoxStyle = "Normal", --Updated For v8 StrokeWidth = 0, --Updated For v8 TextFontSize = 10, FontSize = 10, HTextAlign = "Center", VTextAlign = "Center", WordWrap = true, IsBold = false, IsReadOnly = false, UnlinkTextColor = true, --Updated For v8 TextColor = {255,255,255}, } end local function drawChan(x, y, chanNum) controlNameSuffix = string.format(" %d", chanNum) if props["Model"].Value == "Rack" then controlNameSuffix = "" end table.insert( graphics, { Type = "GroupBox", Text = "", HTextAlign = "Left", StrokeWidth = 1, CornerRadius = 0, Fill = {25, 25, 25}, Position = { x+0, y+0 }, Size = { 255, 31 } }) table.insert( graphics, { Type = "GroupBox", Text = "", HTextAlign = "Left", StrokeWidth = 1, CornerRadius = 0, Fill = {45, 47, 49}, Position = { x+0, y+31 }, Size = { 255, 121 } }) table.insert( graphics, { Type = "GroupBox", Text = "", HTextAlign = "Left", StrokeWidth = 1, CornerRadius = 0, Fill = {45, 47, 49}, Position = { x+0, y+152 }, Size = { 255, 274 } }) table.insert(graphics, {--left bracket Type = "Svg", Image = "PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSIzNyIgc3R5bGU9IiI+PHBhdGggZmlsbD0iI2RhZGFkYSIgZmlsbC1vcGFjaXR5PSIxIiBzdHJva2U9Im5vbmUiIHN0cm9rZS1vcGFjaXR5PSIxIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1kYXNoYXJyYXk9Im5vbmUiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtZGFzaG9mZnNldD0iIiBmaWxsLXJ1bGU9Im5vbnplcm8iIG9wYWNpdHk9IjEiIG1hcmtlci1zdGFydD0iIiBtYXJrZXItbWlkPSIiIG1hcmtlci1lbmQ9IiIgaWQ9InN2Z18xMiIgZD0iTTAsIDAgTDksIDAgTDksIDUgTDUsIDUgTDUsIDMyIEw5LCAzMiBMOSwgMzcgTDAsIDM3IFoiIHN0eWxlPSJjb2xvcjogI2RhZGFkYTsiIGNsYXNzPSIiLz48L3N2Zz4=", Position = { x+133, y+114 }, Size = { 6, 25 } }) table.insert(graphics, {--Right Bracket Type = "Svg", Image = "PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSIzNyIgc3R5bGU9IiI+PHBhdGggZmlsbD0iI2RhZGFkYSIgZmlsbC1vcGFjaXR5PSIxIiBzdHJva2U9Im5vbmUiIHN0cm9rZS1vcGFjaXR5PSIxIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1kYXNoYXJyYXk9Im5vbmUiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtZGFzaG9mZnNldD0iIiBmaWxsLXJ1bGU9Im5vbnplcm8iIG9wYWNpdHk9IjEiIG1hcmtlci1zdGFydD0iIiBtYXJrZXItbWlkPSIiIG1hcmtlci1lbmQ9IiIgaWQ9InN2Z18xMiIgZD0iTTAsIDAgTDksIDAgTDksIDM3IEwwLCAzNyBMMCwgMzIgTDQsIDMyIEw0LCA1IEwwLCA1IFoiIHN0eWxlPSJjb2xvcjogI2RhZGFkYTsiIGNsYXNzPSIiLz48L3N2Zz4=", Position = { x+214, y+114 }, Size = { 6, 25 } }) table.insert(graphics, { Type = "Label", Position = { x+11 , y+45 }, Size = { 72 , 27 }, Color = {255, 255, 255}, Text = "RF Signal", TextSize = 13, FontSize = 13, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, TextColor = {255, 255, 255}, }) table.insert(graphics, { Type = "Label", Position = { x+11 , y+79 }, Size = { 72 , 27 }, Color = {255, 255, 255}, Text = "Battery", TextSize = 13, FontSize = 13, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, TextColor = {255, 255, 255}, }) table.insert(graphics, { Type = "Label", Position = { x+11 , y+114 }, Size = { 72 , 27 }, Color = {255, 255, 255}, Text = "Audio Level", TextSize = 13, FontSize = 13, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, TextColor = {255, 255, 255}, }) table.insert(graphics, { Type = "Label", Position = { x+11 , y+175 }, Size = { 103 , 27 }, Color = {255, 255, 255}, Text = "Audio Sensitivity", TextSize = 13, FontSize = 13, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, TextColor = {255, 255, 255}, }) table.insert(graphics, { Type = "Label", Position = { x+11 , y+210 }, Size = { 103 , 27 }, Color = {255, 255, 255}, Text = "Mute Mode", TextSize = 13, FontSize = 13, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, TextColor = {255, 255, 255}, }) table.insert(graphics, { Type = "Label", Position = { x+11 , y+245 }, Size = { 83 , 27 }, Color = {255, 255, 255}, Text = "Mute", TextSize = 13, FontSize = 13, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, TextColor = {255, 255, 255}, }) table.insert(graphics, { Type = "Label", Position = { x+11 , y+280 }, Size = { 83 , 27 }, Color = {255, 255, 255}, Text = "Low-cut", TextSize = 13, FontSize = 13, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, TextColor = {255, 255, 255}, }) table.insert(graphics, { Type = "Label", Position = { x+11 , y+314 }, Size = { 83 , 27 }, Color = {255, 255, 255}, Text = "Sound profile", TextSize = 13, FontSize = 13, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, TextColor = {255, 255, 255}, }) table.insert(graphics, { Type = "Label", Position = { x+11 , y+348 }, Size = { 83 , 27 }, Color = {255, 255, 255}, Text = "Pairing", TextSize = 13, FontSize = 13, HTextAlign = "Left", VTextAlign = "Center", IsBold = false, StrokeWidth = 0, CornerRadius = 0, TextColor = {255, 255, 255}, }) layout["LinkStatus"..controlNameSuffix] = { PrettyName = "Channel "..chanNum.."~Link Status", Position = { x+11, y+4 }, Size = { 20, 20 }, Color = { 84, 222, 29 }, Margin = 0, Radius = 4, StrokeWidth = 0, Style = "Button", ButtonStyle = "Toggle", UnlinkOffColor = true, OffColor = { 238, 34, 17 }, IsReadOnly = false, } layout["DeviceName"..controlNameSuffix] = { PrettyName = "Channel "..chanNum.."~Name", Position = { x+41, y+1 }, Size = { 173, 27 }, Color = { 24, 24, 24}, --Updated For v8 Margin = 0, Radius = 0, Style = "Text", TextBoxStyle = "Normal", --Updated For v8 StrokeWidth = 0, --Updated For v8 TextFontSize = 14, FontSize = 14, HTextAlign = "Left", VTextAlign = "Center", WordWrap = true, IsBold = false, IsReadOnly = false, UnlinkTextColor = true, --Updated For v8 TextColor = {212, 213, 213}, --Updated For v8 } layout["DeviceID"..controlNameSuffix] = { PrettyName = "Channel "..chanNum.."~Identify", Position = { x+220, y+4 }, Size = { 25, 22 }, Color = {255, 255, 255}, Style = "Button", ButtonStyle = "Trigger", --Legend = "", HTextAlign = "Center", VTextAlign = "Center", --UnlinkOffColor = true, --OffColor = { 91, 91, 93 }, Margin = 0, Radius = 1, StrokeWidth = 0, IsReadOnly = false, } local ind = ((chanNum-1)*6) + 1 for i=ind, ind+5 do local ind2 = i%6 if ind2 == 0 then ind2 = 6 end layout[tostring("RFLevelIcon " .. i)] = { PrettyName = "Channel "..chanNum.."~RF Level Meter "..ind2, Position = { (x+114)+(((ind2)-1)*13), (y+58)-(((ind2)-1)*2) }, Size = { 8, (6+(((ind2)-1)*2)) }, Color = {0, 150, 214}, BackgroundColor = {212, 213, 213}, Margin = 0, Radius = 0, StrokeWidth = 0, Style = "Meter", MeterStyle = "Level", UnlinkOffColor = false, IsReadOnly = false, } end layout["BatteryIcon"..controlNameSuffix] = { PrettyName = "Channel "..chanNum.."~Battery Icon", Position = { x+105, y+82 }, Size = { 23, 23 }, Color = { 45, 47, 49 }, Style = "Button", ButtonStyle = "Trigger", Legend = "", HTextAlign = "Center", VTextAlign = "Center", UnlinkOffColor = true, OffColor = { 45, 47, 49 }, StrokeColor = { 51, 51, 51}, StrokeWidth = 0, Margin = 2, Radius = 2, IsReadOnly = false, ButtonVisualStyle = "Flat", } layout["BatteryTime"..controlNameSuffix] = { PrettyName = "Channel "..chanNum.."~Battery Remaining", Position = { x+130, y+80 }, Size = { 59, 27 }, Color = {212, 213, 213}, Margin = 0, Radius = 0, Style = "Text", TextBoxStyle = "NoBackground", TextFontSize = 12, FontSize = 12, HTextAlign = "Left", VTextAlign = "Center", WordWrap = true, IsBold = false, IsReadOnly = false, TextColor = {255,255,255}, } layout["MuteIcon"..controlNameSuffix] = { PrettyName = "Channel "..chanNum.."~Mute Icon", Position = { x+103, y+114 }, Size = { 27, 25 }, Color = { 45, 47, 49 }, Style = "Button", ButtonStyle = "Trigger", Legend = "", HTextAlign = "Center", VTextAlign = "Center", UnlinkOffColor = true, OffColor = { 45, 47, 49 }, StrokeColor = { 51, 51, 51}, StrokeWidth = 0, Margin = 0, Radius = 0, IsReadOnly = false, ButtonVisualStyle = "Flat", } layout["AudioLevel"..controlNameSuffix] = { PrettyName = "Channel "..chanNum.."~Audio Level", Position = { x+141, y+120 }, Size = { 71, 13 }, Color = { 59, 132, 224 }, BackgroundColor = { 212, 213, 213}, Margin = 0, Radius = 0, StrokeWidth = 0, Style = "Meter", MeterStyle = "Level", ShowTextbox = false, IsReadOnly = false, } layout["AudioSensitivity"..controlNameSuffix] = { PrettyName = "Channel "..chanNum.."~Audio Sensitivity", Position = { x+122, y+175 }, Size = { 122, 23 }, Color = {255,255,255}, Style = "ComboBox", TextBoxStyle = "Normal", TextFontSize = 12, FontSize = 12, HTextAlign = "Left", VTextAlign = "Center", WordWrap = true, IsBold = false, Margin = 0, Radius = 2, IsReadOnly = false, } layout["MuteMode"..controlNameSuffix] = { PrettyName = "Channel "..chanNum.."~Mute Mode", Position = { x+122, y+210 }, Size = { 122, 23 }, Color = {255,255,255}, Style = "ComboBox", TextBoxStyle = "Normal", TextFontSize = 12, FontSize = 12, HTextAlign = "Left", VTextAlign = "Center", WordWrap = true, IsBold = false, Margin = 0, Radius = 2, IsReadOnly = false, } layout["MuteSwitchActive"..controlNameSuffix] = { PrettyName = "Channel "..chanNum.."~Mute", Position = { x+122, y+245 }, Size = { 122, 25 }, Color = { 0, 150, 214 }, Style = "Button", ButtonStyle = "Toggle", Legend = "Deactivated", TextFontSize = 12, FontSize = 12, HTextAlign = "Center", VTextAlign = "Center", UnlinkOffColor = true, OffColor = { 25, 25, 25 }, StrokeWidth = 0, Margin = 0, Radius = 2, IsReadOnly = false, } layout["LowCut"..controlNameSuffix] = { PrettyName = "Channel "..chanNum.."~Low Cut", Position = { x+122, y+280 }, Size = { 122, 25 }, Color = { 0, 150, 214 }, Style = "Button", ButtonStyle = "Toggle", Legend = "Off", TextFontSize = 12, FontSize = 12, HTextAlign = "Center", VTextAlign = "Center", UnlinkOffColor = true, OffColor = { 25, 25, 25 }, StrokeWidth = 0, Margin = 0, Radius = 2, IsReadOnly = false, } layout["SoundProfile"..controlNameSuffix] = { PrettyName = "Channel "..chanNum.."~Sound Profile", Position = { x+122, y+314 }, Size = { 122, 23 }, Color = {255,255,255}, Style = "ComboBox", TextBoxStyle = "Normal", TextFontSize = 12, FontSize = 12, HTextAlign = "Left", VTextAlign = "Center", WordWrap = true, IsBold = false, Margin = 0, Radius = 2, IsReadOnly = false, } layout["Pairing"..controlNameSuffix] = { PrettyName = "Channel "..chanNum.."~Pairing", Position = { x+122, y+348 }, Size = { 122, 25 }, Color = { 0, 150, 214 }, Style = "Button", ButtonStyle = "Toggle", Legend = "Start", TextFontSize = 12, FontSize = 12, HTextAlign = "Center", VTextAlign = "Center", UnlinkOffColor = true, OffColor = { 25, 25, 25 }, StrokeWidth = 0, Margin = 0, Radius = 2, IsReadOnly = false, } layout["State"..controlNameSuffix] = { PrettyName = "Channel "..chanNum.."~Status", Position = { x+11, y+384 }, Size = { 233, 20 }, Color = {120, 123, 125}, Margin = 0, Radius = 2, Style = "Text", TextFontSize = 9, FontSize = 9, WordWrap = true, IsBold = false, IsReadOnly = false, StrokeWidth = 0, } layout["RFLevel"..controlNameSuffix] = { Style = "None", PrettyName = "Channel "..chanNum.."~RF Level", Position = { 0,0 }, Size = { 0, 0 }, Color = { 24, 24, 24}, StrokeWidth = 0, TextBoxStyle = "NoBackground", } layout["BatteryLevel"..controlNameSuffix] = { Style = "None", PrettyName = "Channel "..chanNum.."~Battery Level", Position = { 0,0 }, Size = { 0, 0 }, Color = { 24, 24, 24}, StrokeWidth = 0, TextBoxStyle = "NoBackground", } end for i=1, numChans, 1 do drawChan(11+((i-1)*263), 118, i) end return layout, graphics end if Controls then --Multi Channel Script ----------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------- --[[ VARIABLE NAMING CONVENTIONS CONSTANT_VARIABLES are defined in the head of the code and use CAPITAL_SNAKE_CASE Assignable_Properties are declared in the head, but assigned by a function and use the Upper_Heart_Beat_Case ObjectVariables are declared in the head, but may be manipulated by several functions and use the UpperCamelCase holdingVariables are declared in the head, but are manipulated by several functions and use the lowerCamelCase namedFunctions also use lowerCamelCase localvariables are used only in functions and loops for temporary assignment and usage and use lowercase ]]-------------------------------------------------------------------------------------------------------------- --CONSTANT_DECLARATIONS RACK = Properties["Model"].Value == "Rack" MCR2 = Properties["Model"].Value == "MCR 2" MCR4 = Properties["Model"].Value == "MCR 4" DEBUG = false ICON_NOMUTE = "iVBORw0KGgoAAAANSUhEUgAAAIUAAACFCAYAAAB12js8AAAACXBIWXMAAC4jAAAuIwF4pT92AAAGYmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTExLTEzVDEwOjUzOjQ2LTA1OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOC0xMS0xM1QxMjo1ODoxNi0wNTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOC0xMS0xM1QxMjo1ODoxNi0wNTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpjM2FmNmMwMi0zOTJjLWY4NGUtYWIxOC02OTQ3NzdjYjBlZGIiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDoyMTYzZTc2NC00ZGE1LWY2NDgtOGJhZC1lY2FmMzNmZWIzYWYiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo5NzEyNGQxYi03NDE0LWVjNDEtYTgyOC00YTZlNTdkYWI2ZWUiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjk3MTI0ZDFiLTc0MTQtZWM0MS1hODI4LTRhNmU1N2RhYjZlZSIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0xM1QxMDo1Mzo0Ni0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjb252ZXJ0ZWQiIHN0RXZ0OnBhcmFtZXRlcnM9ImZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmciLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmMzYWY2YzAyLTM5MmMtZjg0ZS1hYjE4LTY5NDc3N2NiMGVkYiIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0xM1QxMjo1ODoxNi0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgWVV7sAAAI8SURBVHic7dxRjtowFIbRm6qrYf9bYT3pw1CV6R8ghIwIvucsILpyPtlP9jTPc8G1X+8egOMRBUEUBFEQREEQBUEUBFEQREEQBUEUBFEQREEQBUEUBFEQREEQBUEUBFEQREH4/e4B/pqmqc7n87vH2Mt8Op2mdw+xlZ1ifx9/Z0IU+/r4IKpEsachgqgSxV6GCaJKFHsYKogqUbxquCCqRPGKIYOoEsVWwwZRJYothg6iShTPGj6IKlE8o0UQVaJYq00QVaJYo1UQVaJ4pF0QVaK4p2UQVaK4pW0QVaJY0jqIKlH8r30QVaK4JogLUXwRxBVRCCJ0j0IQCzpHIYgbukYhiDs6RiGIB7pFIYgVDnNt8MJPO4BuOwUriIIgCoIoCKIgiIIgCoIoCKIgiIIgCoIoCKIgiIIgCoIoCKIgiIIgCoIoCKIgiIIgCoIoCKIgiIIgCoIoCKIgiIIgCoIoCKIgiIJwtOeNph/+vueTVui2U/x0dEPoFkWVMB7qGEWVMO7qGkWVMG7qHEWVMBZ1j6JKGEEUX4RxRRT/CONCFN8Jo0SxpH0YoljWOgxR3NY2DFHc1zIMUTzWLgxRrNMqDFGs1yYMUTynRRiieN7wYYhim6HDEMV2w4YhitcMGYYoXjdcGKLYx1BhiGI/w4Qhin0NEYYo9vfxYUzzfIybdNP08Wv5zVHWdYvDRMFxOD4IoiCIgiAKgigIoiCIgiAKgigIoiCIgiAKgigIoiCIgiAKgigIoiCIgiAKwh/OqC0Ejw6LlgAAAABJRU5ErkJggg==" ICON_MUTE = "iVBORw0KGgoAAAANSUhEUgAAAIUAAACFCAYAAAB12js8AAAACXBIWXMAAC4jAAAuIwF4pT92AAAGYmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTExLTEzVDEwOjUzOjQ2LTA1OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOC0xMS0xM1QxMjo1ODozNC0wNTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOC0xMS0xM1QxMjo1ODozNC0wNTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1NzFhMjk4Yi01YTQ3LTRjNGUtYmE5My00ODg4MzY3YWQ0ZWYiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpjYjUyYzE0Ny1lNDRjLWE1NDgtYjMyYS0zNDMzYTlkODczMTMiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpmMmRiMjhhNS0wZmJmLTE3NDItYWI3OS03YmJjOWNmMDg0ZDEiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmYyZGIyOGE1LTBmYmYtMTc0Mi1hYjc5LTdiYmM5Y2YwODRkMSIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0xM1QxMDo1Mzo0Ni0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjb252ZXJ0ZWQiIHN0RXZ0OnBhcmFtZXRlcnM9ImZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmciLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjU3MWEyOThiLTVhNDctNGM0ZS1iYTkzLTQ4ODgzNjdhZDRlZiIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0xM1QxMjo1ODozNC0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PhK6te4AAAMaSURBVHic7dxLcqswEEbh5tbdDftfir0dMogHxD/YPPToVp8zdiECX8kEkKdlWYxo3b/eO0D+AgVJoCAJFCSBgiRQkAQKkkBBEihIAgVJoCAJFCSBgiRQkAQKkkBBEihIAgVJoCAJFCSBgqT/vQZ+Pp+9hq7VMs/z1HzQCm/jM1OUaah1EqC431AgzEBxt+FAmHW8phggAfF4PGqO0ex6hZniWi1miPcxms1KoDhfDxAtxwbFyXqCaLYPoDieBxBnP3cpUHxvMV8grn7+cKD4XKuLu6vjVNk/UOznHUS1QLFdWhBmoNgqEogqN7RA8bf0IMxAsQ4Qr0DxGyBWgQIQUnYUgNgoM4rSIPZOWigQZnlRAOJDGVEA4kvZUADiQJlQAOJgWVAA4kQZUADiZKOjAMSFRkYBiIv1XPfh8l2CndKAMBt7pihVKhBmoPhWOhBmoPhUShBmoNgrLQgzUGyVGoQZKN5LD8IMFOsA8QoUvwFiFSgAIWVHAYiNMqMAxE5ZUQDiQxlRAOJL2VAA4kCZUADiYFlQAOJEGVAA4mSjowDEhUZG0RpEpNcLPzYqCkDcaEQUgLjZaCgAUaCRUACiUKOgAETBRkABiMJFRwGICkVGAYhKRUUBiIpFRAGIykVDAYgGRUIBiEZFQQGIhvX80ZK7j54BUakoM8V7gKhYRBSAqFw0FIBoUCQUgGhUFBSAaFgEFIBonHcUgOiQZxQ1X8Mf9vX8EnlF0WJdBjB28oii5UIdYGzkDUWPlVvAeMsTip5L+YCxygsKD2s7gfHKAwoPIEpvJ3S9UXgCUWt74eqJwiOI2tsNUU8U3q8B0sLo/fXh/b+FlDB6ozDzf18hHQwPKMz834GcOo7dPC8ozGI8q/CwD9XzhMIsxlNNT/tSJW8ozPSgezwJHvepWNOy9HnfZJr6HdfH41FqU38O3jzPzf+oGufP40wRqSFnDFDcbzgYoCjTUDBSXlOMVI3z1w0F+Y2vD5JAQRIoSAIFSaAgCRQkgYIkUJAECpJAQRIoSAIFSaAgCRQkgYIkUJAECpJAQRIoSAIFST87VKOOEE/QuwAAAABJRU5ErkJggg==" ICON_CIRCLEMUTE = "iVBORw0KGgoAAAANSUhEUgAAAIUAAACFCAYAAAB12js8AAAACXBIWXMAAC4jAAAuIwF4pT92AAAGYmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTExLTEzVDEwOjUzOjQ2LTA1OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOC0xMS0xM1QxMjo1ODo0Ni0wNTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOC0xMS0xM1QxMjo1ODo0Ni0wNTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxZTdmMzFiOS1jODdlLTUzNDgtOGI5NS01N2FkOWJhZGViMjgiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo5NTg1MDcxNy0yODc2LWY5NGYtOWJhNy04ZjYzNGI5MzM1NWEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxYWRhMTg1Yy01ZjdlLTQxNDAtODIzYS1hOTVhNWRjM2YxNWUiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjFhZGExODVjLTVmN2UtNDE0MC04MjNhLWE5NWE1ZGMzZjE1ZSIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0xM1QxMDo1Mzo0Ni0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjb252ZXJ0ZWQiIHN0RXZ0OnBhcmFtZXRlcnM9ImZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmciLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjFlN2YzMWI5LWM4N2UtNTM0OC04Yjk1LTU3YWQ5YmFkZWIyOCIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0xM1QxMjo1ODo0Ni0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsRWJqEAAATjSURBVHic7Z1BcuQgDEXlqdwm979KrtNZzFDFGLsbgyS+xH+rWUx10PeLwG0HjtfrJYTU/Fk9AIIHpSANlII0fJV//Pz8rByHJZqLpkPxs2D4/v6Wem359eb/RsR61Xz1+elEiS4Fwq3TeQzhJYkoBYII76jHF1KQKFKgi3BHSEHQpYgqwxWlFng5UKXIJMMZeDnQpMgswxlYOVCk2EmGM3ByIHyjubMQNTA5rOwUMCEAAdE1VknhKYRmwF7jfslCMbyl8AjVMszzZ1vWs6xreEphGeCq36r651rV5941vKSwCAxmtf4PS0FcxbCWYgcZrihj1KzfbTqxvCXVFuKQGELUWIzZfF1mJYX2iy3RZDijXYOpGBZSaA04ggwveVavZk1mYmhLoTHQCDKIzNUKLYamFFpCRABJfnUxtKSgEOPAiYHwlDSKDCJ287jFLewwGp0CYW71AP0rerXxzUpBIfRZLsaMFBTCjqVirHjJhkL0sSynUSlGw6IQzxjNa2rsI1JQCF/cxfCaPijEHK75PZViJDAKocNIjkP1PJECOTANstb3uC7r6SNKl4gihEuevVJknjaiCFEwn0YQ/hhoJdGEcKFHiqxdIrIQpt3ColNQCB/Mcv4kRYbwzmSsqZeu2rU7BXqXyCaESd7vpMgWYLZ6RvmYg2anQO4SmYVQz32HW9LMQphwJ8XTIFG7xC5CPM3/bS6ZO8UuQqijIQVil9hRCLXrcPWKv+duLWQdt9sbzHYKxC5BJsm8ptgRlV9SSkEazlJwnt+Ly+s90ym4nsBk+rpw+iANlII0UArSUEvxdO8mgsuT69Ncd3YK0kApSAOlIA2UgjRQCtJAKUgDpSANlII0UArSQClIA6UgDZSCNFAK0kApSAOlIA2UgjRQCtJAKUhDLcXUK1wEiqlXK9kpSAOlIA2UgjSsOkOM2BHyDDGCQ9emJfwjH7L0BGNLAXed2pafS6o2CAPY8Sa4kiJLoFnqsOI2n9VnnVuzkxgwZ51HYCcxVLiTQnVbXwCyi6G6bfYOnaKQXQw1NKVA7xYiOcVQz/2dFBkDFMlbVy8f69eePiJ0C5E8Ypjk/UmKLOFdkbm2O7pqtlhoRukWIrHFMMu5R4rIwfWQvb5Cd51Wt6SRuoVIPDFM8+2VwvzQdQCiiGF+zLj1l1cUQxeXPJ9IgR6YFtnqfFzP006xwzQigimG+bRR8Hr2QTHmcM1vRIrRsCjGGKO5DY99tFNQDB/chRBZ8+icYvSxLKdVZ4hRjPfM5LP8DDGKoc9SIUTWn3VOMf5nuRAiemuKWTGiyaEtBlQGSO9owoTSiZYYGnWrSqophcbAIooxc3sOJ4SIfqfQEiOaHE/Rqs9kfWMxfWi21WxyaNZktuC1WlNoDjiDHNo1mN4aW25FUAauFUb5HITnEb1YyGxev8f+FIfohhNBjpAyFLxuSS0KQpxWrMbk+gvguZONdsconD8zyjOKXtw7ovf2RtrrjCssJfHsTMumx1V7Xll1jSvQppgelq6XEDZCi3jRrIBYPCM8+4AIAgCYHFZ2ipqduwaMDAUUKQo7yQEnQwFNikJmOWBlKKBKUcgkB7wMBXQpCnWgkQQJI0JNFClq0AUJKUJNRClqEAQJL8GZ6FLU3F2cMO8xoHC8Xn8zO44t6iU3FA9EKikIKfwCH9nVO8Sd1NQAAAAASUVORK5CYII=" ICON_BATTERY = {} ICON_BATTERY[0] = "iVBORw0KGgoAAAANSUhEUgAAAJYAAACWCAYAAAA8AXHiAAAACXBIWXMAAC4jAAAuIwF4pT92AAAGYmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTExLTEzVDEwOjUzOjQ2LTA1OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOC0xMS0yN1QxNTowMTo1OC0wNTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOC0xMS0yN1QxNTowMTo1OC0wNTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2MzM5NTQ4ZC1iN2Y3LTY4NGItYTA3MS1lNzkyODI1NGUzOWEiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpjNTAyODYzYi1iN2M0LWVmNGMtYjJmYy1hYjg4ZTc1Njk4NTIiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxYTJkMDkzOC1mMmM0LTkwNGQtOTIyYi1kMWFkZmMxZTk3NjMiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjFhMmQwOTM4LWYyYzQtOTA0ZC05MjJiLWQxYWRmYzFlOTc2MyIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0xM1QxMDo1Mzo0Ni0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjb252ZXJ0ZWQiIHN0RXZ0OnBhcmFtZXRlcnM9ImZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmciLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjYzMzk1NDhkLWI3ZjctNjg0Yi1hMDcxLWU3OTI4MjU0ZTM5YSIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0yN1QxNTowMTo1OC0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PnDVyKgAAAHjSURBVHic7d07joMwFEDR8Sj73x3r8bQpYilkuOR3TktDcfWQwTJjzvkDR/t99g3wmYRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCQuqwtjjDPvgze1OsN2Gda2bU695dquSeNRSEJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSWH6EfZEvE6ztlc4GJRUJYJIRFQlgkhEXi6FUhn2u1mrz5JsDEIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIuHUZO616z9JJhYJYZEQFglhkRAWiaNXhaf8vZPXZ2KREBYJYZEQFglhkRAWCWGREBYJYZEQFglhkRAWiTHn7e/GY+zaicqXWvazugD/4VFIQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSEReIPmnQWrBQNsAsAAAAASUVORK5CYII=" ICON_BATTERY[1] = "iVBORw0KGgoAAAANSUhEUgAAAJYAAACWCAYAAAA8AXHiAAAACXBIWXMAAC4jAAAuIwF4pT92AAAGYmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTExLTEzVDEwOjUzOjQ2LTA1OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOC0xMS0yN1QxNTowMTozNi0wNTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOC0xMS0yN1QxNTowMTozNi0wNTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowMGFkYTYwNC0wN2FiLWE1NGUtYmQ3My02ODMxYjM4YWQ0ZTYiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDozMjY1YTliYS1iYjNhLWUxNDEtYmMyOC1mYThmYzQ2MTAwNTYiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo0NTlhNzIyZS1lMmU4LWZhNGEtYjg1OS01OTY2NDcyNmRlMDUiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjQ1OWE3MjJlLWUyZTgtZmE0YS1iODU5LTU5NjY0NzI2ZGUwNSIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0xM1QxMDo1Mzo0Ni0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjb252ZXJ0ZWQiIHN0RXZ0OnBhcmFtZXRlcnM9ImZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmciLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjAwYWRhNjA0LTA3YWItYTU0ZS1iZDczLTY4MzFiMzhhZDRlNiIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0yN1QxNTowMTozNi0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PuIvizYAAAHoSURBVHic7d1LCsIwAEBBI97/dj1P3LowiNpXP8xsuynkkRIS0jHnPMHezp9+Af6TsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLxGX1YIxx5Hvwo1Z32C7D2rbNrbfcemqm8SkkISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEstN6Bc5EvH9DjlcYMYiISwSwiIhLBLCIrH3qnBlz5WIlednrMbw7niYsUgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgcdWuym45/31NjaMYiISwSwiIhLBLCIrH3qvCQv3fy/cxYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERWLMeX/feAyniXls2c/qAbzDp5CEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLxBVwKxet+w8WDgAAAABJRU5ErkJggg==" ICON_BATTERY[2] = "iVBORw0KGgoAAAANSUhEUgAAAJYAAACWCAYAAAA8AXHiAAAACXBIWXMAAC4jAAAuIwF4pT92AAAGYmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTExLTEzVDEwOjUzOjQ2LTA1OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOC0xMS0yN1QxNTowMTowMy0wNTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOC0xMS0yN1QxNTowMTowMy0wNTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpmMjRhNTBhMi1lYjQwLTc1NGQtYmE2Yi1jMDY2ZWI3ZTg4NjAiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpiNmJlZDk4YS0wZjk5LTNkNGMtOWE1MS0wZTMwODI3MzYwN2UiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDozYWFhNTNiMS01YmZhLTgzNDItOTE0MS1kOGJkZGE1MTEzOWIiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjNhYWE1M2IxLTViZmEtODM0Mi05MTQxLWQ4YmRkYTUxMTM5YiIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0xM1QxMDo1Mzo0Ni0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjb252ZXJ0ZWQiIHN0RXZ0OnBhcmFtZXRlcnM9ImZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmciLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmYyNGE1MGEyLWViNDAtNzU0ZC1iYTZiLWMwNjZlYjdlODg2MCIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0yN1QxNTowMTowMy0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Psm3TXYAAAHySURBVHic7d1BasMwFEDBqvT+t/N5lG2hFiGJnpuWma03RjxkPjbymHN+wG6fv30D/E/CIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSX6sLY4wr74M/anWG7TKs4zicest3D+00HoUkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFYvkS+kk+iXh/l3xcYMciISwSwiIhLBLCIrF7KlzZOYmcTZ67Jx3T7U+rNT5dKzsWCWGREBYJYZEQFglhkRAWCWGREBYJYZEQFglhkRAWCWGREBYJYZEQFglhkRAWCWGREBYJYZEQFglhkRAWCWGREBYJYZEQFglhkRAWCWGREBYJYZEQFglhkRAWiatOTa5PIXbKce+hNbZjkRAWCWGREBYJYZHYPRVe8vdO3p8di4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEiMOc/fG4/ha1/uW/azugCv8CgkISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCInED5W8YrryDPrYAAAAASUVORK5CYII=" ICON_BATTERY[3] = "iVBORw0KGgoAAAANSUhEUgAAAJYAAACWCAYAAAA8AXHiAAAACXBIWXMAAC4jAAAuIwF4pT92AAAGYmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTExLTEzVDEwOjUzOjQ2LTA1OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOC0xMS0yN1QxNTowMDo0My0wNTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOC0xMS0yN1QxNTowMDo0My0wNTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowZGRhNjdkNS02YzY2LTMwNGUtYTY5NC1kNjVhYzMxMjY0N2YiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpiNTMwYmZmNC0zOTAwLTJkNDUtYTZhNS04ZTRlOWE0MmQ0ZjUiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDphYTFjOTEzZC1hNTI1LTNmNGItYmQ0MS1jMDEzYTgyODI3YzAiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmFhMWM5MTNkLWE1MjUtM2Y0Yi1iZDQxLWMwMTNhODI4MjdjMCIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0xM1QxMDo1Mzo0Ni0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjb252ZXJ0ZWQiIHN0RXZ0OnBhcmFtZXRlcnM9ImZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmciLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjBkZGE2N2Q1LTZjNjYtMzA0ZS1hNjk0LWQ2NWFjMzEyNjQ3ZiIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0yN1QxNTowMDo0My0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PpRR/VMAAAH7SURBVHic7d1BToUwFEBRa9z/7lgPTv+AJn7trXw9Z8qEJjclL5AyzvN8g9Xef/sG+JuERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkPmYXxhg774MXNTvDdhrWcRxOveXRUzuNRyEJYZEQFglhkRAWCWGREBYJYZEQFglhkRAWCWGRmL6E/iafRNzflo8L7FgkhEVCWCSERUJYJFZPhTMrJ5GryXP1pDObbut13Nls7ZfrsGOREBYJYZEQFglhkRAWCWGREBYJYZEQFglhkRAWCWGREBYJYZEQFglhkRAWCWGREBYJYZEQFglhkRAWCWGREBYJYZEQFglhkRAWCWGREBYJYZEQFglhkRAWCWGR2HVqcn1C8K4TiF/tpOOVnlq7HYuEsEgIi4SwSAiLxOqpcMvfO7k/OxYJYZEQFglhkRAWCWGREBYJYZEQFglhkRAWCWGRGOd5/d54jP/8FS5fNe1ndgF+wqOQhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi8Qn8k8Zr04bc6MAAAAASUVORK5CYII=" ICON_BATTERY[4] = "iVBORw0KGgoAAAANSUhEUgAAAJYAAACWCAYAAAA8AXHiAAAACXBIWXMAAC4jAAAuIwF4pT92AAAGYmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTExLTEzVDEwOjUzOjQ2LTA1OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOC0xMS0yN1QxNTowMDoxNy0wNTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOC0xMS0yN1QxNTowMDoxNy0wNTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplNDQ3ODY3NS1iMTI4LTAwNDYtYTNkZi03Y2E1MzdiNjk0NWUiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo0NTVkZTUzNC1kNzJmLWRlNGQtODUxZS01OWIyODEzODRmNDAiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3MTQ2NzgxOC0yNjkwLThhNDAtYjZlOC1jMjQ1MjdkM2Q0ZmQiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjcxNDY3ODE4LTI2OTAtOGE0MC1iNmU4LWMyNDUyN2QzZDRmZCIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0xM1QxMDo1Mzo0Ni0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjb252ZXJ0ZWQiIHN0RXZ0OnBhcmFtZXRlcnM9ImZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmciLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmU0NDc4Njc1LWIxMjgtMDA0Ni1hM2RmLTdjYTUzN2I2OTQ1ZSIgc3RFdnQ6d2hlbj0iMjAxOC0xMS0yN1QxNTowMDoxNy0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pg4HQv0AAAH+SURBVHic7d0xboMwAEDRusr9b8d56NoBD0n5jhu9tzJgpC8jCwuP8zy/4G7f7x4An0lYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEXiMbswxlg5Dv6p2T9sp2Edx+Gvt/z21EzjVUhCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkph+hX2RLxP6WbC4wY5EQFglhkRAWCWGRuHtVOHPnSuRq5Xn3Sme2uq2fY9U9XjEb1+U9zFgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkhEVCWCSERUJYJIRFQlgkVv01uT5jZ9UZPivus+t5RE+Ny4xFQlgkhEVCWCSEReLuVeGS0zvZnxmLhLBICIuEsEgIi4SwSAiLhLBICIuEsEgIi4SwSIzzvP5uPMauO2TZybSf2QX4C69CEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBLCIiEsEsIiISwSwiIhLBI/mssasK5B6ZsAAAAASUVORK5CYII=" SOUND_PROFILE_CHOICES = {[1]="Off", [2]="Female Speech", [3]="Male Speech", [4]="Media", [5]="Custom"} AUDIO_SENS_SERV_TO_CLIENT = {["AUTOMATIC"] = "Automatic", ["LEVEL1_0DB"] = "0 dB", ["LEVEL2_6DB"] = "-6 dB", ["LEVEL3_12DB"] = "-12 dB", ["LEVEL4_18DB"] = "-18 dB", ["LEVEL5_24DB"] = "-24 dB", ["LEVEL6_30DB"] = "-30 dB"} AUDIO_SENS_CLIENT_TO_SERV = {["Automatic"] = "AUTOMATIC", ["0 dB"] = "LEVEL1_0DB", ["-6 dB"] = "LEVEL2_6DB", ["-12 dB"] = "LEVEL3_12DB", ["-18 dB"] = "LEVEL4_18DB", ["-24 dB"] = "LEVEL5_24DB", ["-30 dB"] = "LEVEL6_30DB"} AUDIO_SENS_RACK = {["Automatic"] = 0, ["0 dB"] = 1, ["-6 dB"] = 2, ["-12 dB"] = 3, ["-18 dB"] = 4, ["-24 dB"] = 5, ["-30 dB"] = 6} MUTE_MODE_SERV_TO_CLIENT = {["OFF"] = "Switch inactive", ["ON"] = "Switch active", ["PUSH_TO_TALK"] = "Push to talk", ["PUSH_TO_MUTE"] = "Push to mute"} MUTE_MODE_CLIENT_TO_SERV = {["Switch inactive"] = "OFF", ["Switch active"] = "ON", ["Push to talk"] = "PUSH_TO_TALK", ["Push to mute"] = "PUSH_TO_MUTE"} MUTE_MODE_RACK = {["Switch inactive"] = 0, ["Switch active"] = 1, ["Push to talk"] = 2, ["Push to mute"] = 3} PRESET_SERV_TO_CLIENT = {["OFF"] = "Off", ["FEMALE_SPEECH"] = "Female Speech", ["MALE_SPEECH"] = "Male Speech", ["MEDIA"] = "Media", ["CUSTOM"] = "Custom"} PRESET_CLIENT_TO_SERV = {["Off"] = "OFF", ["Female Speech"] = "FEMALE_SPEECH", ["Male Speech"] = "MALE_SPEECH", ["Media"] = "MEDIA", ["Custom"] = "CUSTOM"} NUMCHANS = 1 if MCR2 then NUMCHANS = 2 elseif MCR4 then NUMCHANS = 4 end TOTAL_SUBSCRIPTIONS = 15 if MCR2 then TOTAL_SUBSCRIPTIONS = 30 elseif MCR4 then TOTAL_SUBSCRIPTIONS = 58 end OFF_GRAY = "#BABABA" CTLS = {} for k,v in pairs(Controls) do if type(v) == "table" then CTLS[k] = v else CTLS[k] = {[1] = v} end end --Assignable_Properties emulation = System.IsEmulating Discovered_Devices_List = {} --holdingVariables activeIP = "" activePort = 0 subscripCount = 0 messageCache = {} lostConn = false connectedToDevice = false ConnectedToRightProd = false linkStatus = {} indetifyStatus = {} pairingStatus = {} muteSwitchStatus = {} muteSwitchActive = {} identifyTimerRunning = {} pairingTimerRunning = {} IdentifyTimer = {} pairingTimer = {} channelPairing = {} channelUpdating = {} channelWalktest = {} for i=1, NUMCHANS, 1 do linkStatus[i] = false indetifyStatus[i] = false pairingStatus[i] = false muteSwitchStatus[i] = false muteSwitchActive[i] = true identifyTimerRunning[i] = false pairingTimerRunning[i] = false IdentifyTimer[i] = Timer.New() pairingTimer[i] = Timer.New() channelPairing[i] = false channelUpdating[i] = false channelWalktest[i] = false IdentifyTimer[i].EventHandler = function() if CTLS["LinkStatus"][i].Color == "#FF54DE1D" then --Green CTLS["LinkStatus"][i].Color = "#555555" elseif CTLS["LinkStatus"][i].Color == "#555555" then CTLS["LinkStatus"][i].Color = "#FF54DE1D" --Green end end pairingTimer[i].EventHandler = function() if CTLS["LinkStatus"][i].Color == "#FF54DE1D" then --Green CTLS["LinkStatus"][i].Color = "#FFEE2211" elseif CTLS["LinkStatus"][i].Color == "#FFEE2211" then CTLS["LinkStatus"][i].Color = "#FF54DE1D" --Green end end end --Tables --Objects json = require("rapidjson") ControlSocket = UdpSocket.New() if not emulation then mDNS = BonjourBrowser.New() end SocketTimer = Timer.New() HeartbeatTimer = Timer.New() HeartbeatMonitorTimer = Timer.New() --Timer to check if channels have been muted from cockpit--------------------------------------------------------- MuteTimer = Timer.New() MuteTimer.EventHandler = function() for i=1, NUMCHANS, 1 do sendMessage("/rx"..i.."/mute_state", nil) end end MuteTimer:Start(2) -----------------------------------------------------------------------------------------Socket Related Functions ControlSocket.Data = function(udp, packet) --if DEBUG then print( packet.Address, packet.Port, packet.Data ) end if packet.Address == activeIP then local osctable = json.decode(packet.Data) local osclist = tableToList("", osctable) for i,v in pairs(osclist) do local funcname = string.gsub(v[1], "/", "_") local chan = string.match(funcname, '[rt]x[1-4]') if chan ~= nil then if string.sub(chan, 1,1) == "r" then funcname = string.gsub(funcname, chan, "rx1") elseif string.sub(chan, 1,1) == "t" then funcname = string.gsub(funcname, chan, "tx1") end end if _G[funcname] then _G[funcname](v[2], chan) elseif string.find(funcname, "#") then else if DEBUG then print("No function found:", funcname, "Value:", v[2]) end end end end end SocketTimer.EventHandler = function() popMessage() end function popMessage() if #messageCache > 0 then ControlSocket:Send(activeIP, activePort, messageCache[1]) table.remove(messageCache, 1) else SocketTimer:Stop() end end function _device_identity_product(value) connectedToDevice = true if RACK then if value == "SLDW" then ConnectedToRightProd = true else ConnectedToRightProd = false end elseif MCR2 then if value == "SLDW2CH" then ConnectedToRightProd = true else ConnectedToRightProd = false end elseif MCR4 then if value == "SLDW4CH" then ConnectedToRightProd = true else ConnectedToRightProd = false end end subscribe() end -----------------------------------------------------------------------------------------Event Handlers function connected() if Controls.Status.String ~= "Initializing - Receiver Update is in Progress" then Controls.Status.Value = 0 Controls.Status.String = "" end updateLinkStatus() HeartbeatTimer:Start(5) if subscripCount < TOTAL_SUBSCRIPTIONS then subscribe() end if lostConn then lostConn = false subscribe() end end function disconnected() Controls.Status.Value = 3 Controls.Status.String = "Missing - No connection" --Controls.LinkStatus.Color = OFF_GRAY --HeartbeatTimer:Stop() HeartbeatMonitorTimer:Stop() --MuteTimer:Stop() for i=1, NUMCHANS, 1 do CTLS["LinkStatus"][i].Color = OFF_GRAY CTLS["BatteryTime"][i].String = "" CTLS["BatteryIcon"][i].Legend = "" CTLS["AudioLevel"][i].Value = -120 CTLS["State"][i].Value = 3 end CTLS["DeviceLocation"][1].String = "" for k,v in pairs(CTLS["RFLevelIcon"]) do v.Boolean = false end for k,v in pairs(CTLS["DeviceName"]) do v.String = "" end end function countSubscriptions() if DEBUG then print("The number of subscriptions is:", subscripCount) end if subscripCount == 0 then disconnected() end if subscripCount < TOTAL_SUBSCRIPTIONS then subscribe() end end function addSubscriptions() subscripCount = subscripCount + 1 if subscripCount == TOTAL_SUBSCRIPTIONS then connected() end end function subscribe() subscripCount = 0 if not connectedToDevice then disconnected() sendMessage("/device/identity/product", nil) elseif connectedToDevice and not ConnectedToRightProd then Controls.Status.Value = 2 Controls.Status.String = "Fault - Wrong Model" for i=1, NUMCHANS, 1 do CTLS["LinkStatus"][i].Color = OFF_GRAY CTLS["BatteryTime"][i].String = "" CTLS["BatteryIcon"][i].Legend = "" CTLS["AudioLevel"][i].Value = -120 CTLS["State"][i].Value = 3 --CTLS["DeviceLocation"][i].String = "" end CTLS["DeviceLocation"][1].String = "" for k,v in pairs(CTLS["RFLevelIcon"]) do v.Boolean = false end for k,v in pairs(CTLS["DeviceName"]) do v.String = "" end --MuteTimer:Stop() for i=1, NUMCHANS, 1 do linkStatus[i] = false end elseif connectedToDevice and ConnectedToRightProd then sendMessage("/osc/state/subscribe/1/device/name", nil, true, 180) sendMessage("/osc/state/subscribe/1/device/state", nil, true, 231) if RACK then sendMessage("/osc/state/subscribe/1/mates/active", nil, true, 183) sendMessage("/osc/state/subscribe/1/rx1/identify", nil, true, 186) sendMessage("/osc/state/subscribe/1/audio/out1/level_db", nil, true, 189) sendMessage("/osc/state/subscribe/1/device/group", nil, true, 192) sendMessage("/osc/state/subscribe/1/audio/low_cut", nil, true, 195) sendMessage("/osc/state/subscribe/1/audio/equalizer/preset", nil, true, 198) sendMessage("/osc/state/subscribe/1/rx1/pair", nil, true, 201) sendMessage("/osc/state/subscribe/1/rx1/rf_quality", nil, true, 204) sendMessage("/osc/state/subscribe/1/mates/tx1/bat_gauge", nil, true, 207) sendMessage("/osc/state/subscribe/1/mates/tx1/bat_lifetime", nil, true, 210) sendMessage("/osc/state/subscribe/1/mates/tx1/agc", nil, true, 213) sendMessage("/osc/state/subscribe/1/rx1/mute_mode", nil, true, 216) sendMessage("/osc/state/subscribe/1/rx1/mute_state", nil, true, 219) sendMessage("/osc/state/subscribe/1/mates/tx1/bat_bars", nil, true, 222) --sendMessage("/osc/state/subscribe/1/device/state", nil, true, 225) sendMessage("/osc/state/subscribe/1/rx1/walktest", nil, true, 228) else sendMessage("/osc/state/subscribe/1/device/location", nil, true, 183) for i=1, NUMCHANS, 1 do sendMessage("/osc/state/subscribe/1/rx"..i.."/rf_quality", nil, true, 186 + ((i-1)*42)) sendMessage("/osc/state/subscribe/1/mates/tx"..i.."/bat_gauge", nil, true, 189 + ((i-1)*42)) sendMessage("/osc/state/subscribe/1/mates/tx"..i.."/bat_lifetime", nil, true, 192 + ((i-1)*42)) sendMessage("/osc/state/subscribe/1/mates/tx"..i.."/agc", nil, true, 195 + ((i-1)*42)) sendMessage("/osc/state/subscribe/1/rx"..i.."/mute_mode", nil, true, 198 + ((i-1)*42)) sendMessage("/osc/state/subscribe/1/rx"..i.."/mute_state", nil, true, 201 + ((i-1)*42)) sendMessage("/osc/state/subscribe/1/mates/tx"..i.."/bat_bars", nil, true, 204 + ((i-1)*42)) sendMessage("/osc/state/subscribe/1/mates/tx"..i.."/active", nil, true, 207 + ((i-1)*42)) sendMessage("/osc/state/subscribe/1/rx"..i.."/name", nil, true, 210 + ((i-1)*42)) sendMessage("/osc/state/subscribe/1/rx"..i.."/identification/visual", nil, true, 213 + ((i-1)*42)) sendMessage("/osc/state/subscribe/1/m/rx"..i.."/level", nil, true, 216 + ((i-1)*42)) sendMessage("/osc/state/subscribe/1/audio/rx"..i.."/low_cut", nil, true, 219 + ((i-1)*42)) sendMessage("/osc/state/subscribe/1/audio/rx"..i.."/equalizer/preset", nil, true, 222 + ((i-1)*42)) sendMessage("/osc/state/subscribe/1/rx"..i.."/pair/enable", nil, true, 225 + ((i-1)*42)) sendMessage("/osc/state/subscribe/1/rx"..i.."/state", nil, true, 228 + ((i-1)*42)) --sendMessage("/osc/state/subscribe/1/rx"..i.."/update/progress", nil, true, 231 + ((i-1)*42)) end end Timer.CallAfter(countSubscriptions, 5) end end Controls.DiscoveryMode.EventHandler = function(ctl) setDeviceControls() if ctl.String == "Discovery" then discoverDevices() elseif ctl.String == "Discovery Not Supported in Emulation" then Controls.DiscoveryMode.String = "Manual" updateIP() else updateIP() end end Controls.Device.EventHandler = function() updateIP() end Controls.IPAddress.EventHandler = function() if emulation then ControlSocket:Close() ControlSocket:Open() end updateIP() end Controls.Port.EventHandler = function() if emulation then ControlSocket:Close() ControlSocket:Open() end updateIP() end -------------------------------------------------------------------------------------------Response Handlers --Heartbeat ------------------------------- function _interface_version(value, chan) HeartbeatMonitorTimer:Stop() HeartbeatMonitorTimer:Start(11) connected() end --Link Status------------------------------- function _mates_active(value, chan) --No device connected linkStatus[1] = false updateLinkStatus(1) --Controls["State"].Value = 3 end function _mates_active_1(value, chan) if value == "tx1" then --Device connected linkStatus[1] = true updateLinkStatus(1) end end function _osc_state_subscribe_1_mates_active(value, chan) --subscription successful addSubscriptions() end function _osc_error_1_mates_active_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_mates_active_1", value) end end --Link Status Multi----------------------- function _mates_tx1_active(value, chan) local ind = tonumber(string.sub(chan, 3,3)) if value then linkStatus[ind] = true updateLinkStatus(ind) else linkStatus[ind] = false updateLinkStatus(ind) local currentIcon = ICON_BATTERY[0] local icon = { DrawChrome = false, -- Draw the button face and edges IconData = currentIcon, } CTLS["BatteryIcon"][ind].Legend = json.encode(icon) CTLS["BatteryTime"][ind].String = "" end end function _mates_tx1_active_1(value, chan) --[[if value == "tx1" then --Device connected linkStatus[1] = true updateLinkStatus(1) elseif value == "tx2" then linkStatus[2] = true updateLinkStatus(2) elseif value == "tx3" then linkStatus[3] = true updateLinkStatus(3) elseif value == "tx4" then linkStatus[4] = true updateLinkStatus(4) end]] --[[if value == "tx1" then --Device connected linkStatus = true updateLinkStatus() end]] end function _osc_state_subscribe_1_mates_tx1_active(value, chan) --subscription successful addSubscriptions() end function _osc_error_1_mates_tx1_active_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_mates_tx1_active_1", value) end end --Device Name ---------------------------- function _device_name(value, chan) if RACK then --Controls.DeviceName.String = value CTLS["DeviceName"][1].String = value end if Controls.DiscoveryMode.String == "Manual" or Controls.DiscoveryMode.String == "Discovery Not Supported in Emulation" then if Controls.Device.String ~= value then Controls.Device.String = string.upper(value) end end end function _osc_state_subscribe_1_device_name(value, chan) --subscription successful addSubscriptions() end function _osc_error_1_device_name_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_device_name_1", value) end end --Device Name Multi (Channel Name)------------------------- function _rx1_name(value, chan) local ind = tonumber(string.sub(chan, 3,3)) --Controls.DeviceName.String = value CTLS["DeviceName"][ind].String = value end function _osc_state_subscribe_1_rx1_name(value, chan) --subscription successful addSubscriptions() end function _osc_error_1_rx1_name_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_rx1_name_1", value) end end --Identify --------------------------------- function _osc_state_subscribe_1_rx1_identify(value, chan) --subscription successful addSubscriptions() end function _rx1_identify(value, chan) indetifyStatus[1] = value updateLinkStatus(1) end function _osc_error_1_rx1_identify_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_rx1_identify_1", value) end end if RACK then CTLS["DeviceID"][1].EventHandler = function() sendMessage("/rx1/identify", true) --Start identification end end --Identify Multi----------------------------------- function _osc_state_subscribe_1_rx1_identification_visual(value, chan) --subscription successful addSubscriptions() end function _rx1_identification_visual(value, chan) local ind = tonumber(string.sub(chan, 3,3)) indetifyStatus[ind] = value updateLinkStatus(ind) end function _osc_error_1_rx1_identification_visual_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_rx1_identification_visual_1", value) end end if MCR2 or MCR4 then for i=1, NUMCHANS, 1 do Controls["DeviceID"][i].EventHandler = function() sendMessage("/rx"..i.."/identification/visual", true) --Start identification end end end --[[Controls.DeviceID.EventHandler = function() sendMessage("/rx1/identify", true) --Start identification end]] --RF Signal ------------------------------ function _osc_state_subscribe_1_rx1_rf_quality(value, chan) --subscription successful addSubscriptions() end function _rx1_rf_quality(value, chan) local ind = tonumber(string.sub(chan, 3,3)) local barcount = 0 if value == 0 then barcount = 0 elseif value < 17 then barcount = 1 elseif value < 34 then barcount = 2 elseif value < 51 then barcount = 3 elseif value < 67 then barcount = 4 elseif value < 84 then barcount = 5 elseif value < 101 then barcount = 6 end if ConnectedToRightProd then for i=1,6 do if i <= barcount then Controls.RFLevelIcon[(6*(ind-1))+i].Boolean = true else Controls.RFLevelIcon[(6*(ind-1))+i].Boolean = false end end --Controls.RFLevel.Value = barcount CTLS["RFLevel"][ind].Value = barcount end end function _osc_error_1_rx1_rf_quality_1(value, chan) --Resubscribe if value == 310 then subscribe() else print("ERROR _osc_error_1_rx1_rf_quality_1", value) end end ---Battery Level --------------------------- function _osc_state_subscribe_1_mates_tx1_bat_gauge(value, chan) --subscription successful addSubscriptions() end function _mates_tx1_bat_gauge(value, chan) local ind = tonumber(string.sub(chan, 3,3)) --Controls.BatteryLevel.Position = value/100 CTLS["BatteryLevel"][ind].Position = value/100 end function _osc_error_1_mates_tx1_bat_gauge_1(value, chan) local ind = tonumber(string.sub(chan, 3,3)) if value == 404 then --Controls.BatteryLevel.Position = 0 CTLS["BatteryLevel"][ind].Position = 0 elseif value == 310 then subscribe() else print("ERROR _osc_error_1_mates_tx1_bat_gauge_1", value) end end --Battery Time ------------------------------ function _osc_state_subscribe_1_mates_tx1_bat_lifetime(value, chan) --subscription successful addSubscriptions() end function _mates_tx1_bat_lifetime(value, chan) local ind = tonumber(string.sub(chan, 3,3)) if value < 3600 then --Controls.BatteryTime.String = "<1h" CTLS["BatteryTime"][ind].String = "<1h" elseif value >= 3600 then --Controls.BatteryTime.String = math.floor(value/3600).."h" CTLS["BatteryTime"][ind].String = math.floor(value/3600).."h" end end function _osc_error_1_mates_tx1_bat_lifetime_1(value, chan) local ind = tonumber(string.sub(chan, 3,3)) if value == 404 then --Controls.BatteryTime.String = "" CTLS["BatteryTime"][ind].String = "" elseif value == 424 then --Controls.BatteryTime.String = "" --Be patient, the transmitter is calculating the battery time CTLS["BatteryTime"][ind].String = "" elseif value == 310 then subscribe() else print("ERROR _osc_state_subscribe_1_mates_tx1_bat_lifetime", value) end end --Audio Level ----------------------------- function _osc_state_subscribe_1_audio_out1_level_db(value, chan) --subscription successful addSubscriptions() end function _audio_out1_level_db(value, chan) --Controls.AudioLevel.Value = value if ConnectedToRightProd then CTLS["AudioLevel"][1].Value = value end end function _osc_error_1_audio_out1_level_db_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_audio_out1_level_db_1", value) end end --Audio Level Multi---------------------------- function _osc_state_subscribe_1_m_rx1_level(value, chan) --subscription successful addSubscriptions() end function _m_rx1_level(value, chan) local ind = tonumber(string.sub(chan, 3,3)) --Controls.AudioLevel.Value = value if ConnectedToRightProd then CTLS["AudioLevel"][ind].Value = value end end function _osc_error_1_m_rx1_level_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_m_rx1_level_1", value) end end --Mute Switch State ------------------------- function _osc_state_subscribe_1_mates_tx1_switch1_state(value, chan) --subscription successful addSubscriptions() end function _mates_tx1_switch1_state(value, chan) local ind = tonumber(string.sub(chan, 3,3)) --muteSwitchStatus = value --updateLinkStatus() --updateMuteIcon() muteSwitchStatus[ind] = value updateLinkStatus(ind) updateMuteIcon(ind) end function _osc_error_1_mates_tx1_switch1_state_1(value, chan) local ind = tonumber(string.sub(chan, 3,3)) if value == 404 then muteSwitchStatus[ind] = false updateLinkStatus(ind) updateMuteIcon(ind) elseif value == 310 then subscribe() else print("ERROR _osc_error_1_mates_tx1_switch1_state_1", value) end end -- Location ---------------------------------- function _osc_state_subscribe_1_device_group(value, chan) --subscription successful addSubscriptions() end function _device_group(value, chan) Controls.DeviceLocation.String = value end function _osc_error_1_device_group_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_state_subscribe_1_device_group", value) end end --Location Multi----------------------------------------- function _osc_state_subscribe_1_device_location(value, chan) --subscription successful addSubscriptions() end function _device_location(value, chan) Controls.DeviceLocation.String = value end function _osc_error_1_device_location_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_state_subscribe_1_device_location", value) end end --Audio Sensitivity------------------------------------- function _osc_state_subscribe_1_mates_tx1_agc(value, chan) --subscription successful addSubscriptions() end function _mates_tx1_agc(value, chan) local ind = tonumber(string.sub(chan, 3,3)) local t = {"Automatic", "0 dB", "-6 dB", "-12 dB", "-18 dB", "-24 dB", "-30 dB"} if RACK then CTLS["AudioSensitivity"][1].String = t[value + 1] else if value == "AUTOMATIC" then --Controls.AudioSensitivity.String = "Automatic" CTLS["AudioSensitivity"][ind].String = "Automatic" else local index = tonumber(string.sub(value,6,6)) --Controls.AudioSensitivity.String = t[index + 1] CTLS["AudioSensitivity"][ind].String = t[index + 1] end end end function _osc_error_1_mates_tx1_agc_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_state_subscribe_1_mates_tx1_agc", value) end end for i=1, NUMCHANS, 1 do if RACK then CTLS["AudioSensitivity"][i].EventHandler = function(ctl) sendMessage("/mates/tx"..i.."/agc", AUDIO_SENS_RACK[ctl.String]) end else CTLS["AudioSensitivity"][i].EventHandler = function(ctl) sendMessage("/mates/tx"..i.."/agc", AUDIO_SENS_CLIENT_TO_SERV[ctl.String]) end end end --Mute Mode------------------------------------- function _osc_state_subscribe_1_rx1_mute_mode(value, chan) --subscription successful addSubscriptions() end function _rx1_mute_mode(value, chan) local ind = tonumber(string.sub(chan, 3,3)) if RACK then local t ={"Switch inactive", "Switch active", "Push to talk", "Push to mute"} CTLS["MuteMode"][1].String = t[value + 1] else local t2 = {["OFF"] = "Switch inactive", ["ON"] = "Switch active", ["PUSH_TO_TALK"] = "Push to talk", ["PUSH_TO_MUTE"] = "Push to mute"} --Controls.MuteMode.String = t2[value] CTLS["MuteMode"][ind].String = t2[value] end end function _osc_error_1_rx1_mute_mode_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_state_subscribe_1_rx1_mute_mode", value) end end for i=1, NUMCHANS, 1 do if RACK then CTLS["MuteMode"][i].EventHandler = function(ctl) sendMessage("/rx"..i.."/mute_mode", MUTE_MODE_RACK[ctl.String]) end else CTLS["MuteMode"][i].EventHandler = function(ctl) sendMessage("/rx"..i.."/mute_mode", MUTE_MODE_CLIENT_TO_SERV[ctl.String]) end end end --Mute----------------------------------------------------- function _osc_state_subscribe_1_rx1_mute_state(value, chan) --subscription successful addSubscriptions() end function _rx1_mute_state(value, chan) local ind = tonumber(string.sub(chan, 3,3)) CTLS["MuteSwitchActive"][ind].Value = value muteSwitchStatus[ind] = value muteSwitchActive[ind] = value updateLinkStatus(ind) if value then CTLS["MuteSwitchActive"][ind].Legend = "Activated" local currentIcon = ICON_MUTE --CTLS["MuteState"][ind].Value = 1 local icon = { DrawChrome = false, -- Draw the button face and edges IconData = currentIcon, } CTLS["MuteIcon"][ind].Legend = json.encode(icon) else CTLS["MuteSwitchActive"][ind].Legend = "Deactivated" local currentIcon = ICON_NOMUTE --CTLS["MuteState"][ind].Value = 0 local icon = { DrawChrome = false, -- Draw the button face and edges IconData = currentIcon, } CTLS["MuteIcon"][ind].Legend = json.encode(icon) end end function _osc_error_1_rx1_mute_state_1(value, chan) if value == 310 then subscribe() else --print("ERROR _osc_state_subscribe_1_rx1_mute_state", value) end end for i=1, NUMCHANS, 1 do CTLS["MuteSwitchActive"][i].EventHandler = function(ctl) sendMessage("/rx"..i.."/mute_state", ctl.Boolean) end end --Mute Switch Active ---------------------------Skipped for MCR update function _osc_state_subscribe_1_rx1_mute_switch_active(value) --subscription successful addSubscriptions() end function _rx1_mute_switch_active(value) muteSwitchActive = value updateLinkStatus() updateMuteIcon() if value then Controls.MuteSwitchActive.Value = 1 Controls.MuteSwitchActive.Legend = "Activated" else Controls.MuteSwitchActive.Value = 0 Controls.MuteSwitchActive.Legend = "Deactivated" end end function _osc_error_1_rx1_mute_switch_active_1(value) if value == 310 then subscribe() else print("ERROR _osc_error_1_rx1_mute_switch_active_1", value) end end --[[Controls.MuteSwitchActive.EventHandler = function(ctl) sendMessage("/rx1/mute_switch_active", ctl.Boolean) end]] --Low Cut --------------------------------------- function _osc_state_subscribe_1_audio_low_cut(value) --subscription successful addSubscriptions() end function _audio_low_cut(value) if value then --Controls.LowCut.Value = 1 --Controls.LowCut.Legend = "On" CTLS["LowCut"][1].Value = 1 CTLS["LowCut"][1].Legend = "On" else --Controls.LowCut.Value = 0 --Controls.LowCut.Legend = "Off" CTLS["LowCut"][1].Value = 0 CTLS["LowCut"][1].Legend = "Off" end end function _osc_error_1_audio_low_cut_1(value) if value == 310 then subscribe() else print("ERROR _osc_error_1_audio_low_cut_1", value) end end if RACK then CTLS["LowCut"][1].EventHandler = function(ctl) sendMessage("/audio/low_cut", ctl.Boolean) end end --Low Cut Multi------------------------------------------------ function _osc_state_subscribe_1_audio_rx1_low_cut(value, chan) --subscription successful addSubscriptions() end function _audio_rx1_low_cut(value, chan) local ind = tonumber(string.sub(chan, 3,3)) if value then --Controls.LowCut.Value = 1 --Controls.LowCut.Legend = "On" CTLS["LowCut"][ind].Value = 1 CTLS["LowCut"][ind].Legend = "On" else --Controls.LowCut.Value = 0 --Controls.LowCut.Legend = "Off" CTLS["LowCut"][ind].Value = 0 CTLS["LowCut"][ind].Legend = "Off" end end function _osc_error_1_audio_rx1_low_cut_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_audio_rx1_low_cut_1", value) end end if MCR2 or MCR4 then for i=1, NUMCHANS, 1 do Controls["LowCut"][i].EventHandler = function(ctl) sendMessage("/audio/rx"..i.."/low_cut", ctl.Boolean) end end end --Sound Profile ----------------------------------- function _osc_state_subscribe_1_audio_equalizer_preset(value, chan) --subscription successful addSubscriptions() end function _audio_equalizer_preset(value, chan) CTLS["SoundProfile"][1].String = SOUND_PROFILE_CHOICES[value+1] end function _osc_error_1_audio_equalizer_preset_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_audio_equalizer_preset_1", value) end end if RACK then CTLS["SoundProfile"][1].EventHandler = function(ctl) for i=1,#SOUND_PROFILE_CHOICES do if ctl.String == SOUND_PROFILE_CHOICES[i] then sendMessage("/audio/equalizer/preset", i-1) break end end end end --Sound Profile Multi----------------------------------------------------- function _osc_state_subscribe_1_audio_rx1_equalizer_preset(value, chan) --subscription successful addSubscriptions() end function _audio_rx1_equalizer_preset(value, chan) if RACK then Controls.SoundProfile.String = SOUND_PROFILE_CHOICES[value+1] else local ind = tonumber(string.sub(chan, 3,3)) local t = {["OFF"] = "Off", ["FEMALE_SPEECH"] = "Female Speech", ["MALE_SPEECH"] = "Male Speech", ["MEDIA"] = "Media", ["CUSTOM"] = "Custom"} --Controls.SoundProfile.String = t[value] CTLS["SoundProfile"][ind].String = t[value] end end function _osc_error_1_audio_rx1_equalizer_preset_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_audio_rx1_equalizer_preset_1", value) end end if MCR2 or MCR4 then for i=1, NUMCHANS, 1 do CTLS["SoundProfile"][i].EventHandler = function(ctl) sendMessage("/audio/rx"..i.."/equalizer/preset", PRESET_CLIENT_TO_SERV[ctl.String]) end end end --[[Controls.SoundProfile.EventHandler = function(ctl) for i=1,#SOUND_PROFILE_CHOICES do if ctl.String == SOUND_PROFILE_CHOICES[i] then sendMessage("/audio/equalizer/preset", i-1) break end end end]] --Pairing -------------------------------------- function _osc_state_subscribe_1_rx1_pair(value, chan) --subscription successful addSubscriptions() end function _rx1_pair(value, chan) pairingStatus[1] = value updateLinkStatus(1) if value then CTLS["Pairing"][1].Value = 1 CTLS["Pairing"][1].Legend = "Pairing" else CTLS["Pairing"][1].Value = 0 CTLS["Pairing"][1].Legend = "Start" end end function _osc_error_1_rx1_pair_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_rx1_pair_1", value) end end if RACK then CTLS["Pairing"][1].EventHandler = function(ctl) if Controls["State"].String == "Fault - Pending Update - Press Pairing to Start" then sendMessage("/device/update/confirmation", true) CTLS["Pairing"][1].Boolean = false else sendMessage("/rx1/pair", ctl.Boolean) end end end --Pairing Multi--------------------------------- function _osc_state_subscribe_1_rx1_pair_enable(value, chan) --subscription successful addSubscriptions() end function _rx1_pair_enable(value, chan) local ind = tonumber(string.sub(chan, 3,3)) pairingStatus[ind] = value updateLinkStatus(ind) if value then --Controls.Pairing.Value = 1 --Controls.Pairing.Legend = "Pairing" CTLS["Pairing"][ind].Value = 1 CTLS["Pairing"][ind].Legend = "Pairing" else --Controls.Pairing.Value = 0 --Controls.Pairing.Legend = "Start" CTLS["Pairing"][ind].Value = 0 CTLS["Pairing"][ind].Legend = "Start" end end function _osc_error_1_rx1_pair_enable_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_rx1_pair_enable_1", value) end end if MCR2 or MCR4 then for i=1, NUMCHANS, 1 do Controls["Pairing"][i].EventHandler = function(ctl) if CTLS["State"][i].String == "Fault - Pending Update - Press Pairing to Start" then sendMessage("/rx"..i.."/update/confirmation", true) CTLS["Pairing"][i].Boolean = false else sendMessage("/rx"..i.."/pair/enable", ctl.Boolean) end end end end --[[Controls.Pairing.EventHandler = function(ctl) sendMessage("/rx1/pair", ctl.Boolean) end]] --State--------------------------------------- function _osc_state_subscribe_1_device_state(value, chan) --subscription successful addSubscriptions() end function _device_state(value, chan) if RACK then local t = {[0] = {0, ""}, [1] = {5, "Pairing"}, [2] = {5, "Receiver Update is in Progress"}, [3] = {5, "Transmitter Update is in Progress"}, [4] = {2, "Pending Update - Press Pairing to Start"}, [5] = {2, "Transmitter Update Failed"}} --print(t[value][2]) if value == 0 or value == 2 then Controls["Status"].Value = t[value][1] Controls["Status"].String = t[value][2] channelPairing[1] = false channelUpdating[1] = false else if value == 1 then channelPairing[1] = true else channelUpdating[1] = true end Controls["State"].Value = t[value][1] Controls["State"].String = t[value][2] end else local t = {["NORMAL"] = {0, ""}, ["UPDATE"] = {5, "Receiver Update is in Progress"}, ["UPDATE_FAILED"] = "Receiver Firmware Update Failed", ["ERROR"] = "Error"} Controls["Status"].Value = t[value][1] Controls["Status"].String = t[value][2] end end function _osc_error_1_device_state_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_device_state_1", value) end end --State Multi--------------------------------- function _osc_state_subscribe_1_rx1_state(value, chan) --subscription successful addSubscriptions() end function _rx1_state(value, chan) local t = {["NOT_CONNECTED"] = {3, ""}, ["CONNECTED"] = {0, ""}, ["PAIRING"] = {5, "Pairing"}, ["FWU_OTA"] = {5, "Transmitter Update is in Progress"}, ["FWU_OTA_CONFIRMATION"] = {2, "Pending Update - Press Pairing to Start"}, ["TRANSMITTER_UPDATE_FAILED"] = {2, "Transmitter Update Failed"}, ["WALKTEST"] = {0, "Walktest"}} --Set the status for the state control local ind = tonumber(string.sub(chan, 3,3)) CTLS["State"][ind].Value = t[value][1] CTLS["State"][ind].String = t[value][2] if value ~= "NOT_CONNECTED" and value ~= "CONNECTED" then if value == "PAIRING" then channelPairing[ind] = true elseif value == "WALKTEST" then channelWalktest[ind] = true else channelUpdating[ind] = true end else channelPairing[ind] = false channelUpdating[ind] = false channelWalktest[ind] = false end end function _osc_error_1_rx1_state_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_rx"..chan.."_state_1", value) end end --Walktest for Rack state--------------------- function _osc_state_subscribe_1_rx1_walktest(value, chan) --subscription successful addSubscriptions() end function _rx1_walktest(value, chan) if value then Controls["State"].Value = 0 Controls["State"].String = "Walktest" channelWalktest[1] = true else channelWalktest[1] = false if Controls["State"].String == "OK - Walktest" then Controls["State"].Value = 0 Controls["State"].String = "" end end end function _osc_error_1_rx1_walktest_1(value, chan) if value == 310 then subscribe() else print("ERROR _osc_error_1_rx1_walktest_1", value) end end --Battery Bars ------------------------------- function _mates_tx1_bat_bars(value, chan) local ind = tonumber(string.sub(chan, 3,3)) local currentIcon = ICON_BATTERY[0] if value == 0 then currentIcon = ICON_BATTERY[0] elseif value == 1 then currentIcon = ICON_BATTERY[1] elseif value == 2 then currentIcon = ICON_BATTERY[2] elseif value == 3 then currentIcon = ICON_BATTERY[3] elseif value == 4 then currentIcon = ICON_BATTERY[3] elseif value > 4 then currentIcon = ICON_BATTERY[4] end local icon = { DrawChrome = false, -- Draw the button face and edges IconData = currentIcon, } --Controls.BatteryIcon.Legend = json.encode(icon) CTLS["BatteryIcon"][ind].Legend = json.encode(icon) end function _osc_state_subscribe_1_mates_tx1_bat_bars(value, chan) --subscription successful addSubscriptions() end function _osc_error_1_mates_tx1_bat_bars_1(value, chan) local ind = tonumber(string.sub(chan, 3,3)) if value == 404 then local icon = { DrawChrome = false, -- Draw the button face and edges IconData = ICON_BATTERY[0], } --Controls.BatteryIcon.Legend = json.encode(icon) CTLS["BatteryIcon"][ind].Legend = json.encode(icon) elseif value == 310 then subscribe() else print("ERROR _osc_error_1_mates_tx1_bat_bars_1", value) end end function _osc_error_1_mates_tx1_bat_gauge_2_reason(value, chan) local ind = tonumber(string.sub(chan, 3,3)) if value == "data_channel_NOT_ready" then local currentIcon = ICON_BATTERY[0] local icon = { DrawChrome = false, -- Draw the button face and edges IconData = currentIcon, } CTLS["BatteryIcon"][ind].Legend = json.encode(icon) end end -------------------------------------------------------------------------------------------Internal Functions function sendMessage(address, value, subscribe, sublength) local message = addressToTable(address, value) if message then message = json.encode(message) if subscribe == true then sublength = sublength or 180 message = string.gsub(message, "%[{", "[{\"#\":{\"lifetime\":"..sublength.."},") elseif subscribe == false then message = string.gsub(message, "%[{", "[{\"#\":{\"cancel\":true},") end --if DEBUG then print("sending:", message) end if activeIP and activeIP ~= "" then table.insert(messageCache, message) SocketTimer:Start(0.01) end end end function setDeviceControls() if Controls.DiscoveryMode.String == "Manual" or Controls.DiscoveryMode.String == "Discovery Not Supported in Emulation" or emulation then Controls.Device.IsDisabled = true Controls.IPAddress.IsDisabled = false Controls.Port.IsDisabled = false elseif Controls.DiscoveryMode.String == "Discovery" then Controls.Device.IsDisabled = false Controls.IPAddress.IsDisabled = true Controls.Port.IsDisabled = true end end function updateIP() if Controls.DiscoveryMode.String == "Manual" or Controls.DiscoveryMode.String == "Discovery Not Supported in Emulation" or emulation then if Controls.IPAddress.String ~= activeIP or Controls.Port.Value ~= activePort then --prevents a resubsription when switching between manual and discovery if checkIP(Controls.IPAddress.String) then activeIP = Controls.IPAddress.String activePort = Controls.Port.Value disconnected() connectedToDevice = false ConnectedToRightProd = false subscribe() else activeIP = "" if RACK then Controls.LinkStatus.Color = OFF_GRAY elseif MCR2 then CTLS["LinkStatus"][1].Color = OFF_GRAY CTLS["LinkStatus"][2].Color = OFF_GRAY elseif MCR4 then CTLS["LinkStatus"][1].Color = OFF_GRAY CTLS["LinkStatus"][2].Color = OFF_GRAY CTLS["LinkStatus"][3].Color = OFF_GRAY CTLS["LinkStatus"][4].Color = OFF_GRAY end end end elseif Controls.DiscoveryMode.String == "Discovery" then for i,v in ipairs(Discovered_Devices_List) do if v.name == Controls.Device.String then if v.address ~= activeIP or v.port ~= activePort then --prevents a resubsription when switching between manual and discovery activeIP = v.address activePort = v.port Controls.IPAddress.String = v.address Controls.Port.Value = v.port disconnected() connectedToDevice = false ConnectedToRightProd = false subscribe() end return end end end end function updateLinkStatus(chanNum) if ConnectedToRightProd then if chanNum == nil then for i=1, NUMCHANS, 1 do CTLS["LinkStatus"][i].Boolean = linkStatus[i] if pairingStatus[i] == true then CTLS["LinkStatus"][i].Color = "#FF54DE1D" --Green if pairingTimerRunning[i] == false then pairingTimerRunning[i] = true pairingTimer[i]:Start(0.25) end elseif indetifyStatus[i] == true then pairingTimer[i]:Stop() pairingTimerRunning[i] = false CTLS["LinkStatus"][i].Color = "#FF54DE1D" --Green if identifyTimerRunning[i] == false then identifyTimerRunning[i] = true IdentifyTimer[i]:Start(0.25) end elseif linkStatus[i] == false and indetifyStatus[i] == false then pairingTimer[i]:Stop() pairingTimerRunning[i] = false IdentifyTimer[i]:Stop() identifyTimerRunning[i] = false CTLS["LinkStatus"][i].Color = "#FFEE2211" --Red if not channelPairing[i] and not channelUpdating[i] and not channelWalktest[i] then CTLS["State"][i].Value = 3 end elseif linkStatus[i] == true and indetifyStatus[i] == false and muteSwitchStatus[i] == true and muteSwitchActive[i] == true then pairingTimer[i]:Stop() pairingTimerRunning[i] = false IdentifyTimer[i]:Stop() identifyTimerRunning[i] = false CTLS["LinkStatus"][i].Color = "#FFE64A" --Yellow elseif linkStatus[i] == true and indetifyStatus[i] == false then pairingTimer[i]:Stop() pairingTimerRunning[i] = false IdentifyTimer[i]:Stop() identifyTimerRunning[i] = false CTLS["LinkStatus"][i].Color = "#FF54DE1D" --Green if not channelPairing[i] and not channelUpdating[i] and not channelWalktest[i] then CTLS["State"][i].Value = 0 end end end else CTLS["LinkStatus"][chanNum].Boolean = linkStatus[chanNum] if pairingStatus[chanNum] == true then CTLS["LinkStatus"][chanNum].Color = "#FF54DE1D" --Green if pairingTimerRunning[chanNum] == false then pairingTimerRunning[chanNum] = true pairingTimer[chanNum]:Start(0.25) end elseif indetifyStatus[chanNum] == true then pairingTimer[chanNum]:Stop() pairingTimerRunning[chanNum] = false CTLS["LinkStatus"][chanNum].Color = "#FF54DE1D" --Green if identifyTimerRunning[chanNum] == false then identifyTimerRunning[chanNum] = true IdentifyTimer[chanNum]:Start(0.25) end elseif linkStatus[chanNum] == false and indetifyStatus[chanNum] == false then pairingTimer[chanNum]:Stop() pairingTimerRunning[chanNum] = false IdentifyTimer[chanNum]:Stop() identifyTimerRunning[chanNum] = false CTLS["LinkStatus"][chanNum].Color = "#FFEE2211" --Red if not channelPairing[chanNum] and not channelUpdating[chanNum] and not channelWalktest[chanNum] then CTLS["State"][chanNum].Value = 3 end elseif linkStatus[chanNum] == true and indetifyStatus[chanNum] == false and muteSwitchStatus[chanNum] == true and muteSwitchActive[chanNum] == true then pairingTimer[chanNum]:Stop() pairingTimerRunning[chanNum] = false IdentifyTimer[chanNum]:Stop() identifyTimerRunning[chanNum] = false CTLS["LinkStatus"][chanNum].Color = "#FFE64A" --Yellow elseif linkStatus[chanNum] == true and indetifyStatus[chanNum] == false then pairingTimer[chanNum]:Stop() pairingTimerRunning[chanNum] = false IdentifyTimer[chanNum]:Stop() identifyTimerRunning[chanNum] = false CTLS["LinkStatus"][chanNum].Color = "#FF54DE1D" --Green if not channelPairing[chanNum] and not channelUpdating[chanNum] and not channelWalktest[chanNum] then CTLS["State"][chanNum].Value = 0 end end end end end function updateMuteIcon(chanNum) if chanNum == 1 then local currentIcon = ICON_NOMUTE if muteSwitchActive[1] == true and muteSwitchStatus[1] == false then currentIcon = ICON_NOMUTE ----Controls.MuteState.Value = 0 --CTLS["MuteState"][1].Value = 0 elseif muteSwitchActive[1] == true and muteSwitchStatus[1] == true then currentIcon = ICON_MUTE ----Controls.MuteState.Value = 1 --CTLS["MuteState"][1].Value = 1 elseif muteSwitchActive[1] == false then currentIcon = ICON_CIRCLEMUTE ----Controls.MuteState.Value = 0 --CTLS["MuteState"][1].Value = 0 end local icon = { DrawChrome = false, -- Draw the button face and edges IconData = currentIcon, } --Controls.MuteIcon.Legend = json.encode(icon) CTLS["MuteIcon"][1].Legend = json.encode(icon) elseif chanNum == 2 then local currentIcon = ICON_NOMUTE if muteSwitchActive[2] == true and muteSwitchStatus[2] == false then currentIcon = ICON_NOMUTE ----Controls.MuteState.Value = 0 --CTLS["MuteState"][2].Value = 0 elseif muteSwitchActive[2] == true and muteSwitchStatus[2] == true then currentIcon = ICON_MUTE ----Controls.MuteState.Value = 1 --CTLS["MuteState"][2].Value = 1 elseif muteSwitchActive[2] == false then currentIcon = ICON_CIRCLEMUTE ----Controls.MuteState.Value = 0 --CTLS["MuteState"][2].Value = 0 end local icon = { DrawChrome = false, -- Draw the button face and edges IconData = currentIcon, } --Controls.MuteIcon.Legend = json.encode(icon) CTLS["MuteIcon"][2].Legend = json.encode(icon) elseif chanNum == 3 then local currentIcon = ICON_NOMUTE if muteSwitchActive[3] == true and muteSwitchStatus[3] == false then currentIcon = ICON_NOMUTE ----Controls.MuteState.Value = 0 --CTLS["MuteState"][3].Value = 0 elseif muteSwitchActive[3] == true and muteSwitchStatus[3] == true then currentIcon = ICON_MUTE ----Controls.MuteState.Value = 1 --CTLS["MuteState"][3].Value = 1 elseif muteSwitchActive[3] == false then currentIcon = ICON_CIRCLEMUTE ----Controls.MuteState.Value = 0 --CTLS["MuteState"][3].Value = 0 end local icon = { DrawChrome = false, -- Draw the button face and edges IconData = currentIcon, } --Controls.MuteIcon.Legend = json.encode(icon) CTLS["MuteIcon"][3].Legend = json.encode(icon) elseif chanNum == 4 then local currentIcon = ICON_NOMUTE if muteSwitchActive[4] == true and muteSwitchStatus[4] == false then currentIcon = ICON_NOMUTE ----Controls.MuteState.Value = 0 --CTLS["MuteState"][4].Value = 0 elseif muteSwitchActive[4] == true and muteSwitchStatus[4] == true then currentIcon = ICON_MUTE ----Controls.MuteState.Value = 1 --CTLS["MuteState"][4].Value = 1 elseif muteSwitchActive[4] == false then currentIcon = ICON_CIRCLEMUTE ----Controls.MuteState.Value = 0 --CTLS["MuteState"][4].Value = 0 end local icon = { DrawChrome = false, -- Draw the button face and edges IconData = currentIcon, } --Controls.MuteIcon.Legend = json.encode(icon) CTLS["MuteIcon"][4].Legend = json.encode(icon) end end HeartbeatTimer.EventHandler = function() sendMessage("/interface/version", nil) end HeartbeatMonitorTimer.EventHandler = function() if DEBUG then print("Two heartbeats missed") end disconnected() lostConn = true end -------------------------------------------------------------------------------------------Discovery Functions function updateDeviceList() --[[table.sort(Discovered_Devices_List, function(a, b) return a.name < b.name end) local prettydevicelist = {} for i,v in ipairs(Discovered_Devices_List) do table.insert(prettydevicelist, v.name) end]] table.sort(Discovered_Devices_List, function(a, b) return a.name < b.name end) local hash = {} local prettydevicelist = {} for k,v in ipairs(Discovered_Devices_List) do if not hash[v.name] then prettydevicelist[#prettydevicelist+1] = v.name hash[v.name] = true end end Controls.Device.Choices = prettydevicelist end function discoverDevices() Discovered_Devices_List = {} if mDNS then mDNS.EventHandler = function(record) local resolver = BonjourResolver.New() resolver.EventHandler = function( client ) local host = Qlib.gethostbyname(client.Target) result = { ["name"] = record.Name, ["added"] = record.Added, ["clent name"] = client.Name, ["port"] = client.Port, ["target"] = client.Target, ["address"] = host.addresses[1] } if result.added then table.insert(Discovered_Devices_List, result) updateDeviceList() if Controls.DiscoveryMode.String == "Discovery" and result.name == Controls.Device.String then --If the currently discovered device matches the device we are looking for, update the IP. updateIP() --This allows the device to be selected before it is discovered. end end end --function r resolver:Resolve( record ) end --mDNS EventHandler mDNS:Start( "_ssc._udp", 0, "local" ) end --if mDNS end -------------------------------------------------------------------------------------------Helper Functions function addressToTable(address, value) if string.sub(address, 1,1) == "/" then --check for OSC address format i.e. /rx1/mute_switch_active local root = {} --table that contains the entire address local pointer = root --moving pointer for each loop local index = 1 while index <= string.len(address) do if string.find(address, "/", index+1) then --A lower branch is found and therefore this branch is a table, not a value local location = string.find(address, "/", index+1) local branch = string.sub(address, index+1, location-1) if tonumber(branch) then branch = tonumber(branch) end pointer[branch] = {} pointer = pointer[branch] index = location else --No lower branch was found, set this branch equal to the value local branch = string.sub(address, index+1, string.len(address)) if value ~= nil then pointer[branch] = value else pointer[branch] = json.null() end break end end --end while return root else if DEBUG then print("OSC address format not found in: ", address, value) end return nil end end function tableToList(key, inTable) local returntable = {} if inTable ~= nil then for i,j in pairs(inTable) do --i,j local count = 0 if type(j) == "table" then for c,d in pairs(j) do count = count + 1 end end if type(j) == "table" and count > 0 then local lowertable = tableToList(key.."/"..i, j) for c,d in pairs(lowertable) do table.insert(returntable, d) end else --i,j table.insert(returntable, {key.."/"..i, j}) end end --i,j end return returntable end function checkIP(ipAddress) if string.find(ipAddress, "%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?") then local oct = {} oct[1],oct[2],oct[3],oct[4] = string.match(ipAddress, "(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)") if tonumber(oct[1]) < 256 and tonumber(oct[2]) < 256 and tonumber(oct[3]) < 256 and tonumber(oct[4]) < 256 then return true else Controls.Status.Value = 2 Controls.Status.String = "Fault - Incorrect IP Address Format" HeartbeatMonitorTimer:Stop() for i=1, NUMCHANS, 1 do CTLS["LinkStatus"][i].Color = OFF_GRAY CTLS["BatteryTime"][i].String = "" CTLS["BatteryIcon"][i].Legend = "" CTLS["AudioLevel"][i].Value = -120 CTLS["State"][i].Value = 3 --CTLS["DeviceLocation"][i].String = "" end CTLS["DeviceLocation"][1].String = "" for k,v in pairs(CTLS["RFLevelIcon"]) do v.Boolean = false end for k,v in pairs(CTLS["DeviceName"]) do v.String = "" end --MuteTimer:Stop() return false end else Controls.Status.Value = 2 Controls.Status.String = "Fault - Incorrect IP Address Format" HeartbeatMonitorTimer:Stop() for i=1, NUMCHANS, 1 do CTLS["LinkStatus"][i].Color = OFF_GRAY CTLS["BatteryTime"][i].String = "" CTLS["BatteryIcon"][i].Legend = "" CTLS["AudioLevel"][i].Value = -120 CTLS["State"][i].Value = 3 --CTLS["DeviceLocation"][i].String = "" end CTLS["DeviceLocation"][1].String = "" for k,v in pairs(CTLS["RFLevelIcon"]) do v.Boolean = false end for k,v in pairs(CTLS["DeviceName"]) do v.String = "" end --MuteTimer:Stop() return false end end function debugFormat(string) -- Format strings containing non-printable characters to hexidecimal display local visual = "" if type(string) == "string" then for i=1,#string do local byte = string:sub(i,i) if string.byte(byte) >= 32 and string.byte(byte) <= 126 then visual = visual..byte --elseif string.byte(byte) == 9 then --visual = visual.."~" else visual = visual..string.format("\\x%02x",string.byte(byte)) end end else visual = "Type is: "..type(string) end return visual end -------------------------------------------------------------------------------------------------------------Setup function Initialize() for i=1, NUMCHANS, 1 do CTLS["State"][i].Value = 3 CTLS["RFLevel"][i].IsInvisible = true CTLS["BatteryLevel"][i].IsInvisible = true if CTLS["MuteIcon"][i].Legend == "" then CTLS["MuteIcon"][i].Legend = json.encode({DrawChrome = false, IconData = ICON_NOMUTE,}) end if CTLS["BatteryIcon"][i].Legend == "" then CTLS["BatteryIcon"][i].Legend = json.encode({DrawChrome = false, IconData = ICON_BATTERY[0],}) end CTLS["AudioSensitivity"][i].Choices = {"Automatic", "0 dB", "-6 dB", "-12 dB", "-18 dB", "-24 dB", "-30 dB"} CTLS["MuteMode"][i].Choices = {"Switch inactive", "Switch active", "Push to talk", "Push to mute"} CTLS["SoundProfile"][i].Choices = {"Off", "Female Speech", "Male Speech", "Media", "Custom"} CTLS["LinkStatus"][i].Color = OFF_GRAY end if emulation then Controls["DiscoveryMode"].Choices = {"Discovery Not Supported in Emulation", "Manual"} if Controls["DiscoveryMode"].String == "" then Controls["DiscoveryMode"].String = "Manual" end else Controls["DiscoveryMode"].Choices = {"Discovery", "Manual"} if Controls["DiscoveryMode"].String == "" then Controls["DiscoveryMode"].String = "Discovery" end if Controls["DiscoveryMode"].String == "Discovery Not Supported in Emulation" then Controls["DiscoveryMode"].String = "Discovery" end end --end if emulation if Controls["IPAddress"].String == "" then Controls["Port"].Value = 45 end Controls["Status"].Value = 5 Controls["Status"].String = "" ControlSocket:Open() setDeviceControls() if Controls["DiscoveryMode"].String == "Discovery" then discoverDevices() end updateIP() end Initialize() end