using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
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.Room.Config;
namespace PepperDash.Essentials.Room.MobileControl
{
public class MobileControlSIMPLRoomBridge : MobileControlBridgeBase, IDelayedConfiguration
{
///
/// Fires when config is ready to go
///
public event EventHandler ConfigurationIsReady;
public ThreeSeriesTcpIpEthernetIntersystemCommunications EISC { get; private set; }
public MobileControlSIMPLRoomJoinMap JoinMap { get; private set; }
///
///
///
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;
}
}
MobileControlDdvc01DeviceBridge SourceBridge;
SIMPLAtcMessenger AtcMessenger;
SIMPLVtcMessenger VtcMessenger;
///
///
///
///
///
///
public MobileControlSIMPLRoomBridge(string key, string name, uint ipId)
: base(key, name)
{
try
{
EISC = new ThreeSeriesTcpIpEthernetIntersystemCommunications(ipId, "127.0.0.2", Global.ControlSystem);
var reg = EISC.Register();
if (reg != Crestron.SimplSharpPro.eDeviceRegistrationUnRegistrationResponse.Success)
Debug.Console(0, this, "Cannot connect EISC at IPID {0}: \r{1}", ipId, reg);
JoinMap = new MobileControlSIMPLRoomJoinMap(1);
SourceBridge = new MobileControlDdvc01DeviceBridge(key + "-sourceBridge", "DDVC01 source bridge", EISC);
DeviceManager.AddDevice(SourceBridge);
}
catch (Exception)
{
throw;
}
}
///
/// Finish wiring up everything after all devices are created. The base class will hunt down the related
/// parent controller and link them up.
///
///
public override bool CustomActivate()
{
Debug.Console(0, this, "Final activation. Setting up actions and feedbacks");
SetupFunctions();
SetupFeedbacks();
var atcKey = string.Format("atc-{0}-{1}", this.Key, Parent.Key);
AtcMessenger = new SIMPLAtcMessenger(atcKey, EISC, "/device/audioCodec");
AtcMessenger.RegisterWithAppServer(Parent);
var vtcKey = string.Format("atc-{0}-{1}", this.Key, Parent.Key);
VtcMessenger = new SIMPLVtcMessenger(vtcKey, EISC, "/device/videoCodec");
VtcMessenger.RegisterWithAppServer(Parent);
EISC.SigChange += EISC_SigChange;
EISC.OnlineStatusChange += (o, a) =>
{
Debug.Console(1, this, "DDVC 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 (a.DeviceOnLine && EISC.BooleanOutput[JoinMap.ConfigIsReady.JoinNumber].BoolValue)
LoadConfigValues();
if (a.DeviceOnLine && EISC.BooleanOutput[JoinMap.ConfigIsLocal.JoinNumber].BoolValue)
UseEssentialsConfig();
};
// load config if it's already there
if (EISC.IsOnline && EISC.BooleanOutput[JoinMap.ConfigIsReady.JoinNumber].BoolValue) // || EISC.BooleanInput[JoinMap.ConfigIsReady].BoolValue)
LoadConfigValues();
if (EISC.IsOnline && EISC.BooleanOutput[JoinMap.ConfigIsLocal.JoinNumber].BoolValue)
{
UseEssentialsConfig();
}
CrestronConsole.AddNewConsoleCommand(s =>
{
for (uint i = 1; i < 1000; i++)
{
if (s.ToLower().Equals("b"))
{
CrestronConsole.ConsoleCommandResponse("D{0,6} {1} - ", i, EISC.BooleanOutput[i].BoolValue);
}
else if (s.ToLower().Equals("u"))
{
CrestronConsole.ConsoleCommandResponse("U{0,6} {1,8} - ", i, EISC.UShortOutput[i].UShortValue);
}
else if (s.ToLower().Equals("s"))
{
var val = EISC.StringOutput[i].StringValue;
if(!string.IsNullOrEmpty(val))
CrestronConsole.ConsoleCommandResponse("S{0,6} {1}\r", i, EISC.StringOutput[i].StringValue);
}
}
}, "mobilebridgedump", "Dumps DDVC01 bridge EISC data b,u,s", ConsoleAccessLevelEnum.AccessOperator);
return base.CustomActivate();
}
void UseEssentialsConfig()
{
ConfigIsLoaded = false;
SetupDeviceMessengers();
Debug.Console(0, this, "******* ESSENTIALS CONFIG: \r{0}", JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented));
var handler = ConfigurationIsReady;
if (handler != null)
{
handler(this, new EventArgs());
}
ConfigIsLoaded = true;
}
///
/// Setup the actions to take place on various incoming API calls
///
void SetupFunctions()
{
Parent.AddAction(@"/room/room1/promptForCode", new Action(() => EISC.PulseBool(JoinMap.PromptForCode.JoinNumber)));
Parent.AddAction(@"/room/room1/clientJoined", new Action(() => EISC.PulseBool(JoinMap.ClientJoined.JoinNumber)));
Parent.AddAction(@"/room/room1/status", new Action(SendFullStatus));
Parent.AddAction(@"/room/room1/source", new Action(c =>
{
EISC.SetString(JoinMap.CurrentSourceKey.JoinNumber, c.SourceListItem);
EISC.PulseBool(JoinMap.SourceHasChanged.JoinNumber);
}));
Parent.AddAction(@"/room/room1/defaultsource", new Action(() =>
EISC.PulseBool(JoinMap.ActivityShare.JoinNumber)));
Parent.AddAction(@"/room/room1/activityPhone", new Action(() =>
EISC.PulseBool(JoinMap.ActivityPhoneCall.JoinNumber)));
Parent.AddAction(@"/room/room1/activityVideo", new Action(() =>
EISC.PulseBool(JoinMap.ActivityVideoCall.JoinNumber)));
Parent.AddAction(@"/room/room1/volumes/master/level", new Action(u =>
EISC.SetUshort(JoinMap.MasterVolume.JoinNumber, u)));
Parent.AddAction(@"/room/room1/volumes/master/muteToggle", new Action(() =>
EISC.PulseBool(JoinMap.MasterVolume.JoinNumber)));
Parent.AddAction(@"/room/room1/volumes/master/privacyMuteToggle", new Action(() =>
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;
Parent.AddAction(string.Format(@"/room/room1/volumes/level-{0}/level", index), new Action(u =>
EISC.SetUshort(index, u)));
Parent.AddAction(string.Format(@"/room/room1/volumes/level-{0}/muteToggle", index), new Action(() =>
EISC.PulseBool(index)));
}
Parent.AddAction(@"/room/room1/shutdownStart", new Action(() =>
EISC.PulseBool(JoinMap.ShutdownStart.JoinNumber)));
Parent.AddAction(@"/room/room1/shutdownEnd", new Action(() =>
EISC.PulseBool(JoinMap.ShutdownEnd.JoinNumber)));
Parent.AddAction(@"/room/room1/shutdownCancel", new Action(() =>
EISC.PulseBool(JoinMap.ShutdownCancel.JoinNumber)));
}
///
///
///
///
void SetupSourceFunctions(string devKey)
{
SourceDeviceMapDictionary sourceJoinMap = new SourceDeviceMapDictionary();
var prefix = string.Format("/device/{0}/", devKey);
foreach (var item in sourceJoinMap)
{
var join = item.Value;
Parent.AddAction(string.Format("{0}{1}", prefix, item.Key), new PressAndHoldAction(b => EISC.SetBool(join, b)));
}
}
///
/// Links feedbacks to whatever is gonna happen!
///
void SetupFeedbacks()
{
// Power
EISC.SetBoolSigAction(JoinMap.RoomIsOn.JoinNumber, b =>
PostStatusMessage(new
{
isOn = b
}));
// Source change things
EISC.SetSigTrueAction(JoinMap.SourceHasChanged.JoinNumber, () =>
PostStatusMessage(new
{
selectedSourceKey = EISC.StringOutput[JoinMap.CurrentSourceKey.JoinNumber].StringValue
}));
// Volume things
EISC.SetUShortSigAction(JoinMap.MasterVolume.JoinNumber, u =>
PostStatusMessage(new
{
volumes = new
{
master = new
{
level = u
}
}
}));
// map MasterVolumeIsMuted join -> status/volumes/master/muted
//
EISC.SetBoolSigAction(JoinMap.MasterVolume.JoinNumber, b =>
PostStatusMessage(new
{
volumes = new
{
master = new
{
muted = b
}
}
}));
EISC.SetBoolSigAction(JoinMap.PrivacyMute.JoinNumber, b =>
PostStatusMessage(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();
dict.Add("level-" + index, new { level = u });
PostStatusMessage(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();
dict.Add("level-" + index, new { muted = b });
PostStatusMessage(new
{
volumes = new
{
auxFaders = dict,
}
});
});
}
EISC.SetUShortSigAction(JoinMap.NumberOfAuxFaders.JoinNumber, u =>
PostStatusMessage(new {
volumes = new {
numberOfAuxFaders = u,
}
}));
// shutdown things
EISC.SetSigTrueAction(JoinMap.ShutdownCancel.JoinNumber, new Action(() =>
PostMessage("/room/shutdown/", new
{
state = "wasCancelled"
})));
EISC.SetSigTrueAction(JoinMap.ShutdownEnd.JoinNumber, new Action(() =>
PostMessage("/room/shutdown/", new
{
state = "hasFinished"
})));
EISC.SetSigTrueAction(JoinMap.ShutdownStart.JoinNumber, new Action(() =>
PostMessage("/room/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));
}
///
/// Updates activity states
///
void UpdateActivity(int mode)
{
PostStatusMessage(new
{
activityMode = mode,
});
}
///
/// Reads in config values when the Simpl program is ready
///
void LoadConfigValues()
{
Debug.Console(1, this, "Loading configuration from DDVC01 EISC bridge");
ConfigIsLoaded = false;
var co = ConfigReader.ConfigObject;
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 = "ddvc01";
DDVC01RoomPropertiesConfig rmProps;
if (rm.Properties == null)
rmProps = new DDVC01RoomPropertiesConfig();
else
rmProps = JsonConvert.DeserializeObject(rm.Properties.ToString());
rmProps.Help = new EssentialsHelpPropertiesConfig();
rmProps.Help.CallButtonText = EISC.StringOutput[JoinMap.ConfigHelpNumber.JoinNumber].StringValue;
rmProps.Help.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
rmProps.AudioCodecKey = "audioCodec";
rmProps.VideoCodecKey = "videoCodec";
// volume control names
var volCount = EISC.UShortOutput[JoinMap.NumberOfAuxFaders.JoinNumber].UShortValue;
//// 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 DDVC devices
co.Devices.RemoveAll(d =>
d.Key.StartsWith("source-", StringComparison.OrdinalIgnoreCase)
|| d.Key.Equals("audioCodec", StringComparison.OrdinalIgnoreCase)
|| d.Key.Equals("videoCodec", StringComparison.OrdinalIgnoreCase));
rmProps.SourceListKey = "default";
rm.Properties = JToken.FromObject(rmProps);
// Source list! This might be brutal!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
var groupMap = GetSourceGroupDictionary();
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...
for (uint i = 0; i <= 19; i++)
{
var name = EISC.StringOutput[JoinMap.SourceNameJoinStart.JoinNumber + i].StringValue;
if (EISC.BooleanOutput[JoinMap.UseSourceEnabled.JoinNumber].BoolValue
&& !EISC.BooleanOutput[JoinMap.SourceIsEnabledJoinStart.JoinNumber + i].BoolValue)
{
continue;
}
else if(!EISC.BooleanOutput[JoinMap.UseSourceEnabled.JoinNumber].BoolValue && string.IsNullOrEmpty(name))
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;
Debug.Console(0, this, "Adding source {0} '{1}'", key, name);
var newSLI = new SourceListItem{
Icon = icon,
Name = name,
Order = (int)i + 10,
SourceKey = key,
Type = eSourceListItemType.Route,
DisableCodecSharing = disableShare,
};
newSl.Add(key, newSLI);
string 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
};
co.Devices.Add(devConf);
if (group.ToLower().StartsWith("settopbox")) // Add others here as needed
{
SetupSourceFunctions(key);
}
}
co.SourceLists.Add("default", newSl);
// 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 PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem()
{
Name = EISC.GetString(JoinMap.SpeedDialNameStartJoin.JoinNumber + i),
Number = EISC.GetString(JoinMap.SpeedDialNumberStartJoin.JoinNumber + i),
Type = PepperDash.Essentials.Devices.Common.Codec.eCodecCallType.Audio
});
}
var acProps = new
{
favorites = acFavs
};
var 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