feat(essentials): #865 Updates join map and bridge for new features

Adds control and feedback for presentation source
Updates camera setup, selection and feedback
Adds ringtone volume control/feedback
Adds call hold/resume/join control to bridge
Adds new config properties for camera info
This commit is contained in:
Neil Dorin
2021-11-11 21:05:57 -07:00
parent 8aae23db9e
commit 0ff29695e7
7 changed files with 490 additions and 118 deletions

View File

@@ -342,6 +342,48 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital JoinType = eJoinType.Digital
}); });
[JoinName("EndCallStart")]
public JoinDataComplete EndCallStart = new JoinDataComplete(
new JoinData
{
JoinNumber = 81,
JoinSpan = 8
},
new JoinMetadata
{
Description = "End a specific call by call index. ",
JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Digital
});
[JoinName("JoinAllCalls")]
public JoinDataComplete JoinAllCalls = new JoinDataComplete(
new JoinData
{
JoinNumber = 90,
JoinSpan = 8
},
new JoinMetadata
{
Description = "End a specific call by call index. ",
JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Digital
});
[JoinName("JoinCallStart")]
public JoinDataComplete JoinCallStart = new JoinDataComplete(
new JoinData
{
JoinNumber = 91,
JoinSpan = 8
},
new JoinMetadata
{
Description = "End a specific call by call index. ",
JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Digital
});
[JoinName("DirectorySearchBusy")] [JoinName("DirectorySearchBusy")]
public JoinDataComplete DirectorySearchBusy = new JoinDataComplete( public JoinDataComplete DirectorySearchBusy = new JoinDataComplete(
new JoinData new JoinData
@@ -931,6 +973,34 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Digital JoinType = eJoinType.Digital
}); });
[JoinName("HoldCallsStart")]
public JoinDataComplete HoldCallsStart = new JoinDataComplete(
new JoinData
{
JoinNumber = 221,
JoinSpan = 8
},
new JoinMetadata
{
Description = "Holds Call at specified index",
JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Digital
});
[JoinName("ResumeCallsStart")]
public JoinDataComplete ResumeCallsStart = new JoinDataComplete(
new JoinData
{
JoinNumber = 231,
JoinSpan = 8
},
new JoinMetadata
{
Description = "Resume Call at specified index",
JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Digital
});
[JoinName("ParticipantAudioMuteToggleStart")] [JoinName("ParticipantAudioMuteToggleStart")]
public JoinDataComplete ParticipantAudioMuteToggleStart = new JoinDataComplete( public JoinDataComplete ParticipantAudioMuteToggleStart = new JoinDataComplete(
new JoinData new JoinData
@@ -989,24 +1059,11 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
}, },
new JoinMetadata new JoinMetadata
{ {
Description = "Sets the selected Call. Valid values 1-8", Description = "Sets the selected Call for DTMF commands. Valid values 1-8",
JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Analog JoinType = eJoinType.Analog
}); });
[JoinName("EndCall")]
public JoinDataComplete EndCall = new JoinDataComplete(
new JoinData
{
JoinNumber = 24,
JoinSpan = 1
},
new JoinMetadata
{
Description = "End a specific call by call index. Valid values 1-8",
JoinCapabilities = eJoinCapabilities.FromSIMPL,
JoinType = eJoinType.Analog
});
[JoinName("ConnectedCallCount")] [JoinName("ConnectedCallCount")]
public JoinDataComplete ConnectedCallCount = new JoinDataComplete( public JoinDataComplete ConnectedCallCount = new JoinDataComplete(
@@ -1045,11 +1102,25 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
}, },
new JoinMetadata new JoinMetadata
{ {
Description = "Camera Number Select/FB", Description = "Camera Number Select/FB. 1 based index. Valid range is 1 to the value reported by CameraCount.",
JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
JoinType = eJoinType.Analog JoinType = eJoinType.Analog
}); });
[JoinName("CameraCount")]
public JoinDataComplete CameraCount = new JoinDataComplete(
new JoinData
{
JoinNumber = 61,
JoinSpan = 1
},
new JoinMetadata
{
Description = "Reports the number of cameras",
JoinCapabilities = eJoinCapabilities.ToSIMPL,
JoinType = eJoinType.Analog
});
[JoinName("DirectoryRowCount")] [JoinName("DirectoryRowCount")]
public JoinDataComplete DirectoryRowCount = new JoinDataComplete( public JoinDataComplete DirectoryRowCount = new JoinDataComplete(
new JoinData new JoinData
@@ -1323,6 +1394,20 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps
JoinType = eJoinType.Serial JoinType = eJoinType.Serial
}); });
[JoinName("CameraNamesFb")]
public JoinDataComplete CameraNamesFb = new JoinDataComplete(
new JoinData
{
JoinNumber = 161,
JoinSpan = 10
},
new JoinMetadata
{
Description = "Camera Name Fb",
JoinCapabilities = eJoinCapabilities.ToSIMPL,
JoinType = eJoinType.Serial
});
[JoinName("CurrentSource")] [JoinName("CurrentSource")]
public JoinDataComplete CurrentSource = new JoinDataComplete( public JoinDataComplete CurrentSource = new JoinDataComplete(
new JoinData new JoinData

View File

@@ -116,7 +116,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// <summary> /// <summary>
/// The ID of the camera on the codec /// The ID of the camera on the codec
/// </summary> /// </summary>
protected uint CameraId { get; private set; } public uint CameraId { get; private set; }
/// <summary> /// <summary>
/// Valid range 1-15 /// Valid range 1-15

View File

@@ -9,6 +9,34 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{ {
#region Digital #region Digital
[JoinName("PresentationLocalOnly")]
public JoinDataComplete PresentationLocalOnly = new JoinDataComplete(
new JoinData
{
JoinNumber = 205,
JoinSpan = 1
},
new JoinMetadata
{
Description = "Presentation Local Only Feedback",
JoinCapabilities = eJoinCapabilities.ToSIMPL,
JoinType = eJoinType.Digital
});
[JoinName("PresentationLocalRemote")]
public JoinDataComplete PresentationLocalRemote = new JoinDataComplete(
new JoinData
{
JoinNumber = 206,
JoinSpan = 1
},
new JoinMetadata
{
Description = "Presentation Local Only Feedback",
JoinCapabilities = eJoinCapabilities.ToSIMPL,
JoinType = eJoinType.Digital
});
[JoinName("ActivateDoNotDisturbMode")] [JoinName("ActivateDoNotDisturbMode")]
public JoinDataComplete ActivateDoNotDisturbMode = new JoinDataComplete( public JoinDataComplete ActivateDoNotDisturbMode = new JoinDataComplete(
new JoinData new JoinData
@@ -112,6 +140,34 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
#region Analog #region Analog
[JoinName("RingtoneVolume")]
public JoinDataComplete RingtoneVolume = new JoinDataComplete(
new JoinData
{
JoinNumber = 21,
JoinSpan = 1
},
new JoinMetadata
{
Description = "Ringtone volume set/FB. Valid values are 0 - 100 in increments of 5 (5, 10, 15, 20, etc.)",
JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
JoinType = eJoinType.Analog
});
[JoinName("PresentationSource")]
public JoinDataComplete PresentationSource = new JoinDataComplete(
new JoinData
{
JoinNumber = 201,
JoinSpan = 1
},
new JoinMetadata
{
Description = "Presentation set/FB. Valid values are 0 - 6 depending on the codec model.",
JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
JoinType = eJoinType.Analog
});
#endregion #endregion

View File

@@ -42,11 +42,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public StatusMonitorBase CommunicationMonitor { get; private set; } public StatusMonitorBase CommunicationMonitor { get; private set; }
private GenericQueue ReceiveQueue; private GenericQueue _receiveQueue;
public BoolFeedback PresentationViewMaximizedFeedback { get; private set; } public BoolFeedback PresentationViewMaximizedFeedback { get; private set; }
string CurrentPresentationView; private string _currentPresentationView;
public BoolFeedback RoomIsOccupiedFeedback { get; private set; } public BoolFeedback RoomIsOccupiedFeedback { get; private set; }
@@ -66,9 +66,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public IntFeedback RingtoneVolumeFeedback { get; private set; } public IntFeedback RingtoneVolumeFeedback { get; private set; }
private CodecCommandWithLabel CurrentSelfviewPipPosition; private CodecCommandWithLabel _currentSelfviewPipPosition;
private CodecCommandWithLabel CurrentLocalLayout; private CodecCommandWithLabel _currentLocalLayout;
/// <summary> /// <summary>
/// List the available positions for the selfview PIP window /// List the available positions for the selfview PIP window
@@ -167,7 +167,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{ {
get get
{ {
return () => PresentationSourceKey; return () => _presentationSourceKey;
} }
} }
@@ -231,7 +231,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{ {
get get
{ {
return () => CurrentSelfviewPipPosition.Label; return () => _currentSelfviewPipPosition.Label;
} }
} }
@@ -239,7 +239,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{ {
get get
{ {
return () => CurrentLocalLayout.Label; return () => _currentLocalLayout.Label;
} }
} }
@@ -247,43 +247,58 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{ {
get get
{ {
return () => CurrentLocalLayout.Label == "Prominent"; return () => _currentLocalLayout.Label == "Prominent";
} }
} }
private string CliFeedbackRegistrationExpression; private string _cliFeedbackRegistrationExpression;
private CodecSyncState SyncState; private CodecSyncState _syncState;
public CodecPhonebookSyncState PhonebookSyncState { get; private set; } public CodecPhonebookSyncState PhonebookSyncState { get; private set; }
private StringBuilder JsonMessage; private StringBuilder _jsonMessage;
private bool JsonFeedbackMessageIsIncoming; private bool _jsonFeedbackMessageIsIncoming;
public bool CommDebuggingIsOn; public bool CommDebuggingIsOn;
string Delimiter = "\r\n"; string Delimiter = "\r\n";
public IntFeedback PresentationSourceFeedback { get; private set; }
public BoolFeedback PresentationSendingLocalOnlyFeedback { get; private set; }
public BoolFeedback PresentationSendingLocalRemoteFeedback { get; private set; }
/// <summary> /// <summary>
/// Used to track the current connector used for the presentation source /// Used to track the current connector used for the presentation source
/// </summary> /// </summary>
int PresentationSource; private int _presentationSource;
string PresentationSourceKey; /// <summary>
/// Used to track the connector that is desired to be the current presentation source (until the command is send)
/// </summary>
private int _desiredPresentationSource;
string PhonebookMode = "Local"; // Default to Local private string _presentationSourceKey;
uint PhonebookResultsLimit = 255; // Could be set later by config. private bool _presentationLocalOnly;
CTimer LoginMessageReceivedTimer; private bool _presentationLocalRemote;
CTimer RetryConnectionTimer;
private string _phonebookMode = "Local"; // Default to Local
private uint _phonebookResultsLimit = 255; // Could be set later by config.
private CTimer _loginMessageReceivedTimer;
private CTimer _retryConnectionTimer;
// **___________________________________________________________________** // **___________________________________________________________________**
// Timers to be moved to the global system timer at a later point.... // Timers to be moved to the global system timer at a later point....
CTimer BookingsRefreshTimer; private CTimer BookingsRefreshTimer;
CTimer PhonebookRefreshTimer; private CTimer PhonebookRefreshTimer;
// **___________________________________________________________________** // **___________________________________________________________________**
public RoutingInputPort CodecOsdIn { get; private set; } public RoutingInputPort CodecOsdIn { get; private set; }
@@ -302,11 +317,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
// Use the configured phonebook results limit if present // Use the configured phonebook results limit if present
if (props.PhonebookResultsLimit > 0) if (props.PhonebookResultsLimit > 0)
{ {
PhonebookResultsLimit = props.PhonebookResultsLimit; _phonebookResultsLimit = props.PhonebookResultsLimit;
} }
// The queue that will collect the repsonses in the order they are received // The queue that will collect the repsonses in the order they are received
ReceiveQueue = new GenericQueue(this.Key + "-rxQueue", 25); _receiveQueue = new GenericQueue(this.Key + "-rxQueue", 25);
RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc); RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc);
PeopleCountFeedback = new IntFeedback(PeopleCountFeedbackFunc); PeopleCountFeedback = new IntFeedback(PeopleCountFeedbackFunc);
@@ -324,10 +339,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
HalfWakeModeIsOnFeedback = new BoolFeedback(() => CodecStatus.Status.Standby.State.Value.ToLower() == "halfwake"); HalfWakeModeIsOnFeedback = new BoolFeedback(() => CodecStatus.Status.Standby.State.Value.ToLower() == "halfwake");
EnteringStandbyModeFeedback = new BoolFeedback(() => CodecStatus.Status.Standby.State.Value.ToLower() == "enteringstandby"); EnteringStandbyModeFeedback = new BoolFeedback(() => CodecStatus.Status.Standby.State.Value.ToLower() == "enteringstandby");
PresentationViewMaximizedFeedback = new BoolFeedback(() => CurrentPresentationView == "Maximized"); PresentationViewMaximizedFeedback = new BoolFeedback(() => _currentPresentationView == "Maximized");
RingtoneVolumeFeedback = new IntFeedback(() => CodecConfiguration.Configuration.Audio.SoundsAndAlerts.RingVolume.Volume); RingtoneVolumeFeedback = new IntFeedback(() => CodecConfiguration.Configuration.Audio.SoundsAndAlerts.RingVolume.Volume);
PresentationSourceFeedback = new IntFeedback(() => _presentationSource);
PresentationSendingLocalOnlyFeedback = new BoolFeedback(() => _presentationLocalOnly);
PresentationSendingLocalRemoteFeedback = new BoolFeedback(() => _presentationLocalRemote);
Communication = comm; Communication = comm;
if (props.CommunicationMonitorProperties != null) if (props.CommunicationMonitorProperties != null)
@@ -346,13 +365,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
DeviceManager.AddDevice(CommunicationMonitor); DeviceManager.AddDevice(CommunicationMonitor);
PhonebookMode = props.PhonebookMode; _phonebookMode = props.PhonebookMode;
SyncState = new CodecSyncState(Key + "--Sync"); _syncState = new CodecSyncState(Key + "--Sync");
PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync"); PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync");
SyncState.InitialSyncCompleted += new EventHandler<EventArgs>(SyncState_InitialSyncCompleted); _syncState.InitialSyncCompleted += new EventHandler<EventArgs>(SyncState_InitialSyncCompleted);
PortGather = new CommunicationGather(Communication, Delimiter); PortGather = new CommunicationGather(Communication, Delimiter);
PortGather.IncludeDelimiter = true; PortGather.IncludeDelimiter = true;
@@ -399,7 +418,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
InputPorts.Add(HdmiIn3); InputPorts.Add(HdmiIn3);
OutputPorts.Add(HdmiOut1); OutputPorts.Add(HdmiOut1);
SetUpCameras(); SetUpCameras(props.CameraInfo);
CreateOsdSource(); CreateOsdSource();
@@ -589,7 +608,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
const string prefix = "xFeedback register "; const string prefix = "xFeedback register ";
CliFeedbackRegistrationExpression = _cliFeedbackRegistrationExpression =
prefix + "/Configuration" + Delimiter + prefix + "/Configuration" + Delimiter +
prefix + "/Status/Audio" + Delimiter + prefix + "/Status/Audio" + Delimiter +
prefix + "/Status/Call" + Delimiter + prefix + "/Status/Call" + Delimiter +
@@ -651,12 +670,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
Debug.Console(1, this, "Socket status change {0}", e.Client.ClientStatus); Debug.Console(1, this, "Socket status change {0}", e.Client.ClientStatus);
if (e.Client.IsConnected) if (e.Client.IsConnected)
{ {
if(!SyncState.LoginMessageWasReceived) if(!_syncState.LoginMessageWasReceived)
LoginMessageReceivedTimer = new CTimer(o => DisconnectClientAndReconnect(), 5000); _loginMessageReceivedTimer = new CTimer(o => DisconnectClientAndReconnect(), 5000);
} }
else else
{ {
SyncState.CodecDisconnected(); _syncState.CodecDisconnected();
PhonebookSyncState.CodecDisconnected(); PhonebookSyncState.CodecDisconnected();
if (PhonebookRefreshTimer != null) if (PhonebookRefreshTimer != null)
@@ -679,7 +698,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
Communication.Disconnect(); Communication.Disconnect();
RetryConnectionTimer = new CTimer(o => Communication.Connect(), 2000); _retryConnectionTimer = new CTimer(o => Communication.Connect(), 2000);
//CrestronEnvironment.Sleep(2000); //CrestronEnvironment.Sleep(2000);
@@ -696,66 +715,66 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{ {
if (CommDebuggingIsOn) if (CommDebuggingIsOn)
{ {
if (!JsonFeedbackMessageIsIncoming) if (!_jsonFeedbackMessageIsIncoming)
Debug.Console(1, this, "RX: '{0}'", args.Text); Debug.Console(1, this, "RX: '{0}'", args.Text);
} }
if (args.Text == "{" + Delimiter) // Check for the beginning of a new JSON message if (args.Text == "{" + Delimiter) // Check for the beginning of a new JSON message
{ {
JsonFeedbackMessageIsIncoming = true; _jsonFeedbackMessageIsIncoming = true;
if (CommDebuggingIsOn) if (CommDebuggingIsOn)
Debug.Console(1, this, "Incoming JSON message..."); Debug.Console(1, this, "Incoming JSON message...");
JsonMessage = new StringBuilder(); _jsonMessage = new StringBuilder();
} }
else if (args.Text == "}" + Delimiter) // Check for the end of a JSON message else if (args.Text == "}" + Delimiter) // Check for the end of a JSON message
{ {
JsonFeedbackMessageIsIncoming = false; _jsonFeedbackMessageIsIncoming = false;
JsonMessage.Append(args.Text); _jsonMessage.Append(args.Text);
if (CommDebuggingIsOn) if (CommDebuggingIsOn)
Debug.Console(1, this, "Complete JSON Received:\n{0}", JsonMessage.ToString()); Debug.Console(1, this, "Complete JSON Received:\n{0}", _jsonMessage.ToString());
// Enqueue the complete message to be deserialized // Enqueue the complete message to be deserialized
ReceiveQueue.Enqueue(new ProcessStringMessage(JsonMessage.ToString(), DeserializeResponse)); _receiveQueue.Enqueue(new ProcessStringMessage(_jsonMessage.ToString(), DeserializeResponse));
return; return;
} }
if(JsonFeedbackMessageIsIncoming) if(_jsonFeedbackMessageIsIncoming)
{ {
JsonMessage.Append(args.Text); _jsonMessage.Append(args.Text);
//Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString()); //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString());
return; return;
} }
if (!SyncState.InitialSyncComplete) if (!_syncState.InitialSyncComplete)
{ {
switch (args.Text.Trim().ToLower()) // remove the whitespace switch (args.Text.Trim().ToLower()) // remove the whitespace
{ {
case "*r login successful": case "*r login successful":
{ {
SyncState.LoginMessageReceived(); _syncState.LoginMessageReceived();
if(LoginMessageReceivedTimer != null) if(_loginMessageReceivedTimer != null)
LoginMessageReceivedTimer.Stop(); _loginMessageReceivedTimer.Stop();
SendText("xPreferences outputmode json"); SendText("xPreferences outputmode json");
break; break;
} }
case "xpreferences outputmode json": case "xpreferences outputmode json":
{ {
if (!SyncState.InitialStatusMessageWasReceived) if (!_syncState.InitialStatusMessageWasReceived)
SendText("xStatus"); SendText("xStatus");
break; break;
} }
case "xfeedback register /event/calldisconnect": case "xfeedback register /event/calldisconnect":
{ {
SyncState.FeedbackRegistered(); _syncState.FeedbackRegistered();
break; break;
} }
} }
@@ -801,11 +820,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
if (conference.Presentation.LocalInstance.Count > 0) if (conference.Presentation.LocalInstance.Count > 0)
{ {
if (!string.IsNullOrEmpty(conference.Presentation.LocalInstance[0].ghost)) if (!string.IsNullOrEmpty(conference.Presentation.LocalInstance[0].ghost))
PresentationSource = 0; _presentationSource = 0;
else if (conference.Presentation.LocalInstance[0].Source != null) else if (conference.Presentation.LocalInstance[0].Source != null)
{ {
PresentationSource = conference.Presentation.LocalInstance[0].Source.IntValue; _presentationSource = conference.Presentation.LocalInstance[0].Source.IntValue;
} }
_presentationLocalOnly = conference.Presentation.LocalInstance.Any((i) => i.SendingMode.LocalOnly);
_presentationLocalRemote = conference.Presentation.LocalInstance.Any((i) => i.SendingMode.LocalRemote);
PresentationSourceFeedback.FireUpdate();
PresentationSendingLocalOnlyFeedback.FireUpdate();
PresentationSendingLocalRemoteFeedback.FireUpdate();
} }
// Check to see if this is a call status message received after the initial status message // Check to see if this is a call status message received after the initial status message
@@ -966,11 +992,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
JsonConvert.PopulateObject(response, CodecStatus); JsonConvert.PopulateObject(response, CodecStatus);
} }
if (!SyncState.InitialStatusMessageWasReceived) if (!_syncState.InitialStatusMessageWasReceived)
{ {
SyncState.InitialStatusMessageReceived(); _syncState.InitialStatusMessageReceived();
if (!SyncState.InitialConfigurationMessageWasReceived) if (!_syncState.InitialConfigurationMessageWasReceived)
SendText("xConfiguration"); SendText("xConfiguration");
} }
} }
@@ -980,12 +1006,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
JsonConvert.PopulateObject(response, CodecConfiguration); JsonConvert.PopulateObject(response, CodecConfiguration);
if (!SyncState.InitialConfigurationMessageWasReceived) if (!_syncState.InitialConfigurationMessageWasReceived)
{ {
SyncState.InitialConfigurationMessageReceived(); _syncState.InitialConfigurationMessageReceived();
if (!SyncState.FeedbackWasRegistered) if (!_syncState.FeedbackWasRegistered)
{ {
SendText(CliFeedbackRegistrationExpression); SendText(_cliFeedbackRegistrationExpression);
} }
} }
@@ -1158,7 +1184,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public override void ExecuteSwitch(object selector) public override void ExecuteSwitch(object selector)
{ {
(selector as Action)(); (selector as Action)();
PresentationSourceKey = selector.ToString(); _presentationSourceKey = selector.ToString();
} }
/// <summary> /// <summary>
@@ -1168,7 +1194,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType)
{ {
ExecuteSwitch(inputSelector); ExecuteSwitch(inputSelector);
PresentationSourceKey = inputSelector.ToString(); _presentationSourceKey = inputSelector.ToString();
} }
@@ -1247,13 +1273,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
private void GetPhonebookFolders() private void GetPhonebookFolders()
{ {
// Get Phonebook Folders (determine local/corporate from config, and set results limit) // Get Phonebook Folders (determine local/corporate from config, and set results limit)
SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Folder", PhonebookMode)); SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Folder", _phonebookMode));
} }
private void GetPhonebookContacts() private void GetPhonebookContacts()
{ {
// Get Phonebook Folders (determine local/corporate from config, and set results limit) // Get Phonebook Folders (determine local/corporate from config, and set results limit)
SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Contact Limit: {1}", PhonebookMode, PhonebookResultsLimit)); SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Contact Limit: {1}", _phonebookMode, _phonebookResultsLimit));
} }
/// <summary> /// <summary>
@@ -1262,7 +1288,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// <param name="searchString"></param> /// <param name="searchString"></param>
public void SearchDirectory(string searchString) public void SearchDirectory(string searchString)
{ {
SendText(string.Format("xCommand Phonebook Search SearchString: \"{0}\" PhonebookType: {1} ContactType: Contact Limit: {2}", searchString, PhonebookMode, PhonebookResultsLimit)); SendText(string.Format("xCommand Phonebook Search SearchString: \"{0}\" PhonebookType: {1} ContactType: Contact Limit: {2}", searchString, _phonebookMode, _phonebookResultsLimit));
} }
/// <summary> /// <summary>
@@ -1271,7 +1297,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// <param name="folderId"></param> /// <param name="folderId"></param>
public void GetDirectoryFolderContents(string folderId) public void GetDirectoryFolderContents(string folderId)
{ {
SendText(string.Format("xCommand Phonebook Search FolderId: {0} PhonebookType: {1} ContactType: Any Limit: {2}", folderId, PhonebookMode, PhonebookResultsLimit)); SendText(string.Format("xCommand Phonebook Search FolderId: {0} PhonebookType: {1} ContactType: Any Limit: {2}", folderId, _phonebookMode, _phonebookResultsLimit));
} }
/// <summary> /// <summary>
@@ -1449,14 +1475,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// </summary> /// </summary>
/// <param name="s"></param> /// <param name="s"></param>
/// <param name="activeCall"></param> /// <param name="activeCall"></param>
public void SendDtmf(string s, CodecActiveCallItem activeCall) public override void SendDtmf(string s, CodecActiveCallItem activeCall)
{ {
SendText(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", activeCall.Id, s)); SendText(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", activeCall.Id, s));
} }
public void SelectPresentationSource(int source) public void SelectPresentationSource(int source)
{ {
PresentationSource = source; _desiredPresentationSource = source;
StartSharing(); StartSharing();
} }
@@ -1467,6 +1493,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// <param name="volume">level from 0 - 100 in increments of 5</param> /// <param name="volume">level from 0 - 100 in increments of 5</param>
public void SetRingtoneVolume(int volume) public void SetRingtoneVolume(int volume)
{ {
if (volume < 0 || volume > 100)
{
Debug.Console(0, this, "Cannot set ringtone volume to '{0}'. Value must be between 0 - 100", volume);
return;
}
if (volume % 5 != 0)
{
Debug.Console(0, this, "Cannot set ringtone volume to '{0}'. Value must be between 0 - 100 and a multiple of 5", volume);
return;
}
SendText(string.Format("xConfiguration Audio SoundsAndAlerts RingVolume: [0]", volume)); SendText(string.Format("xConfiguration Audio SoundsAndAlerts RingVolume: [0]", volume));
} }
@@ -1500,8 +1538,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
else else
sendingMode = "LocalOnly"; sendingMode = "LocalOnly";
if(PresentationSource > 0) if (_desiredPresentationSource > 0)
SendText(string.Format("xCommand Presentation Start PresentationSource: {0} SendingMode: {1}", PresentationSource, sendingMode)); SendText(string.Format("xCommand Presentation Start PresentationSource: {0} SendingMode: {1}", _desiredPresentationSource, sendingMode));
} }
/// <summary> /// <summary>
@@ -1509,7 +1547,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// </summary> /// </summary>
public override void StopSharing() public override void StopSharing()
{ {
PresentationSource = 0; _desiredPresentationSource = 0;
SendText("xCommand Presentation Stop"); SendText("xCommand Presentation Stop");
} }
@@ -1645,7 +1683,16 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
trilist.SetSigFalseAction(joinMap.ActivateHalfWakeMode.JoinNumber, () => halfwakeCodec.HalfwakeActivate()); trilist.SetSigFalseAction(joinMap.ActivateHalfWakeMode.JoinNumber, () => halfwakeCodec.HalfwakeActivate());
} }
// TODO: Add mechanism to select a call instance to be able to direct DTMF tones to... // Ringtone volume
trilist.SetUShortSigAction(joinMap.RingtoneVolume.JoinNumber, (u) => SetRingtoneVolume(u));
RingtoneVolumeFeedback.LinkInputSig(trilist.UShortInput[joinMap.RingtoneVolume.JoinNumber]);
// Presentation Source
trilist.SetUShortSigAction(joinMap.PresentationSource.JoinNumber, (u) => SelectPresentationSource(u));
PresentationSourceFeedback.LinkInputSig(trilist.UShortInput[joinMap.PresentationSource.JoinNumber]);
PresentationSendingLocalOnlyFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PresentationLocalOnly.JoinNumber]);
PresentationSendingLocalRemoteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PresentationLocalRemote.JoinNumber]);
} }
/// <summary> /// <summary>
@@ -1719,9 +1766,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// </summary> /// </summary>
public void SelfviewPipPositionToggle() public void SelfviewPipPositionToggle()
{ {
if (CurrentSelfviewPipPosition != null) if (_currentSelfviewPipPosition != null)
{ {
var nextPipPositionIndex = SelfviewPipPositions.IndexOf(CurrentSelfviewPipPosition) + 1; var nextPipPositionIndex = SelfviewPipPositions.IndexOf(_currentSelfviewPipPosition) + 1;
if (nextPipPositionIndex >= SelfviewPipPositions.Count) // Check if we need to loop back to the first item in the list if (nextPipPositionIndex >= SelfviewPipPositions.Count) // Check if we need to loop back to the first item in the list
nextPipPositionIndex = 0; nextPipPositionIndex = 0;
@@ -1744,9 +1791,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// </summary> /// </summary>
public void LocalLayoutToggle() public void LocalLayoutToggle()
{ {
if(CurrentLocalLayout != null) if(_currentLocalLayout != null)
{ {
var nextLocalLayoutIndex = LocalLayouts.IndexOf(CurrentLocalLayout) + 1; var nextLocalLayoutIndex = LocalLayouts.IndexOf(_currentLocalLayout) + 1;
if (nextLocalLayoutIndex >= LocalLayouts.Count) // Check if we need to loop back to the first item in the list if (nextLocalLayoutIndex >= LocalLayouts.Count) // Check if we need to loop back to the first item in the list
nextLocalLayoutIndex = 0; nextLocalLayoutIndex = 0;
@@ -1760,9 +1807,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// </summary> /// </summary>
public void LocalLayoutToggleSingleProminent() public void LocalLayoutToggleSingleProminent()
{ {
if (CurrentLocalLayout != null) if (_currentLocalLayout != null)
{ {
if (CurrentLocalLayout.Label != "Prominent") if (_currentLocalLayout.Label != "Prominent")
LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Prominent"))); LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Prominent")));
else else
LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Single"))); LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Single")));
@@ -1776,11 +1823,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public void MinMaxLayoutToggle() public void MinMaxLayoutToggle()
{ {
if (PresentationViewMaximizedFeedback.BoolValue) if (PresentationViewMaximizedFeedback.BoolValue)
CurrentPresentationView = "Minimized"; _currentPresentationView = "Minimized";
else else
CurrentPresentationView = "Maximized"; _currentPresentationView = "Maximized";
SendText(string.Format("xCommand Video PresentationView Set View: {0}", CurrentPresentationView)); SendText(string.Format("xCommand Video PresentationView Set View: {0}", _currentPresentationView));
PresentationViewMaximizedFeedback.FireUpdate(); PresentationViewMaximizedFeedback.FireUpdate();
} }
@@ -1789,9 +1836,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// </summary> /// </summary>
void ComputeSelfviewPipStatus() void ComputeSelfviewPipStatus()
{ {
CurrentSelfviewPipPosition = SelfviewPipPositions.FirstOrDefault(p => p.Command.ToLower().Equals(CodecStatus.Status.Video.Selfview.PIPPosition.Value.ToLower())); _currentSelfviewPipPosition = SelfviewPipPositions.FirstOrDefault(p => p.Command.ToLower().Equals(CodecStatus.Status.Video.Selfview.PIPPosition.Value.ToLower()));
if(CurrentSelfviewPipPosition != null) if(_currentSelfviewPipPosition != null)
SelfviewIsOnFeedback.FireUpdate(); SelfviewIsOnFeedback.FireUpdate();
} }
@@ -1800,9 +1847,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// </summary> /// </summary>
void ComputeLocalLayout() void ComputeLocalLayout()
{ {
CurrentLocalLayout = LocalLayouts.FirstOrDefault(l => l.Command.ToLower().Equals(CodecStatus.Status.Video.Layout.LayoutFamily.Local.Value.ToLower())); _currentLocalLayout = LocalLayouts.FirstOrDefault(l => l.Command.ToLower().Equals(CodecStatus.Status.Video.Layout.LayoutFamily.Local.Value.ToLower()));
if (CurrentLocalLayout != null) if (_currentLocalLayout != null)
LocalLayoutFeedback.FireUpdate(); LocalLayoutFeedback.FireUpdate();
} }
@@ -1850,19 +1897,59 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// <summary> /// <summary>
/// Builds the cameras List. Could later be modified to build from config data /// Builds the cameras List. Could later be modified to build from config data
/// </summary> /// </summary>
void SetUpCameras() void SetUpCameras(List<CameraInfo> cameraInfo)
{ {
// Add the internal camera // Add the internal camera
Cameras = new List<CameraBase>(); Cameras = new List<CameraBase>();
var internalCamera = new CiscoSparkCamera(Key + "-camera1", "Near End", this, 1); var camCount = CodecStatus.Status.Cameras.Camera.Count;
if(CodecStatus.Status.Cameras.Camera.Count > 0) Debug.Console(0, this, "Codec reports {0} cameras", camCount);
internalCamera.SetCapabilites(CodecStatus.Status.Cameras.Camera[0].Capabilities.Options.Value);
// Deal with the case of 1 or no reported cameras
if (camCount <= 1)
{
var internalCamera = new CiscoSparkCamera(Key + "-camera1", "Near End", this, 1);
if (CodecStatus.Status.Cameras.Camera[0] != null && CodecStatus.Status.Cameras.Camera[0].Capabilities != null)
{
internalCamera.SetCapabilites(CodecStatus.Status.Cameras.Camera[0].Capabilities.Options.Value);
}
Cameras.Add(internalCamera);
DeviceManager.AddDevice(internalCamera);
}
else else
// Somehow subscribe to the event on the Options.Value property and update when it changes. {
// Setup all the cameras
for (int i = 0; i < camCount; i++)
{
var cam = CodecStatus.Status.Cameras.Camera[i];
Cameras.Add(internalCamera); var id = (uint)i;
var name = string.Format("Camera {0}", id);
// Check for a config object that matches the camera number
var camInfo = cameraInfo.FirstOrDefault(c => c.CameraNumber == i + 1);
if (camInfo != null)
{
id = (uint)camInfo.SourceId;
name = camInfo.Name;
}
var key = string.Format("{0}-camera{1}", Key, id);
var camera = new CiscoSparkCamera(key, name, this, id);
if (cam.Capabilities != null)
{
camera.SetCapabilites(cam.Capabilities.Options.Value);
}
Cameras.Add(camera);
DeviceManager.AddDevice(camera);
}
}
// Add the far end camera // Add the far end camera
var farEndCamera = new CiscoFarEndCamera(Key + "-cameraFar", "Far End", this); var farEndCamera = new CiscoFarEndCamera(Key + "-cameraFar", "Far End", this);
@@ -1872,7 +1959,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
ControllingFarEndCameraFeedback = new BoolFeedback(() => SelectedCamera is IAmFarEndCamera); ControllingFarEndCameraFeedback = new BoolFeedback(() => SelectedCamera is IAmFarEndCamera);
DeviceManager.AddDevice(internalCamera);
DeviceManager.AddDevice(farEndCamera); DeviceManager.AddDevice(farEndCamera);
NearEndPresets = new List<CodecRoomPreset>(15); NearEndPresets = new List<CodecRoomPreset>(15);
@@ -1886,7 +1972,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
FarEndRoomPresets.Add(new CodecRoomPreset(i, label, true, false)); FarEndRoomPresets.Add(new CodecRoomPreset(i, label, true, false));
} }
SelectedCamera = internalCamera; ; // call the method to select the camera and ensure the feedbacks get updated. SelectedCamera = Cameras[0]; ; // call the method to select the camera and ensure the feedbacks get updated.
} }
#region IHasCodecCameras Members #region IHasCodecCameras Members
@@ -1934,6 +2020,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
} }
else else
Debug.Console(2, this, "Unable to select camera with key: '{0}'", key); Debug.Console(2, this, "Unable to select camera with key: '{0}'", key);
var ciscoCam = camera as CiscoSparkCamera;
if (ciscoCam != null)
{
SendText(string.Format("xCommand Video Input SetMainVideoSource SourceId: {0}", ciscoCam.CameraId));
}
} }
public CameraBase FarEndCamera { get; private set; } public CameraBase FarEndCamera { get; private set; }

