Files
Essentials/src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlSIMPLRoomBridge.cs
Andrew Welker f6fdc14059 feat: move MC into Essentials
In order to solve some dependency issues that keep cropping up, MC
should be moved back into the Essentials repo and loaded automatically
on startup. This will allow for all plugins that use the MC Messengers
library to use the same version without fear of overwriting a dll due to
loading of plugin libraries.
2025-03-24 22:28:27 -05:00

1128 lines
43 KiB
C#

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.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
public class MobileControlSIMPLRoomBridge : MobileControlBridgeBase, IDelayedConfiguration
{
private const int SupportedDisplayCount = 10;
/// <summary>
/// Fires when config is ready to go
/// </summary>
public event EventHandler<EventArgs> ConfigurationIsReady;
public ThreeSeriesTcpIpEthernetIntersystemCommunications Eisc { get; private set; }
public MobileControlSIMPLRoomJoinMap JoinMap { get; private set; }
public Dictionary<string, MessengerBase> DeviceMessengers { get; private set; }
/// <summary>
///
/// </summary>
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";
/// <summary>
///
/// </summary>
/// <param name="key"></param>
/// <param name="name"></param>
/// <param name="ipId"></param>
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();
}
});
}
/// <summary>
/// Finish wiring up everything after all devices are created. The base class will hunt down the related
/// parent controller and link them up.
/// </summary>
/// <returns></returns>
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;
}
#if SERIES4
protected override void RegisterActions()
#else
protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
#endif
{
SetupFunctions();
SetupFeedbacks();
}
/// <summary>
/// Setup the actions to take place on various incoming API calls
/// </summary>
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<SourceSelectMessageContent>();
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<ushort>();
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<ushort>();
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));
}
/// <summary>
///
/// </summary>
/// <param name="devKey"></param>
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<bool> action)
{
var state = content.ToObject<MobileControlSimpleContent<string>>();
var timerHandler = PressAndHoldHandler.GetPressAndHoldHandler(state.Value);
if (timerHandler == null)
{
return;
}
timerHandler(state.Value, action);
action(state.Value.Equals("true", StringComparison.InvariantCultureIgnoreCase));
}
/// <summary>
/// Links feedbacks to whatever is gonna happen!
/// </summary>
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<string, object> { { "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<string, object> { { "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]);
}
/// <summary>
/// Updates activity states
/// </summary>
private void UpdateActivity(int mode)
{
PostStatus(new
{
activityMode = mode,
});
}
/// <summary>
/// Synthesizes a source device config from the SIMPL config join data
/// </summary>
/// <param name="sli"></param>
/// <param name="type"></param>
/// <param name="i"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Reads in config values when the Simpl program is ready
/// </summary>
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<DeviceConfig>();
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<SimplRoomPropertiesConfig>(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<SimplSpeedDial>();
// 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<string>();
//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<DeviceConfig>();
// 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<string, Dictionary<string, SourceListItem>>();
var newSl = new Dictionary<string, SourceListItem>();
// 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))
{
Debug.Console(1, "Source at join {0} 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<bool>(_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<string, Dictionary<string, DestinationListItem>>();
}
CreateDestinationList(co);
}
// Build "audioCodec" config if we need
if (!string.IsNullOrEmpty(rmProps.AudioCodecKey))
{
var acFavs = new List<CodecActiveCallItem>();
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<CodecActiveCallItem>();
// cameras
var camsProps = new List<object>();
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<string, DestinationListItem>();
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<bool>(_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();
}
/// <summary>
/// Iterates device config and adds messengers as neede for each device type
/// </summary>
private void SetupDeviceMessengers()
{
DeviceMessengers = new Dictionary<string, MessengerBase>();
try
{
foreach (var device in ConfigReader.ConfigObject.Devices)
{
if (device.Group.Equals("simplmessenger"))
{
var props =
JsonConvert.DeserializeObject<SimplMessengerPropertiesConfig>(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);
}
}
/// <summary>
///
/// </summary>
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<string, Volume>();
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"
});
}
}
/// <summary>
/// Returns the activity mode int
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// Helper for posting status message
/// </summary>
/// <param name="contentObject">The contents of the content object</param>
private void PostStatus(object contentObject)
{
AppServerController.SendMessageObject(new MobileControlMessage
{
Type = "/status/",
Content = JToken.FromObject(contentObject)
});
}
/// <summary>
///
/// </summary>
/// <param name="messageType"></param>
/// <param name="contentObject"></param>
private void PostMessage(string messageType, object contentObject)
{
AppServerController.SendMessageObject(new MobileControlMessage
{
Type = messageType,
Content = JToken.FromObject(contentObject)
});
}
/// <summary>
///
/// </summary>
/// <param name="currentDevice"></param>
/// <param name="args"></param>
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<bool>)
(uo as Action<bool>)(args.Sig.BoolValue);
else if (uo is Action<ushort>)
(uo as Action<ushort>)(args.Sig.UShortValue);
else if (uo is Action<string>)
(uo as Action<string>)(args.Sig.StringValue);
}
}
/// <summary>
/// Returns the mapping of types to groups, for setting up devices.
/// </summary>
/// <returns></returns>
private Dictionary<string, string> GetSourceGroupDictionary()
{
//type, group
var d = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{"laptop", "pc"},
{"pc", "pc"},
{"wireless", "genericsource"},
{"iptv", "settopbox"},
{"simplcameramessenger", "simplmessenger"},
{"camera", "camera"},
};
return d;
}
/// <summary>
/// updates the usercode from server
/// </summary>
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;
}
}
}