mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-02-01 22:04:52 +00:00
1126 lines
43 KiB
C#
1126 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.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
|
|
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;
|
|
}
|
|
|
|
protected override void RegisterActions()
|
|
|
|
{
|
|
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))
|
|
{
|
|
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<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;
|
|
|
|
}
|
|
}
|
|
} |