View File

@@ -50,8 +50,16 @@ namespace PepperDash.Essentials.Devices.Common.Codec
public uint PhonebookResultsLimit { get; set; } public uint PhonebookResultsLimit { get; set; }
[JsonProperty("UiBranding")] [JsonProperty("UiBranding")]
public BrandingLogoProperties UiBranding { get; set; } public BrandingLogoProperties UiBranding { get; set; }
[JsonProperty("cameraInfo")]
public List<CameraInfo> CameraInfo { get; set; }
public CiscoSparkCodecPropertiesConfig()
{
CameraInfo = new List<CameraInfo>();
}
} }
public class SharingProperties public class SharingProperties
@@ -68,4 +76,14 @@ namespace PepperDash.Essentials.Devices.Common.Codec
[JsonProperty("brandingUrl")] [JsonProperty("brandingUrl")]
public string BrandingUrl { get; set; } public string BrandingUrl { get; set; }
} }
/// <summary>
/// Describes configuration information for the near end cameras
/// </summary>
public class CameraInfo
{
public int CameraNumber { get; set; }
public string Name { get; set; }
public int SourceId { get; set; }
}
} }

View File

@@ -558,9 +558,41 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
} }
} }
public class SendingMode public class SendingMode : ValueProperty
{ {
public string Value { get; set; } string _Value;
/// <summary>
/// Sets Value and triggers the action when set
/// </summary>
public string Value
{
get
{
return _Value;
}
set
{
_Value = value;
OnValueChanged();
}
}
public bool LocalOnly
{
get
{
return _Value.ToLower() == "localonly";
}
}
public bool LocalRemote
{
get
{
return _Value.ToLower() == "localremote";
}
}
} }
public class LocalInstance public class LocalInstance
@@ -573,6 +605,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public LocalInstance() public LocalInstance()
{ {
Source = new Source2(); Source = new Source2();
SendingMode = new SendingMode();
} }
} }

