using Crestron.SimplSharp; using Crestron.SimplSharp.Reflection; using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.EthernetCommunication; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Core.Logging; using PepperDash.Essentials.AppServer; using PepperDash.Essentials.AppServer.Messengers; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.DeviceTypeInterfaces; using PepperDash.Essentials.Devices.Common.Cameras; using PepperDash.Essentials.Devices.Common.Codec; using PepperDash.Essentials.Room.Config; using System; using System.Collections.Generic; namespace PepperDash.Essentials.Room.MobileControl { // ReSharper disable once InconsistentNaming /// /// Represents a MobileControlSIMPLRoomBridge /// public class MobileControlSIMPLRoomBridge : MobileControlBridgeBase, IDelayedConfiguration { private const int SupportedDisplayCount = 10; /// /// Fires when config is ready to go /// public event EventHandler ConfigurationIsReady; /// /// Gets or sets the Eisc /// public ThreeSeriesTcpIpEthernetIntersystemCommunications Eisc { get; private set; } /// /// Gets or sets the JoinMap /// public MobileControlSIMPLRoomJoinMap JoinMap { get; private set; } public Dictionary DeviceMessengers { get; private set; } /// /// Gets or sets the ConfigIsLoaded /// public bool ConfigIsLoaded { get; private set; } /// public override string RoomName { get { var name = Eisc.StringOutput[JoinMap.ConfigRoomName.JoinNumber].StringValue; return string.IsNullOrEmpty(name) ? "Not Loaded" : name; } } /// public override string RoomKey { get { return "room1"; } } private readonly MobileControlSimplDeviceBridge _sourceBridge; private SIMPLAtcMessenger _atcMessenger; private SIMPLVtcMessenger _vtcMessenger; private SimplDirectRouteMessenger _directRouteMessenger; private const string _syntheticDeviceKey = "syntheticDevice"; /// /// /// /// /// /// public MobileControlSIMPLRoomBridge(string key, string name, uint ipId) : base(key, "") { Eisc = new ThreeSeriesTcpIpEthernetIntersystemCommunications(ipId, "127.0.0.2", Global.ControlSystem); var reg = Eisc.Register(); if (reg != eDeviceRegistrationUnRegistrationResponse.Success) Debug.Console(0, this, "Cannot connect EISC at IPID {0}: \r{1}", ipId, reg); JoinMap = new MobileControlSIMPLRoomJoinMap(1); _sourceBridge = new MobileControlSimplDeviceBridge(key + "-sourceBridge", "SIMPL source bridge", Eisc); DeviceManager.AddDevice(_sourceBridge); CrestronConsole.AddNewConsoleCommand((s) => JoinMap.PrintJoinMapInfo(), "printmobilejoinmap", "Prints the MobileControlSIMPLRoomBridge JoinMap", ConsoleAccessLevelEnum.AccessOperator); AddPostActivationAction(() => { // Inform the SIMPL program that config can be sent Eisc.BooleanInput[JoinMap.ReadyForConfig.JoinNumber].BoolValue = true; Eisc.SigChange += EISC_SigChange; Eisc.OnlineStatusChange += (o, a) => { if (!a.DeviceOnLine) { return; } Debug.Console(1, this, "SIMPL EISC online={0}. Config is ready={1}. Use Essentials Config={2}", a.DeviceOnLine, Eisc.BooleanOutput[JoinMap.ConfigIsReady.JoinNumber].BoolValue, Eisc.BooleanOutput[JoinMap.ConfigIsLocal.JoinNumber].BoolValue); if (Eisc.BooleanOutput[JoinMap.ConfigIsReady.JoinNumber].BoolValue) LoadConfigValues(); if (Eisc.BooleanOutput[JoinMap.ConfigIsLocal.JoinNumber].BoolValue) UseEssentialsConfig(); }; // load config if it's already there if (Eisc.BooleanOutput[JoinMap.ConfigIsReady.JoinNumber].BoolValue) { LoadConfigValues(); } if (Eisc.BooleanOutput[JoinMap.ConfigIsLocal.JoinNumber].BoolValue) { UseEssentialsConfig(); } }); } /// /// Finish wiring up everything after all devices are created. The base class will hunt down the related /// parent controller and link them up. /// /// /// /// CustomActivate method /// public override bool CustomActivate() { Debug.Console(0, this, "Final activation. Setting up actions and feedbacks"); //SetupFunctions(); //SetupFeedbacks(); var atcKey = string.Format("atc-{0}-{1}", Key, Key); _atcMessenger = new SIMPLAtcMessenger(atcKey, Eisc, "/device/audioCodec"); _atcMessenger.RegisterWithAppServer(Parent); var vtcKey = string.Format("atc-{0}-{1}", Key, Key); _vtcMessenger = new SIMPLVtcMessenger(vtcKey, Eisc, "/device/videoCodec"); _vtcMessenger.RegisterWithAppServer(Parent); var drKey = string.Format("directRoute-{0}-{1}", Key, Key); _directRouteMessenger = new SimplDirectRouteMessenger(drKey, Eisc, "/routing"); _directRouteMessenger.RegisterWithAppServer(Parent); CrestronConsole.AddNewConsoleCommand(s => { JoinMap.PrintJoinMapInfo(); _atcMessenger.JoinMap.PrintJoinMapInfo(); _vtcMessenger.JoinMap.PrintJoinMapInfo(); _directRouteMessenger.JoinMap.PrintJoinMapInfo(); // TODO: Update Source Bridge to use new JoinMap scheme //_sourceBridge.JoinMap.PrintJoinMapInfo(); }, "printmobilebridge", "Prints MC-SIMPL bridge EISC data", ConsoleAccessLevelEnum.AccessOperator); return base.CustomActivate(); } private void UseEssentialsConfig() { ConfigIsLoaded = false; SetupDeviceMessengers(); Debug.Console(0, this, "******* ESSENTIALS CONFIG: \r{0}", JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented)); ConfigurationIsReady?.Invoke(this, new EventArgs()); ConfigIsLoaded = true; } protected override void RegisterActions() { SetupFunctions(); SetupFeedbacks(); } /// /// Setup the actions to take place on various incoming API calls /// private void SetupFunctions() { AddAction(@"/promptForCode", (id, content) => Eisc.PulseBool(JoinMap.PromptForCode.JoinNumber)); AddAction(@"/clientJoined", (id, content) => Eisc.PulseBool(JoinMap.ClientJoined.JoinNumber)); AddAction(@"/status", (id, content) => SendFullStatus()); AddAction(@"/source", (id, content) => { var msg = content.ToObject(); Eisc.SetString(JoinMap.CurrentSourceKey.JoinNumber, msg.SourceListItemKey); Eisc.PulseBool(JoinMap.SourceHasChanged.JoinNumber); }); AddAction(@"/defaultsource", (id, content) => Eisc.PulseBool(JoinMap.ActivityShare.JoinNumber)); AddAction(@"/activityPhone", (id, content) => Eisc.PulseBool(JoinMap.ActivityPhoneCall.JoinNumber)); AddAction(@"/activityVideo", (id, content) => Eisc.PulseBool(JoinMap.ActivityVideoCall.JoinNumber)); AddAction(@"/volumes/master/level", (id, content) => { var value = content["value"].Value(); Eisc.SetUshort(JoinMap.MasterVolume.JoinNumber, value); }); AddAction(@"/volumes/master/muteToggle", (id, content) => Eisc.PulseBool(JoinMap.MasterVolume.JoinNumber)); AddAction(@"/volumes/master/privacyMuteToggle", (id, content) => Eisc.PulseBool(JoinMap.PrivacyMute.JoinNumber)); // /xyzxyz/volumes/master/muteToggle ---> BoolInput[1] var volumeStart = JoinMap.VolumeJoinStart.JoinNumber; var volumeEnd = JoinMap.VolumeJoinStart.JoinNumber + JoinMap.VolumeJoinStart.JoinSpan; for (uint i = volumeStart; i <= volumeEnd; i++) { var index = i; AddAction(string.Format(@"/volumes/level-{0}/level", index), (id, content) => { var value = content["value"].Value(); Eisc.SetUshort(index, value); }); AddAction(string.Format(@"/volumes/level-{0}/muteToggle", index), (id, content) => Eisc.PulseBool(index)); } AddAction(@"/shutdownStart", (id, content) => Eisc.PulseBool(JoinMap.ShutdownStart.JoinNumber)); AddAction(@"/shutdownEnd", (id, content) => Eisc.PulseBool(JoinMap.ShutdownEnd.JoinNumber)); AddAction(@"/shutdownCancel", (id, content) => Eisc.PulseBool(JoinMap.ShutdownCancel.JoinNumber)); } /// /// /// /// private void SetupSourceFunctions(string devKey) { var sourceJoinMap = new SourceDeviceMapDictionary(); var prefix = string.Format("/device/{0}/", devKey); foreach (var item in sourceJoinMap) { var join = item.Value; AddAction(string.Format("{0}{1}", prefix, item.Key), (id, content) => { HandlePressAndHoldEisc(content, b => Eisc.SetBool(join, b)); }); } } private void HandlePressAndHoldEisc(JToken content, Action action) { var state = content.ToObject>(); var timerHandler = PressAndHoldHandler.GetPressAndHoldHandler(state.Value); if (timerHandler == null) { return; } timerHandler(state.Value, action); action(state.Value.Equals("true", StringComparison.InvariantCultureIgnoreCase)); } /// /// Links feedbacks to whatever is gonna happen! /// private void SetupFeedbacks() { // Power Eisc.SetBoolSigAction(JoinMap.RoomIsOn.JoinNumber, b => PostStatus(new { isOn = b })); // Source change things Eisc.SetSigTrueAction(JoinMap.SourceHasChanged.JoinNumber, () => PostStatus(new { selectedSourceKey = Eisc.StringOutput[JoinMap.CurrentSourceKey.JoinNumber].StringValue })); // Volume things Eisc.SetUShortSigAction(JoinMap.MasterVolume.JoinNumber, u => PostStatus(new { volumes = new { master = new { level = u } } })); // map MasterVolumeIsMuted join -> status/volumes/master/muted // Eisc.SetBoolSigAction(JoinMap.MasterVolume.JoinNumber, b => PostStatus(new { volumes = new { master = new { muted = b } } })); Eisc.SetBoolSigAction(JoinMap.PrivacyMute.JoinNumber, b => PostStatus(new { volumes = new { master = new { privacyMuted = b } } })); var volumeStart = JoinMap.VolumeJoinStart.JoinNumber; var volumeEnd = JoinMap.VolumeJoinStart.JoinNumber + JoinMap.VolumeJoinStart.JoinSpan; for (uint i = volumeStart; i <= volumeEnd; i++) { var index = i; // local scope for lambdas Eisc.SetUShortSigAction(index, u => // start at join 2 { // need a dict in order to create the level-n property on auxFaders var dict = new Dictionary { { "level-" + index, new { level = u } } }; PostStatus(new { volumes = new { auxFaders = dict, } }); }); Eisc.SetBoolSigAction(index, b => { // need a dict in order to create the level-n property on auxFaders var dict = new Dictionary { { "level-" + index, new { muted = b } } }; PostStatus(new { volumes = new { auxFaders = dict, } }); }); } Eisc.SetUShortSigAction(JoinMap.NumberOfAuxFaders.JoinNumber, u => PostStatus(new { volumes = new { numberOfAuxFaders = u, } })); // shutdown things Eisc.SetSigTrueAction(JoinMap.ShutdownCancel.JoinNumber, () => PostMessage("/shutdown/", new { state = "wasCancelled" })); Eisc.SetSigTrueAction(JoinMap.ShutdownEnd.JoinNumber, () => PostMessage("/shutdown/", new { state = "hasFinished" })); Eisc.SetSigTrueAction(JoinMap.ShutdownStart.JoinNumber, () => PostMessage("/shutdown/", new { state = "hasStarted", duration = Eisc.UShortOutput[JoinMap.ShutdownPromptDuration.JoinNumber].UShortValue })); // Config things Eisc.SetSigTrueAction(JoinMap.ConfigIsReady.JoinNumber, LoadConfigValues); // Activity modes Eisc.SetSigTrueAction(JoinMap.ActivityShare.JoinNumber, () => UpdateActivity(1)); Eisc.SetSigTrueAction(JoinMap.ActivityPhoneCall.JoinNumber, () => UpdateActivity(2)); Eisc.SetSigTrueAction(JoinMap.ActivityVideoCall.JoinNumber, () => UpdateActivity(3)); AppServerController.ApiOnlineAndAuthorized.LinkInputSig(Eisc.BooleanInput[JoinMap.ApiOnlineAndAuthorized.JoinNumber]); } /// /// Updates activity states /// private void UpdateActivity(int mode) { PostStatus(new { activityMode = mode, }); } /// /// Synthesizes a source device config from the SIMPL config join data /// /// /// /// /// private DeviceConfig GetSyntheticSourceDevice(SourceListItem sli, string type, uint i) { var groupMap = GetSourceGroupDictionary(); var key = sli.SourceKey; var name = sli.Name; // If not, synthesize the device config var group = "genericsource"; if (groupMap.ContainsKey(type)) { group = groupMap[type]; } // add dev to devices list var devConf = new DeviceConfig { Group = group, Key = key, Name = name, Type = type, Properties = new JObject(new JProperty(_syntheticDeviceKey, true)), }; if (group.ToLower().StartsWith("settopbox")) // Add others here as needed { SetupSourceFunctions(key); } if (group.ToLower().Equals("simplmessenger")) { if (type.ToLower().Equals("simplcameramessenger")) { var props = new SimplMessengerPropertiesConfig { DeviceKey = key, JoinMapKey = "" }; var joinStart = 1000 + (i * 100) + 1; // 1001, 1101, 1201, 1301... etc. props.JoinStart = joinStart; devConf.Properties = JToken.FromObject(props); } } return devConf; } /// /// Reads in config values when the Simpl program is ready /// private void LoadConfigValues() { Debug.Console(1, this, "Loading configuration from SIMPL EISC bridge"); ConfigIsLoaded = false; var co = ConfigReader.ConfigObject; if (!string.IsNullOrEmpty(Eisc.StringOutput[JoinMap.PortalSystemUrl.JoinNumber].StringValue)) { ConfigReader.ConfigObject.SystemUrl = Eisc.StringOutput[JoinMap.PortalSystemUrl.JoinNumber].StringValue; } co.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name; var version = Assembly.GetExecutingAssembly().GetName().Version; co.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build); //Room //if (co.Rooms == null) // always start fresh in case simpl changed co.Rooms = new List(); var rm = new DeviceConfig(); if (co.Rooms.Count == 0) { Debug.Console(0, this, "Adding room to config"); co.Rooms.Add(rm); } else { Debug.Console(0, this, "Replacing Room[0] in config"); co.Rooms[0] = rm; } rm.Name = Eisc.StringOutput[JoinMap.ConfigRoomName.JoinNumber].StringValue; rm.Key = "room1"; rm.Type = "SIMPL01"; var rmProps = rm.Properties == null ? new SimplRoomPropertiesConfig() : JsonConvert.DeserializeObject(rm.Properties.ToString()); rmProps.Help = new EssentialsHelpPropertiesConfig { CallButtonText = Eisc.StringOutput[JoinMap.ConfigHelpNumber.JoinNumber].StringValue, Message = Eisc.StringOutput[JoinMap.ConfigHelpMessage.JoinNumber].StringValue }; rmProps.Environment = new EssentialsEnvironmentPropertiesConfig(); // enabled defaults to false rmProps.RoomPhoneNumber = Eisc.StringOutput[JoinMap.ConfigRoomPhoneNumber.JoinNumber].StringValue; rmProps.RoomURI = Eisc.StringOutput[JoinMap.ConfigRoomUri.JoinNumber].StringValue; rmProps.SpeedDials = new List(); // This MAY need a check if (Eisc.BooleanOutput[JoinMap.ActivityPhoneCallEnable.JoinNumber].BoolValue) { rmProps.AudioCodecKey = "audioCodec"; } if (Eisc.BooleanOutput[JoinMap.ActivityVideoCallEnable.JoinNumber].BoolValue) { rmProps.VideoCodecKey = "videoCodec"; } // volume control names //// use Volumes object or? //rmProps.VolumeSliderNames = new List(); //for(uint i = 701; i <= 700 + volCount; i++) //{ // rmProps.VolumeSliderNames.Add(EISC.StringInput[i].StringValue); //} // There should be Mobile Control devices in here, I think... if (co.Devices == null) co.Devices = new List(); // clear out previous SIMPL devices co.Devices.RemoveAll(d => d.Key.StartsWith("source-", StringComparison.OrdinalIgnoreCase) || d.Key.Equals("audioCodec", StringComparison.OrdinalIgnoreCase) || d.Key.Equals("videoCodec", StringComparison.OrdinalIgnoreCase) || d.Key.StartsWith("destination-", StringComparison.OrdinalIgnoreCase)); rmProps.SourceListKey = "default"; rm.Properties = JToken.FromObject(rmProps); // Source list! This might be brutal!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! co.SourceLists = new Dictionary>(); var newSl = new Dictionary(); // add "none" source if VTC present if (!string.IsNullOrEmpty(rmProps.VideoCodecKey)) { var codecOsd = new SourceListItem { Name = "None", IncludeInSourceList = true, Order = 1, Type = eSourceListItemType.Route, SourceKey = "" }; newSl.Add("Source-None", codecOsd); } // add sources... var useSourceEnabled = Eisc.BooleanOutput[JoinMap.UseSourceEnabled.JoinNumber].BoolValue; for (uint i = 0; i <= 19; i++) { var name = Eisc.StringOutput[JoinMap.SourceNameJoinStart.JoinNumber + i].StringValue; if (!Eisc.BooleanOutput[JoinMap.UseSourceEnabled.JoinNumber].BoolValue && string.IsNullOrEmpty(name)) { this.LogDebug("Source at join {join} does not have a name", JoinMap.SourceNameJoinStart.JoinNumber + i); break; } var icon = Eisc.StringOutput[JoinMap.SourceIconJoinStart.JoinNumber + i].StringValue; var key = Eisc.StringOutput[JoinMap.SourceKeyJoinStart.JoinNumber + i].StringValue; var type = Eisc.StringOutput[JoinMap.SourceTypeJoinStart.JoinNumber + i].StringValue; var disableShare = Eisc.BooleanOutput[JoinMap.SourceShareDisableJoinStart.JoinNumber + i].BoolValue; var sourceEnabled = Eisc.BooleanOutput[JoinMap.SourceIsEnabledJoinStart.JoinNumber + i].BoolValue; var controllable = Eisc.BooleanOutput[JoinMap.SourceIsControllableJoinStart.JoinNumber + i].BoolValue; var audioSource = Eisc.BooleanOutput[JoinMap.SourceIsAudioSourceJoinStart.JoinNumber + i].BoolValue; Debug.Console(0, this, "Adding source {0} '{1}'", key, name); var sourceKey = Eisc.StringOutput[JoinMap.SourceControlDeviceKeyJoinStart.JoinNumber + i].StringValue; var newSli = new SourceListItem { Icon = icon, Name = name, Order = (int)i + 10, SourceKey = string.IsNullOrEmpty(sourceKey) ? key : sourceKey, // Use the value from the join if defined Type = eSourceListItemType.Route, DisableCodecSharing = disableShare, IncludeInSourceList = !useSourceEnabled || sourceEnabled, IsControllable = controllable, IsAudioSource = audioSource }; newSl.Add(key, newSli); var existingSourceDevice = co.GetDeviceForKey(newSli.SourceKey); var syntheticDevice = GetSyntheticSourceDevice(newSli, type, i); // Look to see if this is a device that already exists in Essentials and get it if (existingSourceDevice != null) { Debug.Console(0, this, "Found device with key: {0} in Essentials.", key); if (existingSourceDevice.Properties.Value(_syntheticDeviceKey)) { Debug.Console(0, this, "Updating previous device config with new values"); existingSourceDevice = syntheticDevice; } else { Debug.Console(0, this, "Using existing Essentials device (non synthetic)"); } } else { co.Devices.Add(syntheticDevice); } } co.SourceLists.Add("default", newSl); if (Eisc.BooleanOutput[JoinMap.SupportsAdvancedSharing.JoinNumber].BoolValue) { if (co.DestinationLists == null) { co.DestinationLists = new Dictionary>(); } CreateDestinationList(co); } // Build "audioCodec" config if we need if (!string.IsNullOrEmpty(rmProps.AudioCodecKey)) { var acFavs = new List(); for (uint i = 0; i < 4; i++) { if (!Eisc.GetBool(JoinMap.SpeedDialVisibleStartJoin.JoinNumber + i)) { break; } acFavs.Add(new CodecActiveCallItem { Name = Eisc.GetString(JoinMap.SpeedDialNameStartJoin.JoinNumber + i), Number = Eisc.GetString(JoinMap.SpeedDialNumberStartJoin.JoinNumber + i), Type = eCodecCallType.Audio }); } var acProps = new { favorites = acFavs }; const string acStr = "audioCodec"; var acConf = new DeviceConfig { Group = acStr, Key = acStr, Name = acStr, Type = acStr, Properties = JToken.FromObject(acProps) }; co.Devices.Add(acConf); } // Build Video codec config if (!string.IsNullOrEmpty(rmProps.VideoCodecKey)) { // No favorites, for now? var favs = new List(); // cameras var camsProps = new List(); for (uint i = 0; i < 9; i++) { var name = Eisc.GetString(i + JoinMap.CameraNearNameStart.JoinNumber); if (!string.IsNullOrEmpty(name)) { camsProps.Add(new { name, selector = "camera" + (i + 1), }); } } var farName = Eisc.GetString(JoinMap.CameraFarName.JoinNumber); if (!string.IsNullOrEmpty(farName)) { camsProps.Add(new { name = farName, selector = "cameraFar", }); } var props = new { favorites = favs, cameras = camsProps, }; const string str = "videoCodec"; var conf = new DeviceConfig { Group = str, Key = str, Name = str, Type = str, Properties = JToken.FromObject(props) }; co.Devices.Add(conf); } SetupDeviceMessengers(); Debug.Console(0, this, "******* CONFIG FROM SIMPL: \r{0}", JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented)); ConfigurationIsReady?.Invoke(this, new EventArgs()); ConfigIsLoaded = true; } private DeviceConfig GetSyntheticDestinationDevice(string key, string name) { // If not, synthesize the device config var devConf = new DeviceConfig { Group = "genericdestination", Key = key, Name = name, Type = "genericdestination", Properties = new JObject(new JProperty(_syntheticDeviceKey, true)), }; return devConf; } private void CreateDestinationList(BasicConfig co) { var useDestEnable = Eisc.BooleanOutput[JoinMap.UseDestinationEnable.JoinNumber].BoolValue; var newDl = new Dictionary(); for (uint i = 0; i < SupportedDisplayCount; i++) { var name = Eisc.StringOutput[JoinMap.DestinationNameJoinStart.JoinNumber + i].StringValue; var routeType = Eisc.StringOutput[JoinMap.DestinationTypeJoinStart.JoinNumber + i].StringValue; var key = Eisc.StringOutput[JoinMap.DestinationDeviceKeyJoinStart.JoinNumber + i].StringValue; //var order = Eisc.UShortOutput[JoinMap.DestinationOrderJoinStart.JoinNumber + i].UShortValue; var enabled = Eisc.BooleanOutput[JoinMap.DestinationIsEnabledJoinStart.JoinNumber + i].BoolValue; if (useDestEnable && !enabled) { continue; } if (string.IsNullOrEmpty(key)) { continue; } Debug.Console(0, this, "Adding destination {0} - {1}", key, name); eRoutingSignalType parsedType; try { parsedType = (eRoutingSignalType)Enum.Parse(typeof(eRoutingSignalType), routeType, true); } catch { Debug.Console(0, this, "Error parsing destination type: {0}", routeType); parsedType = eRoutingSignalType.AudioVideo; } var newDli = new DestinationListItem { Name = name, Order = (int)i, SinkKey = key, SinkType = parsedType, }; if (!newDl.ContainsKey(key)) { newDl.Add(key, newDli); } else { newDl[key] = newDli; } if (!_directRouteMessenger.DestinationList.ContainsKey(newDli.SinkKey)) { //add same DestinationListItem to dictionary for messenger in order to allow for correlation by index _directRouteMessenger.DestinationList.Add(key, newDli); } else { _directRouteMessenger.DestinationList[key] = newDli; } var existingDev = co.GetDeviceForKey(key); var syntheticDisplay = GetSyntheticDestinationDevice(key, name); if (existingDev != null) { Debug.Console(0, this, "Found device with key: {0} in Essentials.", key); if (existingDev.Properties.Value(_syntheticDeviceKey)) { Debug.Console(0, this, "Updating previous device config with new values"); } else { Debug.Console(0, this, "Using existing Essentials device (non synthetic)"); } } else { co.Devices.Add(syntheticDisplay); } } if (!co.DestinationLists.ContainsKey("default")) { co.DestinationLists.Add("default", newDl); } else { co.DestinationLists["default"] = newDl; } _directRouteMessenger.RegisterForDestinationPaths(); } /// /// Iterates device config and adds messengers as neede for each device type /// private void SetupDeviceMessengers() { DeviceMessengers = new Dictionary(); try { foreach (var device in ConfigReader.ConfigObject.Devices) { if (device.Group.Equals("simplmessenger")) { var props = JsonConvert.DeserializeObject(device.Properties.ToString()); var messengerKey = string.Format("device-{0}-{1}", Key, Key); if (DeviceManager.GetDeviceForKey(messengerKey) != null) { Debug.Console(2, this, "Messenger with key: {0} already exists. Skipping...", messengerKey); continue; } var dev = ConfigReader.ConfigObject.GetDeviceForKey(props.DeviceKey); if (dev == null) { Debug.Console(1, this, "Unable to find device config for key: '{0}'", props.DeviceKey); continue; } var type = device.Type.ToLower(); MessengerBase messenger = null; if (type.Equals("simplcameramessenger")) { Debug.Console(2, this, "Adding SIMPLCameraMessenger for: '{0}'", props.DeviceKey); messenger = new SIMPLCameraMessenger(messengerKey, Eisc, "/device/" + props.DeviceKey, props.JoinStart); } else if (type.Equals("simplroutemessenger")) { Debug.Console(2, this, "Adding SIMPLRouteMessenger for: '{0}'", props.DeviceKey); messenger = new SIMPLRouteMessenger(messengerKey, Eisc, "/device/" + props.DeviceKey, props.JoinStart); } if (messenger != null) { DeviceManager.AddDevice(messenger); DeviceMessengers.Add(device.Key, messenger); messenger.RegisterWithAppServer(Parent); } else { Debug.Console(2, this, "Unable to add messenger for device: '{0}' of type: '{1}'", props.DeviceKey, type); } } else { var dev = DeviceManager.GetDeviceForKey(device.Key); if (dev != null) { if (dev is CameraBase) { var camDevice = dev as CameraBase; Debug.Console(1, this, "Adding CameraBaseMessenger for device: {0}", dev.Key); var cameraMessenger = new CameraBaseMessenger(device.Key + "-" + Key, camDevice, "/device/" + device.Key); DeviceMessengers.Add(device.Key, cameraMessenger); DeviceManager.AddDevice(cameraMessenger); cameraMessenger.RegisterWithAppServer(Parent); continue; } } } } } catch (Exception e) { Debug.Console(2, this, "Error Setting up Device Managers: {0}", e); } } /// /// /// private void SendFullStatus() { if (ConfigIsLoaded) { var count = Eisc.UShortOutput[JoinMap.NumberOfAuxFaders.JoinNumber].UShortValue; Debug.Console(1, this, "The Fader Count is : {0}", count); // build volumes object, serialize and put in content of method below // Create auxFaders var auxFaderDict = new Dictionary(); var volumeStart = JoinMap.VolumeJoinStart.JoinNumber; for (var i = volumeStart; i <= count; i++) { auxFaderDict.Add("level-" + i, new Volume("level-" + i, Eisc.UShortOutput[i].UShortValue, Eisc.BooleanOutput[i].BoolValue, Eisc.StringOutput[i].StringValue, true, "someting.png")); } var volumes = new Volumes { Master = new Volume("master", Eisc.UShortOutput[JoinMap.MasterVolume.JoinNumber].UShortValue, Eisc.BooleanOutput[JoinMap.MasterVolume.JoinNumber].BoolValue, Eisc.StringOutput[JoinMap.MasterVolume.JoinNumber].StringValue, true, "something.png") { HasPrivacyMute = true, PrivacyMuted = Eisc.BooleanOutput[JoinMap.PrivacyMute.JoinNumber].BoolValue }, AuxFaders = auxFaderDict, NumberOfAuxFaders = Eisc.UShortInput[JoinMap.NumberOfAuxFaders.JoinNumber].UShortValue }; // TODO: Add property to status message to indicate if advanced sharing is supported and if users can change share mode PostStatus(new { activityMode = GetActivityMode(), isOn = Eisc.BooleanOutput[JoinMap.RoomIsOn.JoinNumber].BoolValue, selectedSourceKey = Eisc.StringOutput[JoinMap.CurrentSourceKey.JoinNumber].StringValue, volumes, supportsAdvancedSharing = Eisc.BooleanOutput[JoinMap.SupportsAdvancedSharing.JoinNumber].BoolValue, userCanChangeShareMode = Eisc.BooleanOutput[JoinMap.UserCanChangeShareMode.JoinNumber].BoolValue, }); } else { PostStatus(new { error = "systemNotReady" }); } } /// /// Returns the activity mode int /// /// private int GetActivityMode() { if (Eisc.BooleanOutput[JoinMap.ActivityPhoneCall.JoinNumber].BoolValue) return 2; if (Eisc.BooleanOutput[JoinMap.ActivityShare.JoinNumber].BoolValue) return 1; return Eisc.BooleanOutput[JoinMap.ActivityVideoCall.JoinNumber].BoolValue ? 3 : 0; } /// /// Helper for posting status message /// /// The contents of the content object private void PostStatus(object contentObject) { AppServerController.SendMessageObject(new MobileControlMessage { Type = "/status/", Content = JToken.FromObject(contentObject) }); } /// /// /// /// /// private void PostMessage(string messageType, object contentObject) { AppServerController.SendMessageObject(new MobileControlMessage { Type = messageType, Content = JToken.FromObject(contentObject) }); } /// /// /// /// /// private void EISC_SigChange(object currentDevice, SigEventArgs args) { if (Debug.Level >= 1) Debug.Console(1, this, "SIMPL EISC change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); var uo = args.Sig.UserObject; if (uo != null) { if (uo is Action) (uo as Action)(args.Sig.BoolValue); else if (uo is Action) (uo as Action)(args.Sig.UShortValue); else if (uo is Action) (uo as Action)(args.Sig.StringValue); } } /// /// Returns the mapping of types to groups, for setting up devices. /// /// private Dictionary GetSourceGroupDictionary() { //type, group var d = new Dictionary(StringComparer.OrdinalIgnoreCase) { {"laptop", "pc"}, {"pc", "pc"}, {"wireless", "genericsource"}, {"iptv", "settopbox"}, {"simplcameramessenger", "simplmessenger"}, {"camera", "camera"}, }; return d; } /// /// updates the usercode from server /// protected override void UserCodeChange() { Debug.Console(1, this, "Server user code changed: {0}", UserCode); var qrUrl = string.Format("{0}/api/rooms/{1}/{3}/qr?x={2}", AppServerController.Host, AppServerController.SystemUuid, new Random().Next(), "room1"); QrCodeUrl = qrUrl; Debug.Console(1, this, "Server user code changed: {0} - {1}", UserCode, qrUrl); OnUserCodeChanged(); Eisc.StringInput[JoinMap.UserCodeToSystem.JoinNumber].StringValue = UserCode; Eisc.StringInput[JoinMap.ServerUrl.JoinNumber].StringValue = McServerUrl; Eisc.StringInput[JoinMap.QrCodeUrl.JoinNumber].StringValue = QrCodeUrl; } } }