View File

@@ -982,18 +982,22 @@ ScreenIndexIsPinnedTo: {8} (a{17})
//End All calls //End All calls
trilist.SetSigFalseAction(joinMap.EndAllCalls.JoinNumber, EndAllCalls); trilist.SetSigFalseAction(joinMap.EndAllCalls.JoinNumber, EndAllCalls);
//End a specific call, specified by index //End a specific call, specified by index. Maximum 8 calls supported
trilist.SetUShortSigAction(joinMap.EndCall.JoinNumber, (i) => for (int i = 0; i < joinMap.EndCallStart.JoinSpan; i++)
{ {
if (i > 0 && i <= 8) trilist.SetSigFalseAction((uint)(joinMap.EndCallStart.JoinNumber + i), () =>
{ {
var call = ActiveCalls[i - 1]; var call = ActiveCalls[i];
if (call != null) if (call != null)
{ {
EndCall(call); EndCall(call);
} }
} else
}); {
Debug.Console(0, this, "[End Call] Unable to find call at index '{0}'", i);
}
});
}
trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall);
@@ -1015,6 +1019,61 @@ ScreenIndexIsPinnedTo: {8} (a{17})
trilist.SetUshort(joinMap.ConnectedCallCount.JoinNumber, (ushort)ActiveCalls.Count); trilist.SetUshort(joinMap.ConnectedCallCount.JoinNumber, (ushort)ActiveCalls.Count);
}; };
var joinCodec = this as IJoinCalls;
if (joinCodec != null)
{
trilist.SetSigFalseAction(joinMap.JoinAllCalls.JoinNumber, () => joinCodec.JoinAllCalls());
for (int i = 0; i < joinMap.JoinCallStart.JoinSpan; i++)
{
trilist.SetSigFalseAction((uint)(joinMap.JoinCallStart.JoinNumber + i), () =>
{
var call = ActiveCalls[i];
if (call != null)
{
joinCodec.JoinCall(call);
}
else
{
Debug.Console(0, this, "[Join Call] Unable to find call at index '{0}'", i);
}
});
}
}
var holdCodec = this as IHasCallHold;
if (holdCodec != null)
{
for (int i = 0; i < joinMap.JoinCallStart.JoinSpan; i++)
{
trilist.SetSigFalseAction((uint)(joinMap.HoldCallsStart.JoinNumber + i), () =>
{
var call = ActiveCalls[i];
if (call != null)
{
holdCodec.HoldCall(call);
}
else
{
Debug.Console(0, this, "[Hold Call] Unable to find call at index '{0}'", i);
}
});
trilist.SetSigFalseAction((uint)(joinMap.ResumeCallsStart.JoinNumber + i), () =>
{
var call = ActiveCalls[i];
if (call != null)
{
holdCodec.ResumeCall(call);
}
else
{
Debug.Console(0, this, "[Resume Call] Unable to find call at index '{0}'", i);
}
});
}
}
} }
private string UpdateCallStatusXSig() private string UpdateCallStatusXSig()
@@ -1296,18 +1355,47 @@ ScreenIndexIsPinnedTo: {8} (a{17})
camera.TriggerAutoFocus(); camera.TriggerAutoFocus();
}); });
// Camera count
trilist.SetUshort(joinMap.CameraCount.JoinNumber, (ushort)codec.Cameras.Count);
// Camera names
for (uint i = 0; i < joinMap.CameraNamesFb.JoinSpan; i++)
{
if (codec.Cameras[(int)i] != null)
{
trilist.SetString(joinMap.CameraNamesFb.JoinNumber + i, codec.Cameras[(int)i].Name);
}
else
{
trilist.SetString(joinMap.CameraNamesFb.JoinNumber + i, "");
}
}
//Camera Select //Camera Select
trilist.SetUShortSigAction(joinMap.CameraNumberSelect.JoinNumber, (i) => trilist.SetUShortSigAction(joinMap.CameraNumberSelect.JoinNumber, (i) =>
{ {
if (codec.SelectedCamera == null) return; if (i > 0 && i <= codec.Cameras.Count)
{
codec.SelectCamera(codec.Cameras[i].Key); codec.SelectCamera(codec.Cameras[i - 1].Key);
}
else
{
Debug.Console(0, this, "Unable to select. No camera found at index {0}", i);
}
}); });
// Set initial selected camera feedback
if (codec.SelectedCamera != null)
{
trilist.SetUshort(joinMap.CameraNumberSelect.JoinNumber, (ushort)codec.Cameras.FindIndex((c) => c.Key == codec.SelectedCamera.Key));
}
codec.CameraSelected += (sender, args) => codec.CameraSelected += (sender, args) =>
{ {
var i = (ushort)codec.Cameras.FindIndex((c) => c.Key == args.SelectedCamera.Key); var i = (ushort)codec.Cameras.FindIndex((c) => c.Key == args.SelectedCamera.Key);
trilist.SetUshort(joinMap.CameraNumberSelect.JoinNumber, (ushort)(i + 1));
if (codec is IHasCodecRoomPresets) if (codec is IHasCodecRoomPresets)
{ {
return; return;