Merge branch 'main' into feature/allow-config-tool-v2-structure

This commit is contained in:
Neil Dorin
2025-10-22 13:48:22 -06:00
234 changed files with 9834 additions and 6493 deletions

View File

@@ -23,23 +23,32 @@
<FileName>$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz</FileName> <FileName>$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz</FileName>
</PropertyGroup> </PropertyGroup>
<Target Name="DeleteCLZ" BeforeTargets="PreBuildEvent" Condition="$(ProjectType) == 'Library' And $(TargetDir) != '' And Exists($(FileName))"> <Target Name="DeleteCLZ" BeforeTargets="CoreBuild" Condition="$(ProjectType) == 'Library' And $(TargetDir) != ''">
<Delete Files="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).clz"> <ItemGroup>
<OldCLZFiles Include="$(TargetDir)$(TargetName).*.$(TargetFramework).clz" />
</ItemGroup>
<Delete Files="@(OldCLZFiles)" Condition="@(OldCLZFiles) != ''">
<Output TaskParameter="DeletedFiles" ItemName="DeletedList"/> <Output TaskParameter="DeletedFiles" ItemName="DeletedList"/>
</Delete> </Delete>
<Message Text="Deleted files: '@(DeletedList)'" /> <Message Text="Deleted old CLZ files: '@(DeletedList)'" Condition="@(DeletedList) != ''" />
</Target> </Target>
<Target Name="DeleteCPZ" BeforeTargets="PreBuildEvent" Condition="$(ProjectType) == 'Program' And $(TargetDir) != '' And Exists($(FileName))"> <Target Name="DeleteCPZ" BeforeTargets="CoreBuild" Condition="$(ProjectType) == 'Program' And $(TargetDir) != ''">
<Delete Files="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz"> <ItemGroup>
<OldCPZFiles Include="$(TargetDir)$(TargetName).*.$(TargetFramework).cpz" />
</ItemGroup>
<Delete Files="@(OldCPZFiles)" Condition="@(OldCPZFiles) != ''">
<Output TaskParameter="DeletedFiles" ItemName="DeletedList"/> <Output TaskParameter="DeletedFiles" ItemName="DeletedList"/>
</Delete> </Delete>
<Message Text="Deleted files: '@(DeletedList)'" /> <Message Text="Deleted old CPZ files: '@(DeletedList)'" Condition="@(DeletedList) != ''" />
</Target> </Target>
<Target Name="DeleteCPLZ" BeforeTargets="PreBuildEvent" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != '' And Exists($(FileName))"> <Target Name="DeleteCPLZ" BeforeTargets="CoreBuild" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''">
<Delete Files="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz"> <ItemGroup>
<OldCPLZFiles Include="$(TargetDir)$(TargetName).*.$(TargetFramework).cplz" />
</ItemGroup>
<Delete Files="@(OldCPLZFiles)" Condition="@(OldCPLZFiles) != ''">
<Output TaskParameter="DeletedFiles" ItemName="DeletedList"/> <Output TaskParameter="DeletedFiles" ItemName="DeletedList"/>
</Delete> </Delete>
<Message Text="Deleted files: '@(DeletedList)'" /> <Message Text="Deleted old CPLZ files: '@(DeletedList)'" Condition="@(DeletedList) != ''" />
</Target> </Target>
<Target Name="CreateCPLZ" AfterTargets="Build" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''" DependsOnTargets="DeleteCPLZ"> <Target Name="CreateCPLZ" AfterTargets="Build" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''" DependsOnTargets="DeleteCPLZ">

View File

@@ -131,14 +131,14 @@ namespace PepperDash.Core
/// <param name="key"></param> /// <param name="key"></param>
/// <param name="address"></param> /// <param name="address"></param>
/// <param name="port"></param> /// <param name="port"></param>
/// <param name="buffefSize"></param> /// <param name="bufferSize"></param>
public GenericUdpServer(string key, string address, int port, int buffefSize) public GenericUdpServer(string key, string address, int port, int bufferSize)
: base(key) : base(key)
{ {
StreamDebugging = new CommunicationStreamDebugging(key); StreamDebugging = new CommunicationStreamDebugging(key);
Hostname = address; Hostname = address;
Port = port; Port = port;
BufferSize = buffefSize; BufferSize = bufferSize;
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler); CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
@@ -194,7 +194,21 @@ namespace PepperDash.Core
{ {
if (Server == null) if (Server == null)
{ {
Server = new UDPServer(); try
{
var address = IPAddress.Parse(Hostname);
Server = new UDPServer(address, Port, BufferSize);
}
catch (Exception ex)
{
this.LogError("Error parsing IP Address '{ipAddress}': message: {message}", Hostname, ex.Message);
this.LogInformation("Creating UDPServer with default buffersize");
Server = new UDPServer();
}
} }
if (string.IsNullOrEmpty(Hostname)) if (string.IsNullOrEmpty(Hostname))

View File

@@ -40,20 +40,20 @@ namespace PepperDash.Core
private static ILogger _logger; private static ILogger _logger;
private static readonly LoggingLevelSwitch _consoleLoggingLevelSwitch; private static readonly LoggingLevelSwitch _consoleLogLevelSwitch;
private static readonly LoggingLevelSwitch _websocketLoggingLevelSwitch; private static readonly LoggingLevelSwitch _websocketLogLevelSwitch;
private static readonly LoggingLevelSwitch _errorLogLevelSwitch; private static readonly LoggingLevelSwitch _errorLogLevelSwitch;
private static readonly LoggingLevelSwitch _fileLevelSwitch; private static readonly LoggingLevelSwitch _fileLogLevelSwitch;
/// <summary> /// <summary>
/// Gets the minimum log level for the websocket sink. /// Gets the minimum log level for the websocket sink.
/// </summary> /// </summary>
public static LogEventLevel WebsocketMinimumLogLevel public static LogEventLevel WebsocketMinimumLogLevel
{ {
get { return _websocketLoggingLevelSwitch.MinimumLevel; } get { return _websocketLogLevelSwitch.MinimumLevel; }
} }
private static readonly DebugWebsocketSink _websocketSink; private static readonly DebugWebsocketSink _websocketSink;
@@ -138,13 +138,13 @@ namespace PepperDash.Core
var defaultFileLogLevel = GetStoredLogEventLevel(FileLevelStoreKey); var defaultFileLogLevel = GetStoredLogEventLevel(FileLevelStoreKey);
_consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel); _consoleLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel);
_websocketLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel); _websocketLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel);
_errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel); _errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel);
_fileLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel); _fileLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel);
_websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true)); _websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true));
@@ -162,14 +162,14 @@ namespace PepperDash.Core
.MinimumLevel.Verbose() .MinimumLevel.Verbose()
.Enrich.FromLogContext() .Enrich.FromLogContext()
.Enrich.With(new CrestronEnricher()) .Enrich.With(new CrestronEnricher())
.WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLoggingLevelSwitch) .WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLogLevelSwitch)
.WriteTo.Sink(_websocketSink, levelSwitch: _websocketLoggingLevelSwitch) .WriteTo.Sink(_websocketSink, levelSwitch: _websocketLogLevelSwitch)
.WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: _errorLogLevelSwitch) .WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: _errorLogLevelSwitch)
.WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath, .WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath,
rollingInterval: RollingInterval.Day, rollingInterval: RollingInterval.Day,
restrictedToMinimumLevel: LogEventLevel.Debug, restrictedToMinimumLevel: LogEventLevel.Debug,
retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 30 : 60, retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 30 : 60,
levelSwitch: _fileLevelSwitch levelSwitch: _fileLogLevelSwitch
); );
try try
@@ -237,10 +237,13 @@ namespace PepperDash.Core
if (DoNotLoadConfigOnNextBoot) if (DoNotLoadConfigOnNextBoot)
CrestronConsole.PrintLine(string.Format("Program {0} will not load config after next boot. Use console command go:{0} to load the config manually", InitialParametersClass.ApplicationNumber)); CrestronConsole.PrintLine(string.Format("Program {0} will not load config after next boot. Use console command go:{0} to load the config manually", InitialParametersClass.ApplicationNumber));
_consoleLoggingLevelSwitch.MinimumLevelChanged += (sender, args) => _errorLogLevelSwitch.MinimumLevelChanged += (sender, args) =>
{ {
LogMessage(LogEventLevel.Information, "Console debug level set to {minimumLevel}", _consoleLoggingLevelSwitch.MinimumLevel); LogMessage(LogEventLevel.Information, "Error log debug level set to {minimumLevel}", _errorLogLevelSwitch.MinimumLevel);
}; };
// Set initial error log level based on platform && stored level. If appliance, use stored level, otherwise default to verbose
SetErrorLogMinimumDebugLevel(CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? _errorLogLevelSwitch.MinimumLevel : LogEventLevel.Verbose);
} }
/// <summary> /// <summary>
@@ -273,9 +276,9 @@ namespace PepperDash.Core
{ {
CrestronConsole.Print($"Unable to retrieve stored log level for {levelStoreKey}.\r\nError: {result}.\r\nSetting level to {LogEventLevel.Information}\r\n"); CrestronConsole.Print($"Unable to retrieve stored log level for {levelStoreKey}.\r\nError: {result}.\r\nSetting level to {LogEventLevel.Information}\r\n");
CrestronDataStoreStatic.SetLocalIntValue(levelStoreKey, (int)LogEventLevel.Information); CrestronDataStoreStatic.SetLocalIntValue(levelStoreKey, levelStoreKey == ErrorLogLevelStoreKey ? (int)LogEventLevel.Warning : (int)LogEventLevel.Information);
return LogEventLevel.Information; return levelStoreKey == ErrorLogLevelStoreKey ? LogEventLevel.Warning : LogEventLevel.Information;
} }
if (logLevel < 0 || logLevel > 5) if (logLevel < 0 || logLevel > 5)
@@ -284,6 +287,8 @@ namespace PepperDash.Core
return LogEventLevel.Information; return LogEventLevel.Information;
} }
CrestronConsole.PrintLine($"Stored log level for {levelStoreKey} is {logLevel}");
return (LogEventLevel)logLevel; return (LogEventLevel)logLevel;
} }
catch (Exception ex) catch (Exception ex)
@@ -349,7 +354,11 @@ namespace PepperDash.Core
if (levelString.Trim() == "?") if (levelString.Trim() == "?")
{ {
CrestronConsole.ConsoleCommandResponse( CrestronConsole.ConsoleCommandResponse(
"Used to set the minimum level of debug messages to be printed to the console:\r\n" + "Used to set the minimum level of debug messages:\r\n" +
"Usage: appdebug:P [sink] [level]\r\n" +
" sink: console (default), errorlog, file, all\r\n" +
" all: sets all sinks to the specified level\r\n" +
" level: 0-5 or LogEventLevel name\r\n" +
$"{_logLevels[0]} = 0\r\n" + $"{_logLevels[0]} = 0\r\n" +
$"{_logLevels[1]} = 1\r\n" + $"{_logLevels[1]} = 1\r\n" +
$"{_logLevels[2]} = 2\r\n" + $"{_logLevels[2]} = 2\r\n" +
@@ -361,32 +370,88 @@ namespace PepperDash.Core
if (string.IsNullOrEmpty(levelString.Trim())) if (string.IsNullOrEmpty(levelString.Trim()))
{ {
CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", _consoleLoggingLevelSwitch.MinimumLevel); CrestronConsole.ConsoleCommandResponse("Console log level = {0}\r\n", _consoleLogLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse("File log level = {0}\r\n", _fileLogLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse("Error log level = {0}\r\n", _errorLogLevelSwitch.MinimumLevel);
return; return;
} }
if (int.TryParse(levelString, out var levelInt)) // Parse tokens: first token is sink (defaults to console), second token is level
var tokens = levelString.Trim().Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
string sinkName;
string levelToken;
if (tokens.Length == 1)
{
// Single token - assume it's a level for console sink
sinkName = "console";
levelToken = tokens[0];
}
else if (tokens.Length == 2)
{
// Two tokens - first is sink, second is level
sinkName = tokens[0].ToLowerInvariant();
levelToken = tokens[1];
}
else
{
CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [sink] [level]");
return;
}
// Parse the level using the same logic as before
LogEventLevel level;
if (int.TryParse(levelToken, out var levelInt))
{ {
if (levelInt < 0 || levelInt > 5) if (levelInt < 0 || levelInt > 5)
{ {
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level. If using a number, value must be between 0-5"); CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelToken} to valid log level. If using a number, value must be between 0-5");
return; return;
} }
SetDebugLevel((uint)levelInt);
return;
}
if (Enum.TryParse<LogEventLevel>(levelString, true, out var levelEnum)) if (!_logLevels.TryGetValue((uint)levelInt, out level))
{
level = LogEventLevel.Information;
CrestronConsole.ConsoleCommandResponse($"{levelInt} not valid. Setting level to {level}");
}
}
else if (Enum.TryParse(levelToken, true, out level))
{ {
SetDebugLevel(levelEnum); // Successfully parsed as LogEventLevel enum
}
else
{
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelToken} to valid log level");
return; return;
} }
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level"); // Set the level for the specified sink
switch (sinkName)
{
case "console":
SetDebugLevel(level);
break;
case "errorlog":
SetErrorLogMinimumDebugLevel(level);
break;
case "file":
SetFileMinimumDebugLevel(level);
break;
case "all":
SetDebugLevel(level);
SetErrorLogMinimumDebugLevel(level);
SetFileMinimumDebugLevel(level);
break;
default:
CrestronConsole.ConsoleCommandResponse($"Error: Unknown sink '{sinkName}'. Valid sinks: console, errorlog, file");
break;
}
} }
catch catch
{ {
CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [0-5]"); CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [sink] [level]");
} }
} }
@@ -416,10 +481,10 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public static void SetDebugLevel(LogEventLevel level) public static void SetDebugLevel(LogEventLevel level)
{ {
_consoleLoggingLevelSwitch.MinimumLevel = level; _consoleLogLevelSwitch.MinimumLevel = level;
CrestronConsole.ConsoleCommandResponse("[Application {0}], Debug level set to {1}\r\n", CrestronConsole.ConsoleCommandResponse("[Application {0}] Debug level set to {1}\r\n",
InitialParametersClass.ApplicationNumber, _consoleLoggingLevelSwitch.MinimumLevel); InitialParametersClass.ApplicationNumber, _consoleLogLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}"); CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
@@ -436,14 +501,14 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public static void SetWebSocketMinimumDebugLevel(LogEventLevel level) public static void SetWebSocketMinimumDebugLevel(LogEventLevel level)
{ {
_websocketLoggingLevelSwitch.MinimumLevel = level; _websocketLogLevelSwitch.MinimumLevel = level;
var err = CrestronDataStoreStatic.SetLocalUintValue(WebSocketLevelStoreKey, (uint)level); var err = CrestronDataStoreStatic.SetLocalUintValue(WebSocketLevelStoreKey, (uint)level);
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
LogMessage(LogEventLevel.Information, "Error saving websocket debug level setting: {erro}", err); LogMessage(LogEventLevel.Information, "Error saving websocket debug level setting: {erro}", err);
LogMessage(LogEventLevel.Information, "Websocket debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel); LogMessage(LogEventLevel.Information, "Websocket debug level set to {0}", _websocketLogLevelSwitch.MinimumLevel);
} }
/// <summary> /// <summary>
@@ -453,12 +518,17 @@ namespace PepperDash.Core
{ {
_errorLogLevelSwitch.MinimumLevel = level; _errorLogLevelSwitch.MinimumLevel = level;
var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level); CrestronConsole.ConsoleCommandResponse("[Application {0}] Error log level set to {1}\r\n",
InitialParametersClass.ApplicationNumber, _errorLogLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
var err = CrestronDataStoreStatic.SetLocalIntValue(ErrorLogLevelStoreKey, (int)level);
CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}");
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
LogMessage(LogEventLevel.Information, "Error saving Error Log debug level setting: {error}", err); CrestronConsole.PrintLine($"Error saving error log debug level setting: {err}");
LogMessage(LogEventLevel.Information, "Error log debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
} }
/// <summary> /// <summary>
@@ -466,14 +536,19 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public static void SetFileMinimumDebugLevel(LogEventLevel level) public static void SetFileMinimumDebugLevel(LogEventLevel level)
{ {
_errorLogLevelSwitch.MinimumLevel = level; _fileLogLevelSwitch.MinimumLevel = level;
var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level); CrestronConsole.ConsoleCommandResponse("[Application {0}] File log level set to {1}\r\n",
InitialParametersClass.ApplicationNumber, _fileLogLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
var err = CrestronDataStoreStatic.SetLocalIntValue(FileLevelStoreKey, (int)level);
CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}");
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
LogMessage(LogEventLevel.Information, "Error saving File debug level setting: {error}", err); CrestronConsole.PrintLine($"Error saving file debug level setting: {err}");
LogMessage(LogEventLevel.Information, "File debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
} }
/// <summary> /// <summary>

View File

@@ -2,59 +2,54 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json; using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using Serilog.Events;
namespace PepperDash.Essentials.Core.CrestronIO namespace PepperDash.Essentials.Core.CrestronIO
{ {
[Description("Wrapper class for Digital Input")]
/// <summary> /// <summary>
/// Represents a GenericDigitalInputDevice /// Represents a GenericDigitalInputDevice
/// </summary> /// </summary>
public class GenericDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput /// [Description("Wrapper class for Digital Input")]
public class GenericDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IHasFeedback
{ {
/// <summary> private DigitalInput inputPort;
/// Gets or sets the InputPort
/// </summary>
public DigitalInput InputPort { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the InputStateFeedback /// Gets or sets the InputStateFeedback
/// </summary> /// </summary>
public BoolFeedback InputStateFeedback { get; private set; } public BoolFeedback InputStateFeedback { get; private set; }
Func<bool> InputStateFeedbackFunc /// <inheritdoc />
{ public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>();
get
{
return () => InputPort.State;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="GenericDigitalInputDevice"/> class.
/// </summary>
/// <param name="key">key for device</param>
/// <param name="name">name for device</param>
/// <param name="postActivationFunc">function to call after activation. Should return the DigitalInput</param>
/// <param name="config">config for device</param>
public GenericDigitalInputDevice(string key, string name, Func<IOPortConfig, DigitalInput> postActivationFunc, public GenericDigitalInputDevice(string key, string name, Func<IOPortConfig, DigitalInput> postActivationFunc,
IOPortConfig config) IOPortConfig config)
: base(key, name) : base(key, name)
{ {
InputStateFeedback = new BoolFeedback(InputStateFeedbackFunc); InputStateFeedback = new BoolFeedback("inputState", () => inputPort.State);
AddPostActivationAction(() => AddPostActivationAction(() =>
{ {
InputPort = postActivationFunc(config); inputPort = postActivationFunc(config);
InputPort.Register(); inputPort.Register();
InputPort.StateChange += InputPort_StateChange;
inputPort.StateChange += InputPort_StateChange;
}); });
} }
@@ -71,41 +66,31 @@ namespace PepperDash.Essentials.Core.CrestronIO
private static DigitalInput GetDigitalInput(IOPortConfig dc) private static DigitalInput GetDigitalInput(IOPortConfig dc)
{ {
IDigitalInputPorts ioPortDevice;
if (dc.PortDeviceKey.Equals("processor")) if (dc.PortDeviceKey.Equals("processor"))
{ {
if (!Global.ControlSystem.SupportsDigitalInput) if (!Global.ControlSystem.SupportsDigitalInput)
{ {
Debug.LogMessage(LogEventLevel.Information, "GetDigitalInput: Processor does not support Digital Inputs"); Debug.LogError("GetDigitalInput: Processor does not support Digital Inputs");
return null; return null;
} }
ioPortDevice = Global.ControlSystem;
return Global.ControlSystem.DigitalInputPorts[dc.PortNumber];
} }
else
if (!(DeviceManager.GetDeviceForKey(dc.PortDeviceKey) is IDigitalInputPorts ioPortDevice))
{ {
var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IDigitalInputPorts; Debug.LogError("GetDigitalInput: Device {key} is not a valid device", dc.PortDeviceKey);
if (ioPortDev == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetDigitalInput: Device {0} is not a valid device", dc.PortDeviceKey);
return null;
}
ioPortDevice = ioPortDev;
}
if (ioPortDevice == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetDigitalInput: Device '0' is not a valid IDigitalInputPorts Device", dc.PortDeviceKey);
return null; return null;
} }
if (dc.PortNumber > ioPortDevice.NumberOfDigitalInputPorts) if (dc.PortNumber > ioPortDevice.NumberOfDigitalInputPorts)
{ {
Debug.LogMessage(LogEventLevel.Information, "GetDigitalInput: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber); Debug.LogError("GetDigitalInput: Device {key} does not contain a digital input port {port}", dc.PortDeviceKey, dc.PortNumber);
return null;
} }
return ioPortDevice.DigitalInputPorts[dc.PortNumber]; return ioPortDevice.DigitalInputPorts[dc.PortNumber];
} }
#endregion #endregion
@@ -131,20 +116,20 @@ namespace PepperDash.Essentials.Core.CrestronIO
} }
else else
{ {
Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); this.LogWarning("Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
} }
try try
{ {
Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); this.LogDebug("Linking to Trilist '{0}'", trilist.ID.ToString("X"));
// Link feedback for input state // Link feedback for input state
InputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.InputState.JoinNumber]); InputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.InputState.JoinNumber]);
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogMessage(LogEventLevel.Debug, this, "Unable to link device '{0}'. Input is null", Key); this.LogError("Unable to link device {key}. {message}", Key, e.Message);
Debug.LogMessage(LogEventLevel.Debug, this, "Error: {0}", e); this.LogDebug(e, "Stack Trace: ");
} }
} }
@@ -153,22 +138,22 @@ namespace PepperDash.Essentials.Core.CrestronIO
#region Factory #region Factory
/// <summary> /// <summary>
/// Represents a GenericDigitalInputDeviceFactory /// Factory for creating GenericDigitalInputDevice devices
/// </summary> /// </summary>
public class GenericDigitalInputDeviceFactory : EssentialsDeviceFactory<GenericDigitalInputDevice> public class GenericDigitalInputDeviceFactory : EssentialsDeviceFactory<GenericDigitalInputDevice>
{ {
/// <summary>
/// Constructor for GenericDigitalInputDeviceFactory
/// </summary>
public GenericDigitalInputDeviceFactory() public GenericDigitalInputDeviceFactory()
{ {
TypeNames = new List<string>() { "digitalinput" }; TypeNames = new List<string>() { "digitalinput" };
} }
/// <summary>
/// BuildDevice method
/// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override EssentialsDevice BuildDevice(DeviceConfig dc) public override EssentialsDevice BuildDevice(DeviceConfig dc)
{ {
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Digital Input Device"); Debug.LogDebug("Factory Attempting to create new Generic Digital Input Device");
var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString()); var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString());

View File

@@ -2,66 +2,64 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Bridges;
using Newtonsoft.Json; using Newtonsoft.Json;
using Serilog.Events; using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials.Core.CrestronIO namespace PepperDash.Essentials.Core.CrestronIO
{ {
/// <summary> /// <summary>
/// Represents a generic digital input deviced tied to a versiport /// Represents a generic digital input deviced tied to a versiport
/// </summary> /// </summary>
public class GenericVersiportAnalogInputDevice : EssentialsBridgeableDevice, IAnalogInput public class GenericVersiportAnalogInputDevice : EssentialsBridgeableDevice, IAnalogInput, IHasFeedback
{ {
public Versiport InputPort { get; private set; } private Versiport inputPort;
/// <inheritdoc />
public IntFeedback InputValueFeedback { get; private set; } public IntFeedback InputValueFeedback { get; private set; }
/// <summary>
/// Get the InputMinimumChangeFeedback
/// </summary>
/// <remarks>
/// Updates when the analog input minimum change value changes
/// </remarks>
public IntFeedback InputMinimumChangeFeedback { get; private set; } public IntFeedback InputMinimumChangeFeedback { get; private set; }
Func<int> InputValueFeedbackFunc /// <inheritdoc />
{ public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>();
get
{
return () => InputPort.AnalogIn;
}
}
Func<int> InputMinimumChangeFeedbackFunc
{
get { return () => InputPort.AnalogMinChange; }
}
/// <summary>
/// Initializes a new instance of the <see cref="GenericVersiportAnalogInputDevice"/> class.
/// </summary>
/// <param name="key">key for the device</param>
/// <param name="name">name for the device</param>
/// <param name="postActivationFunc">function to call after activation</param>
/// <param name="config">IO port configuration</param>
public GenericVersiportAnalogInputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) : public GenericVersiportAnalogInputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) :
base(key, name) base(key, name)
{ {
InputValueFeedback = new IntFeedback(InputValueFeedbackFunc); InputValueFeedback = new IntFeedback("inputValue", () => inputPort.AnalogIn);
InputMinimumChangeFeedback = new IntFeedback(InputMinimumChangeFeedbackFunc); InputMinimumChangeFeedback = new IntFeedback("inputMinimumChange", () => inputPort.AnalogMinChange);
AddPostActivationAction(() => AddPostActivationAction(() =>
{ {
InputPort = postActivationFunc(config); inputPort = postActivationFunc(config);
InputPort.Register(); inputPort.Register();
InputPort.SetVersiportConfiguration(eVersiportConfiguration.AnalogInput); inputPort.SetVersiportConfiguration(eVersiportConfiguration.AnalogInput);
InputPort.AnalogMinChange = (ushort)(config.MinimumChange > 0 ? config.MinimumChange : 655); inputPort.AnalogMinChange = (ushort)(config.MinimumChange > 0 ? config.MinimumChange : 655);
if (config.DisablePullUpResistor) if (config.DisablePullUpResistor)
InputPort.DisablePullUpResistor = true; inputPort.DisablePullUpResistor = true;
InputPort.VersiportChange += InputPort_VersiportChange; inputPort.VersiportChange += InputPort_VersiportChange;
Debug.LogMessage(LogEventLevel.Debug, this, "Created GenericVersiportAnalogInputDevice on port '{0}'. DisablePullUpResistor: '{1}'", config.PortNumber, InputPort.DisablePullUpResistor);
this.LogDebug("Created GenericVersiportAnalogInputDevice on port {port}. DisablePullUpResistor: {pullUpResistorDisabled}", config.PortNumber, inputPort.DisablePullUpResistor);
}); });
} }
@@ -69,20 +67,17 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// <summary> /// <summary>
/// Set minimum voltage change for device to update voltage changed method /// Set minimum voltage change for device to update voltage changed method
/// </summary> /// </summary>
/// <param name="value">valid values range from 0 - 65535, representing the full 100% range of the processor voltage source. Check processor documentation for details</param> /// <param name="value">valid values range from 0 - 65535, representing the full 100% range of the processor voltage source. Check processor documentation for details</param>
/// <summary>
/// SetMinimumChange method
/// </summary>
public void SetMinimumChange(ushort value) public void SetMinimumChange(ushort value)
{ {
InputPort.AnalogMinChange = value; inputPort.AnalogMinChange = value;
} }
void InputPort_VersiportChange(Versiport port, VersiportEventArgs args) void InputPort_VersiportChange(Versiport port, VersiportEventArgs args)
{ {
Debug.LogMessage(LogEventLevel.Debug, this, "Versiport change: {0}", args.Event); this.LogDebug("Versiport change: {event}", args.Event);
if(args.Event == eVersiportEvent.AnalogInChange) if (args.Event == eVersiportEvent.AnalogInChange)
InputValueFeedback.FireUpdate(); InputValueFeedback.FireUpdate();
if (args.Event == eVersiportEvent.AnalogMinChangeChange) if (args.Event == eVersiportEvent.AnalogMinChangeChange)
InputMinimumChangeFeedback.FireUpdate(); InputMinimumChangeFeedback.FireUpdate();
@@ -91,9 +86,6 @@ namespace PepperDash.Essentials.Core.CrestronIO
#region Bridge Linking #region Bridge Linking
/// <summary>
/// LinkToApi method
/// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
{ {
@@ -110,12 +102,12 @@ namespace PepperDash.Essentials.Core.CrestronIO
} }
else else
{ {
Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); this.LogWarning("Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
} }
try try
{ {
Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); this.LogDebug("Linking to Trilist '{trilistId}'", trilist.ID.ToString("X"));
// Link feedback for input state // Link feedback for input state
InputValueFeedback.LinkInputSig(trilist.UShortInput[joinMap.InputValue.JoinNumber]); InputValueFeedback.LinkInputSig(trilist.UShortInput[joinMap.InputValue.JoinNumber]);
@@ -125,8 +117,8 @@ namespace PepperDash.Essentials.Core.CrestronIO
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogMessage(LogEventLevel.Debug, this, "Unable to link device '{0}'. Input is null", Key); this.LogError("Unable to link device {key}: {message}", Key, e.Message);
Debug.LogMessage(LogEventLevel.Debug, this, "Error: {0}", e); this.LogDebug(e, "Stack Trace: ");
} }
trilist.OnlineStatusChange += (d, args) => trilist.OnlineStatusChange += (d, args) =>
@@ -138,11 +130,6 @@ namespace PepperDash.Essentials.Core.CrestronIO
} }
void trilist_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)
{
throw new NotImplementedException();
}
#endregion #endregion
@@ -151,70 +138,55 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// </summary> /// </summary>
public static Versiport GetVersiportDigitalInput(IOPortConfig dc) public static Versiport GetVersiportDigitalInput(IOPortConfig dc)
{ {
IIOPorts ioPortDevice;
if (dc.PortDeviceKey.Equals("processor")) if (dc.PortDeviceKey.Equals("processor"))
{ {
if (!Global.ControlSystem.SupportsVersiport) if (!Global.ControlSystem.SupportsVersiport)
{ {
Debug.LogMessage(LogEventLevel.Information, "GetVersiportAnalogInput: Processor does not support Versiports"); Debug.LogError("GetVersiportAnalogInput: Processor does not support Versiports");
return null; return null;
} }
ioPortDevice = Global.ControlSystem; return Global.ControlSystem.VersiPorts[dc.PortNumber];
} }
else
if (!(DeviceManager.GetDeviceForKey(dc.PortDeviceKey) is IIOPorts ioPortDevice))
{ {
var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IIOPorts; Debug.LogError("GetVersiportAnalogInput: Device {key} is not a valid device", dc.PortDeviceKey);
if (ioPortDev == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetVersiportAnalogInput: Device {0} is not a valid device", dc.PortDeviceKey);
return null;
}
ioPortDevice = ioPortDev;
}
if (ioPortDevice == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetVersiportAnalogInput: Device '0' is not a valid IIOPorts Device", dc.PortDeviceKey);
return null; return null;
} }
if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts) if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts)
{ {
Debug.LogMessage(LogEventLevel.Information, "GetVersiportAnalogInput: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber); Debug.LogError("GetVersiportAnalogInput: Device {key} does not contain a port {port}", dc.PortDeviceKey, dc.PortNumber);
return null; return null;
} }
if(!ioPortDevice.VersiPorts[dc.PortNumber].SupportsAnalogInput) if (!ioPortDevice.VersiPorts[dc.PortNumber].SupportsAnalogInput)
{ {
Debug.LogMessage(LogEventLevel.Information, "GetVersiportAnalogInput: Device {0} does not support AnalogInput on port {1}", dc.PortDeviceKey, dc.PortNumber); Debug.LogError("GetVersiportAnalogInput: Device {key} does not support AnalogInput on port {port}", dc.PortDeviceKey, dc.PortNumber);
return null; return null;
} }
return ioPortDevice.VersiPorts[dc.PortNumber]; return ioPortDevice.VersiPorts[dc.PortNumber];
} }
} }
/// <summary> /// <summary>
/// Represents a GenericVersiportAbalogInputDeviceFactory /// Factory for creating GenericVersiportAnalogInputDevice devices
/// </summary> /// </summary>
public class GenericVersiportAbalogInputDeviceFactory : EssentialsDeviceFactory<GenericVersiportAnalogInputDevice> public class GenericVersiportAnalogInputDeviceFactory : EssentialsDeviceFactory<GenericVersiportAnalogInputDevice>
{ {
public GenericVersiportAbalogInputDeviceFactory() /// <summary>
/// Constructor for GenericVersiportAnalogInputDeviceFactory
/// </summary>
public GenericVersiportAnalogInputDeviceFactory()
{ {
TypeNames = new List<string>() { "versiportanaloginput" }; TypeNames = new List<string>() { "versiportanaloginput" };
} }
/// <summary>
/// BuildDevice method
/// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override EssentialsDevice BuildDevice(DeviceConfig dc) public override EssentialsDevice BuildDevice(DeviceConfig dc)
{ {
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Versiport Device"); Debug.LogDebug("Factory Attempting to create new Generic Versiport Device");
var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString()); var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString());

View File

@@ -2,78 +2,82 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Bridges;
using Newtonsoft.Json; using Newtonsoft.Json;
using Serilog.Events; using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials.Core.CrestronIO namespace PepperDash.Essentials.Core.CrestronIO
{ {
/// <summary> /// <summary>
/// Represents a generic digital input deviced tied to a versiport /// Represents a generic digital input deviced tied to a versiport
/// </summary> /// </summary>
public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IPartitionStateProvider public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IPartitionStateProvider, IHasFeedback
{ {
public Versiport InputPort { get; private set; } private Versiport inputPort;
/// <summary>
/// Gets or sets the InputStateFeedback
/// </summary>
public BoolFeedback InputStateFeedback { get; private set; } public BoolFeedback InputStateFeedback { get; private set; }
Func<bool> InputStateFeedbackFunc /// <inheritdoc />
{ public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>();
get
{
return () => InputPort.DigitalIn;
}
}
/// <summary> /// <summary>
/// Gets or sets the PartitionPresentFeedback /// Gets or sets the PartitionPresentFeedback
/// </summary> /// </summary>
public BoolFeedback PartitionPresentFeedback { get; } public BoolFeedback PartitionPresentFeedback { get; }
public bool PartitionPresent => !InputStateFeedbackFunc(); /// <summary>
/// Get partition state
/// </summary>
public bool PartitionPresent => !inputPort.DigitalIn;
/// <summary>
/// Initializes a new instance of the <see cref="GenericVersiportDigitalInputDevice"/> class.
/// </summary>
/// <param name="key">key for device</param>
/// <param name="name">name for device</param>
/// <param name="postActivationFunc">function to call after activation. Should return the Versiport</param>
/// <param name="config">config for device</param>
public GenericVersiportDigitalInputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) : public GenericVersiportDigitalInputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) :
base(key, name) base(key, name)
{ {
InputStateFeedback = new BoolFeedback(InputStateFeedbackFunc); InputStateFeedback = new BoolFeedback("inputState", () => inputPort.DigitalIn);
PartitionPresentFeedback = new BoolFeedback(() => !InputStateFeedbackFunc()); PartitionPresentFeedback = new BoolFeedback("partitionPresent", () => !inputPort.DigitalIn);
AddPostActivationAction(() => AddPostActivationAction(() =>
{ {
InputPort = postActivationFunc(config); inputPort = postActivationFunc(config);
InputPort.Register(); inputPort.Register();
InputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalInput); inputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalInput);
if (config.DisablePullUpResistor) if (config.DisablePullUpResistor)
InputPort.DisablePullUpResistor = true; inputPort.DisablePullUpResistor = true;
InputPort.VersiportChange += InputPort_VersiportChange; inputPort.VersiportChange += InputPort_VersiportChange;
InputStateFeedback.FireUpdate(); InputStateFeedback.FireUpdate();
PartitionPresentFeedback.FireUpdate(); PartitionPresentFeedback.FireUpdate();
Debug.LogMessage(LogEventLevel.Debug, this, "Created GenericVersiportDigitalInputDevice on port '{0}'. DisablePullUpResistor: '{1}'", config.PortNumber, InputPort.DisablePullUpResistor); this.LogDebug("Created GenericVersiportDigitalInputDevice for port {port}. DisablePullUpResistor: {pullUpResistorDisable}", config.PortNumber, inputPort.DisablePullUpResistor);
}); });
Feedbacks.Add(InputStateFeedback);
Feedbacks.Add(PartitionPresentFeedback);
} }
void InputPort_VersiportChange(Versiport port, VersiportEventArgs args) void InputPort_VersiportChange(Versiport port, VersiportEventArgs args)
{ {
Debug.LogMessage(LogEventLevel.Debug, this, "Versiport change: {0}", args.Event); this.LogDebug("Versiport change: {0}", args.Event);
if(args.Event == eVersiportEvent.DigitalInChange) if (args.Event == eVersiportEvent.DigitalInChange)
{ {
InputStateFeedback.FireUpdate(); InputStateFeedback.FireUpdate();
PartitionPresentFeedback.FireUpdate(); PartitionPresentFeedback.FireUpdate();
@@ -102,20 +106,20 @@ namespace PepperDash.Essentials.Core.CrestronIO
} }
else else
{ {
Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); this.LogWarning("Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
} }
try try
{ {
Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); this.LogDebug("Linking to Trilist '{0}'", trilist.ID.ToString("X"));
// Link feedback for input state // Link feedback for input state
InputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.InputState.JoinNumber]); InputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.InputState.JoinNumber]);
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogMessage(LogEventLevel.Debug, this, "Unable to link device '{0}'. Input is null", Key); this.LogError("Unable to link device {key}. Input is null. {message}", Key, e.Message);
Debug.LogMessage(LogEventLevel.Debug, this, "Error: {0}", e); this.LogDebug(e, "Stack Trace: ");
} }
} }
@@ -127,63 +131,50 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// </summary> /// </summary>
public static Versiport GetVersiportDigitalInput(IOPortConfig dc) public static Versiport GetVersiportDigitalInput(IOPortConfig dc)
{ {
IIOPorts ioPortDevice;
if (dc.PortDeviceKey.Equals("processor")) if (dc.PortDeviceKey.Equals("processor"))
{ {
if (!Global.ControlSystem.SupportsVersiport) if (!Global.ControlSystem.SupportsVersiport)
{ {
Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalInput: Processor does not support Versiports"); Debug.LogError("GetVersiportDigitalInput: Processor does not support Versiports");
return null; return null;
} }
ioPortDevice = Global.ControlSystem; return Global.ControlSystem.VersiPorts[dc.PortNumber];
} }
else
if (!(DeviceManager.GetDeviceForKey(dc.PortDeviceKey) is IIOPorts ioPortDevice))
{ {
var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IIOPorts; Debug.LogError("GetVersiportDigitalInput: Device {key} is not a valid device", dc.PortDeviceKey);
if (ioPortDev == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalInput: Device {0} is not a valid device", dc.PortDeviceKey);
return null;
}
ioPortDevice = ioPortDev;
}
if (ioPortDevice == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalInput: Device '0' is not a valid IIOPorts Device", dc.PortDeviceKey);
return null; return null;
} }
if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts) if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts)
{ {
Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalInput: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber); Debug.LogError("GetVersiportDigitalInput: Device {key} does not contain versiport {port}", dc.PortDeviceKey, dc.PortNumber);
return null;
} }
return ioPortDevice.VersiPorts[dc.PortNumber]; return ioPortDevice.VersiPorts[dc.PortNumber];
} }
} }
/// <summary> /// <summary>
/// Represents a GenericVersiportDigitalInputDeviceFactory /// Factory class for GenericVersiportDigitalInputDevice
/// </summary> /// </summary>
public class GenericVersiportDigitalInputDeviceFactory : EssentialsDeviceFactory<GenericVersiportDigitalInputDevice> public class GenericVersiportDigitalInputDeviceFactory : EssentialsDeviceFactory<GenericVersiportDigitalInputDevice>
{ {
/// <summary>
/// Constructor for GenericVersiportDigitalInputDeviceFactory
/// </summary>
public GenericVersiportDigitalInputDeviceFactory() public GenericVersiportDigitalInputDeviceFactory()
{ {
TypeNames = new List<string>() { "versiportinput" }; TypeNames = new List<string>() { "versiportinput" };
} }
/// <summary>
/// BuildDevice method
/// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override EssentialsDevice BuildDevice(DeviceConfig dc) public override EssentialsDevice BuildDevice(DeviceConfig dc)
{ {
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Versiport Device"); Debug.LogDebug("Factory Attempting to create new Generic Versiport Device");
var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString()); var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString());

View File

@@ -2,18 +2,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Bridges;
using Newtonsoft.Json; using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config;
using Serilog.Events; using Serilog.Events;
namespace PepperDash.Essentials.Core.CrestronIO namespace PepperDash.Essentials.Core.CrestronIO
@@ -21,76 +16,68 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// <summary> /// <summary>
/// Represents a generic digital input deviced tied to a versiport /// Represents a generic digital input deviced tied to a versiport
/// </summary> /// </summary>
public class GenericVersiportDigitalOutputDevice : EssentialsBridgeableDevice, IDigitalOutput public class GenericVersiportDigitalOutputDevice : EssentialsBridgeableDevice, IDigitalOutput, IHasFeedback
{ {
public Versiport OutputPort { get; private set; } private Versiport outputPort;
/// <summary>
/// Gets or sets the OutputStateFeedback
/// </summary>
public BoolFeedback OutputStateFeedback { get; private set; } public BoolFeedback OutputStateFeedback { get; private set; }
Func<bool> OutputStateFeedbackFunc /// <inheritdoc />
{ public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>();
get
{
return () => OutputPort.DigitalOut;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="GenericVersiportDigitalOutputDevice"/> class.
/// </summary>
public GenericVersiportDigitalOutputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) : public GenericVersiportDigitalOutputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) :
base(key, name) base(key, name)
{ {
OutputStateFeedback = new BoolFeedback(OutputStateFeedbackFunc); OutputStateFeedback = new BoolFeedback("outputState", () => outputPort.DigitalOut);
AddPostActivationAction(() => AddPostActivationAction(() =>
{ {
OutputPort = postActivationFunc(config); outputPort = postActivationFunc(config);
OutputPort.Register(); outputPort.Register();
if (!OutputPort.SupportsDigitalOutput) if (!outputPort.SupportsDigitalOutput)
{ {
Debug.LogMessage(LogEventLevel.Information, this, "Device does not support configuration as a Digital Output"); this.LogError("Device does not support configuration as a Digital Output");
return; return;
} }
OutputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalOutput); outputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalOutput);
OutputPort.VersiportChange += OutputPort_VersiportChange; outputPort.VersiportChange += OutputPort_VersiportChange;
}); });
} }
void OutputPort_VersiportChange(Versiport port, VersiportEventArgs args) void OutputPort_VersiportChange(Versiport port, VersiportEventArgs args)
{ {
Debug.LogMessage(LogEventLevel.Debug, this, "Versiport change: {0}", args.Event); this.LogDebug("Versiport change: {event}", args.Event);
if(args.Event == eVersiportEvent.DigitalOutChange) if (args.Event == eVersiportEvent.DigitalOutChange)
OutputStateFeedback.FireUpdate(); OutputStateFeedback.FireUpdate();
} }
/// <summary> /// <summary>
/// Set value of the versiport digital output /// Set value of the versiport digital output
/// </summary> /// </summary>
/// <param name="state">value to set the output to</param> /// <param name="state">value to set the output to</param>
/// <summary>
/// SetOutput method
/// </summary>
public void SetOutput(bool state) public void SetOutput(bool state)
{ {
if (OutputPort.SupportsDigitalOutput) if (!outputPort.SupportsDigitalOutput)
{ {
Debug.LogMessage(LogEventLevel.Information, this, "Passed the Check"); this.LogError("Versiport does not support Digital Output Mode");
return;
OutputPort.DigitalOut = state; }
}
else
{
Debug.LogMessage(LogEventLevel.Information, this, "Versiport does not support Digital Output Mode");
}
outputPort.DigitalOut = state;
} }
#region Bridge Linking #region Bridge Linking
@@ -114,12 +101,12 @@ namespace PepperDash.Essentials.Core.CrestronIO
} }
else else
{ {
Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); this.LogWarning("Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
} }
try try
{ {
Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); this.LogDebug("Linking to Trilist '{0}'", trilist.ID.ToString("X"));
// Link feedback for input state // Link feedback for input state
OutputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.OutputState.JoinNumber]); OutputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.OutputState.JoinNumber]);
@@ -127,8 +114,8 @@ namespace PepperDash.Essentials.Core.CrestronIO
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogMessage(LogEventLevel.Debug, this, "Unable to link device '{0}'. Input is null", Key); this.LogError("Unable to link device: {message}", e.Message);
Debug.LogMessage(LogEventLevel.Debug, this, "Error: {0}", e); this.LogDebug(e, "Stack Trace: ");
} }
} }
@@ -140,41 +127,28 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// </summary> /// </summary>
public static Versiport GetVersiportDigitalOutput(IOPortConfig dc) public static Versiport GetVersiportDigitalOutput(IOPortConfig dc)
{ {
if (dc.PortDeviceKey.Equals("processor"))
IIOPorts ioPortDevice; {
if (!Global.ControlSystem.SupportsVersiport)
if (dc.PortDeviceKey.Equals("processor"))
{ {
if (!Global.ControlSystem.SupportsVersiport) Debug.LogError("GetVersiportDigitalOutput: Processor does not support Versiports");
{
Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOuptut: Processor does not support Versiports");
return null;
}
ioPortDevice = Global.ControlSystem;
}
else
{
var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IIOPorts;
if (ioPortDev == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOuptut: Device {0} is not a valid device", dc.PortDeviceKey);
return null;
}
ioPortDevice = ioPortDev;
}
if (ioPortDevice == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOuptut: Device '0' is not a valid IOPorts Device", dc.PortDeviceKey);
return null; return null;
} }
return Global.ControlSystem.VersiPorts[dc.PortNumber];
}
if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts) if (!(DeviceManager.GetDeviceForKey(dc.PortDeviceKey) is IIOPorts ioPortDevice))
{ {
Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOuptut: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber); Debug.LogError("GetVersiportDigitalOutput: Device {key} is not a valid device", dc.PortDeviceKey);
} return null;
var port = ioPortDevice.VersiPorts[dc.PortNumber]; }
return port;
if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts)
{
Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOutput: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber);
return null;
}
return ioPortDevice.VersiPorts[dc.PortNumber];
} }
} }
@@ -184,18 +158,18 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// </summary> /// </summary>
public class GenericVersiportDigitalOutputDeviceFactory : EssentialsDeviceFactory<GenericVersiportDigitalInputDevice> public class GenericVersiportDigitalOutputDeviceFactory : EssentialsDeviceFactory<GenericVersiportDigitalInputDevice>
{ {
/// <summary>
/// Initialize a new instance of the <see cref="GenericVersiportDigitalOutputDeviceFactory"/> class.
/// </summary>
public GenericVersiportDigitalOutputDeviceFactory() public GenericVersiportDigitalOutputDeviceFactory()
{ {
TypeNames = new List<string>() { "versiportoutput" }; TypeNames = new List<string>() { "versiportoutput" };
} }
/// <summary>
/// BuildDevice method
/// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override EssentialsDevice BuildDevice(DeviceConfig dc) public override EssentialsDevice BuildDevice(DeviceConfig dc)
{ {
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Versiport Device"); Debug.LogDebug("Factory Attempting to create new Generic Versiport Device");
var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString()); var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString());

View File

@@ -12,6 +12,12 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// </summary> /// </summary>
public interface IAnalogInput public interface IAnalogInput
{ {
/// <summary>
/// Get the InputValueFeedback.
/// </summary>
/// <remarks>
/// Updates when the analog input value changes
/// </remarks>
IntFeedback InputValueFeedback { get; } IntFeedback InputValueFeedback { get; }
} }
} }

View File

@@ -14,25 +14,28 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// </summary> /// </summary>
public class IOPortConfig public class IOPortConfig
{ {
[JsonProperty("portDeviceKey")]
/// <summary> /// <summary>
/// Gets or sets the PortDeviceKey /// Gets or sets the PortDeviceKey
/// </summary> /// </summary>
[JsonProperty("portDeviceKey")]
public string PortDeviceKey { get; set; } public string PortDeviceKey { get; set; }
[JsonProperty("portNumber")]
/// <summary> /// <summary>
/// Gets or sets the PortNumber /// Gets or sets the PortNumber
/// </summary> /// </summary>
[JsonProperty("portNumber")]
public uint PortNumber { get; set; } public uint PortNumber { get; set; }
[JsonProperty("disablePullUpResistor")]
/// <summary> /// <summary>
/// Gets or sets the DisablePullUpResistor /// Gets or sets the DisablePullUpResistor
/// </summary> /// </summary>
[JsonProperty("disablePullUpResistor")]
public bool DisablePullUpResistor { get; set; } public bool DisablePullUpResistor { get; set; }
[JsonProperty("minimumChange")]
/// <summary> /// <summary>
/// Gets or sets the MinimumChange /// Gets or sets the MinimumChange
/// </summary> /// </summary>
[JsonProperty("minimumChange")]
public int MinimumChange { get; set; } public int MinimumChange { get; set; }
} }
} }

View File

@@ -0,0 +1,11 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Use this interface on a device or room if it uses custom Mobile Control messengers
/// </summary>
public interface ICustomMobileControl : IKeyed
{
}
}

View File

@@ -1,155 +1,72 @@
using System; using System;
using System.Collections.ObjectModel;
using Crestron.SimplSharpPro;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PepperDash.Core; using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{ {
/// <summary>
/// Use this interface on a device or room if it uses custom Mobile Control messengers
/// </summary>
public interface ICustomMobileControl : IKeyed
{
}
/*/// <summary>
/// Describes a MobileControlSystemController
/// </summary>
public interface IMobileControl : IKeyed
{
void CreateMobileControlRoomBridge(IEssentialsRoom room, IMobileControl parent);
void LinkSystemMonitorToAppServer();
}*/
/// <summary> /// <summary>
/// Defines the contract for IMobileControl /// Defines the contract for IMobileControl
/// </summary> /// </summary>
public interface IMobileControl : IKeyed public interface IMobileControl : IKeyed
{ {
/// <summary>
/// Gets the Host
/// </summary>
string Host { get; } string Host { get; }
/// <summary>
/// Gets the Client App URL
/// </summary>
string ClientAppUrl { get; } string ClientAppUrl { get; }
/// <summary>
/// Gets the System UUID
/// </summary>
string SystemUuid { get; } string SystemUuid { get; }
/// <summary>
/// Gets the ApiOnlineAndAuthorized feedback
/// </summary>
BoolFeedback ApiOnlineAndAuthorized { get; } BoolFeedback ApiOnlineAndAuthorized { get; }
/// <summary>
/// Sends the message object to the AppServer
/// </summary>
/// <param name="o">Message to send</param>
void SendMessageObject(IMobileControlMessage o); void SendMessageObject(IMobileControlMessage o);
/// <summary>
/// Adds an action for a messenger
/// </summary>
/// <typeparam name="T">Messenger type. Must implement IMobileControlMessenger</typeparam>
/// <param name="messenger">messenger to register</param>
/// <param name="action">action to add</param>
void AddAction<T>(T messenger, Action<string, string, JToken> action) where T : IMobileControlMessenger; void AddAction<T>(T messenger, Action<string, string, JToken> action) where T : IMobileControlMessenger;
/// <summary>
/// Removes an action for a messenger
/// </summary>
/// <param name="key">key for action</param>
void RemoveAction(string key); void RemoveAction(string key);
/// <summary>
/// Adds a device messenger
/// </summary>
/// <param name="messenger">Messenger to add</param>
void AddDeviceMessenger(IMobileControlMessenger messenger); void AddDeviceMessenger(IMobileControlMessenger messenger);
/// <summary>
/// Check if a device messenger exists
/// </summary>
/// <param name="key">Messenger key to find</param>
bool CheckForDeviceMessenger(string key); bool CheckForDeviceMessenger(string key);
/// <summary>
/// Get a Room Messenger by key
/// </summary>
/// <param name="key">messenger key to find</param>
/// <returns>Messenger if found, null otherwise</returns>
IMobileControlRoomMessenger GetRoomMessenger(string key); IMobileControlRoomMessenger GetRoomMessenger(string key);
}
/// <summary>
/// Defines the contract for IMobileControlMessenger
/// </summary>
public interface IMobileControlMessenger : IKeyed
{
IMobileControl AppServerController { get; }
string MessagePath { get; }
string DeviceKey { get; }
void RegisterWithAppServer(IMobileControl appServerController);
}
public interface IMobileControlMessage
{
[JsonProperty("type")]
string Type { get; }
[JsonProperty("clientId", NullValueHandling = NullValueHandling.Ignore)]
string ClientId { get; }
[JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
JToken Content { get; }
}
/// <summary>
/// Defines the contract for IMobileControlRoomMessenger
/// </summary>
public interface IMobileControlRoomMessenger : IKeyed
{
event EventHandler<EventArgs> UserCodeChanged;
event EventHandler<EventArgs> UserPromptedForCode;
event EventHandler<EventArgs> ClientJoined;
event EventHandler<EventArgs> AppUrlChanged;
string UserCode { get; }
string QrCodeUrl { get; }
string QrCodeChecksum { get; }
string McServerUrl { get; }
string RoomName { get; }
string AppUrl { get; }
void UpdateAppUrl(string url);
}
/// <summary>
/// Defines the contract for IMobileControlAction
/// </summary>
public interface IMobileControlAction
{
IMobileControlMessenger Messenger { get; }
Action<string, string, JToken> Action { get; }
}
/// <summary>
/// Defines the contract for IMobileControlTouchpanelController
/// </summary>
public interface IMobileControlTouchpanelController : IKeyed
{
/// <summary>
/// The default room key for the controller
/// </summary>
string DefaultRoomKey { get; }
/// <summary>
/// Sets the application URL for the controller
/// </summary>
/// <param name="url">The application URL</param>
void SetAppUrl(string url);
/// <summary>
/// Indicates whether the controller uses a direct server connection
/// </summary>
bool UseDirectServer { get; }
/// <summary>
/// Indicates whether the controller is a Zoom Room controller
/// </summary>
bool ZoomRoomController { get; }
}
/// <summary>
/// Describes a MobileControl Crestron Touchpanel Controller
/// This interface extends the IMobileControlTouchpanelController to include connected IP information
/// </summary>
public interface IMobileControlCrestronTouchpanelController : IMobileControlTouchpanelController
{
/// <summary>
/// Gets a collection of connected IP information for the touchpanel controller
/// </summary>
ReadOnlyCollection<ConnectedIpInformation> ConnectedIps { get; }
} }
} }

View File

@@ -0,0 +1,15 @@
using System;
using Newtonsoft.Json.Linq;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Defines the contract for IMobileControlAction
/// </summary>
public interface IMobileControlAction
{
IMobileControlMessenger Messenger { get; }
Action<string, string, JToken> Action { get; }
}
}

View File

@@ -0,0 +1,17 @@
using System.Collections.ObjectModel;
using Crestron.SimplSharpPro;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Describes a MobileControl Crestron Touchpanel Controller
/// This interface extends the IMobileControlTouchpanelController to include connected IP information
/// </summary>
public interface IMobileControlCrestronTouchpanelController : IMobileControlTouchpanelController
{
/// <summary>
/// Gets a collection of connected IP information for the touchpanel controller
/// </summary>
ReadOnlyCollection<ConnectedIpInformation> ConnectedIps { get; }
}
}

View File

@@ -0,0 +1,18 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
public interface IMobileControlMessage
{
[JsonProperty("type")]
string Type { get; }
[JsonProperty("clientId", NullValueHandling = NullValueHandling.Ignore)]
string ClientId { get; }
[JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
JToken Content { get; }
}
}

View File

@@ -0,0 +1,31 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Defines the contract for IMobileControlMessenger
/// </summary>
public interface IMobileControlMessenger : IKeyed
{
/// <summary>
/// Parent controller for this messenger
/// </summary>
IMobileControl AppServerController { get; }
/// <summary>
/// Path to listen for messages
/// </summary>
string MessagePath { get; }
/// <summary>
/// Key of the device this messenger is associated with
/// </summary>
string DeviceKey { get; }
/// <summary>
/// Register this messenger with the AppServerController
/// </summary>
/// <param name="appServerController"></param>
void RegisterWithAppServer(IMobileControl appServerController);
}
}

View File

@@ -0,0 +1,23 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Defines the contract for IMobileControlMessenger
/// </summary>
public interface IMobileControlMessengerWithSubscriptions : IMobileControlMessenger
{
/// <summary>
/// Unsubscribe a client from this messenger
/// </summary>
/// <param name="clientId"></param>
void UnsubscribeClient(string clientId);
/// <summary>
/// Register this messenger with the AppServerController
/// </summary>
/// <param name="appServerController">parent for this messenger</param>
/// <param name="enableMessengerSubscriptions">Enable messenger subscriptions</param>
void RegisterWithAppServer(IMobileControl appServerController, bool enableMessengerSubscriptions);
}
}

View File

@@ -0,0 +1,33 @@
using System;
using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Defines the contract for IMobileControlRoomMessenger
/// </summary>
public interface IMobileControlRoomMessenger : IKeyed
{
event EventHandler<EventArgs> UserCodeChanged;
event EventHandler<EventArgs> UserPromptedForCode;
event EventHandler<EventArgs> ClientJoined;
event EventHandler<EventArgs> AppUrlChanged;
string UserCode { get; }
string QrCodeUrl { get; }
string QrCodeChecksum { get; }
string McServerUrl { get; }
string RoomName { get; }
string AppUrl { get; }
void UpdateAppUrl(string url);
}
}

View File

@@ -0,0 +1,31 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Defines the contract for IMobileControlTouchpanelController
/// </summary>
public interface IMobileControlTouchpanelController : IKeyed
{
/// <summary>
/// The default room key for the controller
/// </summary>
string DefaultRoomKey { get; }
/// <summary>
/// Sets the application URL for the controller
/// </summary>
/// <param name="url">The application URL</param>
void SetAppUrl(string url);
/// <summary>
/// Indicates whether the controller uses a direct server connection
/// </summary>
bool UseDirectServer { get; }
/// <summary>
/// Indicates whether the controller is a Zoom Room controller
/// </summary>
bool ZoomRoomController { get; }
}
}

View File

@@ -18,7 +18,7 @@ namespace PepperDash.Essentials.Core
public List<string> TypeNames { get; protected set; } public List<string> TypeNames { get; protected set; }
/// <summary> /// <summary>
/// The method that will build the device /// Build the device using the configuration
/// </summary> /// </summary>
/// <param name="dc">The device config</param> /// <param name="dc">The device config</param>
/// <returns>An instance of the device</returns> /// <returns>An instance of the device</returns>

View File

@@ -12,7 +12,7 @@ namespace PepperDash.Essentials.Core
public interface IHasFeedback : IKeyed public interface IHasFeedback : IKeyed
{ {
/// <summary> /// <summary>
/// This method shall return a list of all Output objects on a device, /// This method returns a list of all Output objects on a device,
/// including all "aggregate" devices. /// including all "aggregate" devices.
/// </summary> /// </summary>
FeedbackCollection<Feedback> Feedbacks { get; } FeedbackCollection<Feedback> Feedbacks { get; }

View File

@@ -1,8 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
@@ -17,48 +13,68 @@ namespace PepperDash.Essentials.Devices.Common
/// </summary> /// </summary>
public class GenericAudioOut : EssentialsDevice, IRoutingSink public class GenericAudioOut : EssentialsDevice, IRoutingSink
{ {
public RoutingInputPort CurrentInputPort => AnyAudioIn; /// <summary>
/// Gets the current input port
/// </summary>
public RoutingInputPort CurrentInputPort => AnyAudioIn;
public event SourceInfoChangeHandler CurrentSourceChange; /// <summary>
/// Event fired when the current source changes
/// </summary>
public event SourceInfoChangeHandler CurrentSourceChange;
public string CurrentSourceInfoKey { get; set; } /// <summary>
public SourceListItem CurrentSourceInfo /// Gets or sets the current source info key
{ /// </summary>
get public string CurrentSourceInfoKey { get; set; }
{ /// <summary>
return _CurrentSourceInfo; /// Gets or sets the current source info
} /// </summary>
set public SourceListItem CurrentSourceInfo
{ {
if (value == _CurrentSourceInfo) return; get
{
return _CurrentSourceInfo;
}
set
{
if (value == _CurrentSourceInfo) return;
var handler = CurrentSourceChange; var handler = CurrentSourceChange;
if (handler != null) if (handler != null)
handler(_CurrentSourceInfo, ChangeType.WillChange); handler(_CurrentSourceInfo, ChangeType.WillChange);
_CurrentSourceInfo = value; _CurrentSourceInfo = value;
if (handler != null) if (handler != null)
handler(_CurrentSourceInfo, ChangeType.DidChange); handler(_CurrentSourceInfo, ChangeType.DidChange);
} }
} }
SourceListItem _CurrentSourceInfo; SourceListItem _CurrentSourceInfo;
/// <summary> /// <summary>
/// Gets or sets the AnyAudioIn /// Gets or sets the AnyAudioIn
/// </summary> /// </summary>
public RoutingInputPort AnyAudioIn { get; private set; } public RoutingInputPort AnyAudioIn { get; private set; }
/// <summary>
/// Constructor for GenericAudioOut
/// </summary>
/// <param name="key">Device key</param>
/// <param name="name">Device name</param>
public GenericAudioOut(string key, string name) public GenericAudioOut(string key, string name)
: base(key, name) : base(key, name)
{ {
AnyAudioIn = new RoutingInputPort(RoutingPortNames.AnyAudioIn, eRoutingSignalType.Audio, AnyAudioIn = new RoutingInputPort(RoutingPortNames.AnyAudioIn, eRoutingSignalType.Audio,
eRoutingPortConnectionType.LineAudio, null, this); eRoutingPortConnectionType.LineAudio, null, this);
} }
#region IRoutingInputs Members #region IRoutingInputs Members
/// <summary>
/// Gets the collection of input ports
/// </summary>
public RoutingPortCollection<RoutingInputPort> InputPorts public RoutingPortCollection<RoutingInputPort> InputPorts
{ {
get { return new RoutingPortCollection<RoutingInputPort> { AnyAudioIn }; } get { return new RoutingPortCollection<RoutingInputPort> { AnyAudioIn }; }
@@ -68,23 +84,32 @@ namespace PepperDash.Essentials.Devices.Common
} }
/// <summary> /// <summary>
/// Represents a GenericAudioOutWithVolume /// Represents a GenericAudioOutWithVolume
/// </summary> /// </summary>
public class GenericAudioOutWithVolume : GenericAudioOut, IHasVolumeDevice public class GenericAudioOutWithVolume : GenericAudioOut, IHasVolumeDevice
{ {
/// <summary>
/// Gets the volume device key
/// </summary>
public string VolumeDeviceKey { get; private set; } public string VolumeDeviceKey { get; private set; }
/// <summary>
/// Gets the volume zone
/// </summary>
public uint VolumeZone { get; private set; } public uint VolumeZone { get; private set; }
/// <summary>
/// Gets the volume device
/// </summary>
public IBasicVolumeControls VolumeDevice public IBasicVolumeControls VolumeDevice
{ {
get get
{ {
var dev = DeviceManager.GetDeviceForKey(VolumeDeviceKey); var dev = DeviceManager.GetDeviceForKey(VolumeDeviceKey);
if (dev is IAudioZones) if (dev is IAudioZones)
return (dev as IAudioZones).Zone[VolumeZone]; return (dev as IAudioZones).Zone[VolumeZone];
else return dev as IBasicVolumeControls; else return dev as IBasicVolumeControls;
} }
} }
/// <summary> /// <summary>
@@ -103,24 +128,30 @@ namespace PepperDash.Essentials.Devices.Common
} }
public class GenericAudioOutWithVolumeFactory : EssentialsDeviceFactory<GenericAudioOutWithVolume> /// <summary>
{ /// Factory for creating GenericAudioOutWithVolume devices
public GenericAudioOutWithVolumeFactory() /// </summary>
{ public class GenericAudioOutWithVolumeFactory : EssentialsDeviceFactory<GenericAudioOutWithVolume>
TypeNames = new List<string>() { "genericaudiooutwithvolume" }; {
} /// <summary>
/// Constructor for GenericAudioOutWithVolumeFactory
/// </summary>
public GenericAudioOutWithVolumeFactory()
{
TypeNames = new List<string>() { "genericaudiooutwithvolume" };
}
/// <summary> /// <summary>
/// BuildDevice method /// BuildDevice method
/// </summary> /// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override EssentialsDevice BuildDevice(DeviceConfig dc) public override EssentialsDevice BuildDevice(DeviceConfig dc)
{ {
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new GenericAudioOutWithVolumeFactory Device"); Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new GenericAudioOutWithVolumeFactory Device");
var zone = dc.Properties.Value<uint>("zone"); var zone = dc.Properties.Value<uint>("zone");
return new GenericAudioOutWithVolume(dc.Key, dc.Name, return new GenericAudioOutWithVolume(dc.Key, dc.Name,
dc.Properties.Value<string>("volumeDeviceKey"), zone); dc.Properties.Value<string>("volumeDeviceKey"), zone);
} }
} }
} }

View File

@@ -1,18 +1,20 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Devices.Common.Codec; using PepperDash.Essentials.Devices.Common.Codec;
namespace PepperDash.Essentials.Devices.Common.AudioCodec namespace PepperDash.Essentials.Devices.Common.AudioCodec
{ {
/// <summary>
/// Abstract base class for audio codec devices
/// </summary>
public abstract class AudioCodecBase : EssentialsDevice, IHasDialer, IUsageTracking, IAudioCodecInfo public abstract class AudioCodecBase : EssentialsDevice, IHasDialer, IUsageTracking, IAudioCodecInfo
{ {
/// <summary>
/// Event fired when call status changes
/// </summary>
public event EventHandler<CodecCallStatusItemChangeEventArgs> CallStatusChange; public event EventHandler<CodecCallStatusItemChangeEventArgs> CallStatusChange;
/// <summary> /// <summary>
@@ -52,6 +54,11 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
/// </summary> /// </summary>
public List<CodecActiveCallItem> ActiveCalls { get; set; } public List<CodecActiveCallItem> ActiveCalls { get; set; }
/// <summary>
/// Constructor for AudioCodecBase
/// </summary>
/// <param name="key">Device key</param>
/// <param name="name">Device name</param>
public AudioCodecBase(string key, string name) public AudioCodecBase(string key, string name)
: base(key, name) : base(key, name)
{ {
@@ -70,11 +77,9 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
} }
/// <summary> /// <summary>
/// /// Handles call status change events
/// </summary> /// </summary>
/// <param name="previousStatus"></param> /// <param name="item">The call item that changed status</param>
/// <param name="newStatus"></param>
/// <param name="item"></param>
protected void OnCallStatusChange(CodecActiveCallItem item) protected void OnCallStatusChange(CodecActiveCallItem item)
{ {
var handler = CallStatusChange; var handler = CallStatusChange;
@@ -92,16 +97,22 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
#region IHasDialer Members #region IHasDialer Members
/// <inheritdoc />
public abstract void Dial(string number); public abstract void Dial(string number);
/// <inheritdoc />
public abstract void EndCall(CodecActiveCallItem activeCall); public abstract void EndCall(CodecActiveCallItem activeCall);
/// <inheritdoc />
public abstract void EndAllCalls(); public abstract void EndAllCalls();
/// <inheritdoc />
public abstract void AcceptCall(CodecActiveCallItem item); public abstract void AcceptCall(CodecActiveCallItem item);
/// <inheritdoc />
public abstract void RejectCall(CodecActiveCallItem item); public abstract void RejectCall(CodecActiveCallItem item);
/// <inheritdoc />
public abstract void SendDtmf(string digit); public abstract void SendDtmf(string digit);
#endregion #endregion

View File

@@ -1,16 +1,13 @@
using System; namespace PepperDash.Essentials.Devices.Common.AudioCodec
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Common.AudioCodec
{ {
/// <summary> /// <summary>
/// Implements a common set of data about a codec /// Implements a common set of data about a codec
/// </summary> /// </summary>
public interface IAudioCodecInfo public interface IAudioCodecInfo
{ {
/// <summary>
/// Gets the codec information
/// </summary>
AudioCodecInfo CodecInfo { get; } AudioCodecInfo CodecInfo { get; }
} }
@@ -19,6 +16,9 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
/// </summary> /// </summary>
public abstract class AudioCodecInfo public abstract class AudioCodecInfo
{ {
/// <summary>
/// Gets or sets the phone number
/// </summary>
public abstract string PhoneNumber { get; set; } public abstract string PhoneNumber { get; set; }
} }
} }

View File

@@ -1,23 +1,17 @@
using System; using PepperDash.Essentials.Core;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.AudioCodec namespace PepperDash.Essentials.Devices.Common.AudioCodec
{ {
/// <summary> /// <summary>
/// For rooms that have audio codec /// For rooms that have audio codec
/// </summary> /// </summary>
public interface IHasAudioCodec:IHasInCallFeedback public interface IHasAudioCodec : IHasInCallFeedback
{ {
/// <summary>
/// Gets the audio codec device
/// </summary>
AudioCodecBase AudioCodec { get; } AudioCodecBase AudioCodec { get; }
/// <summary>
/// Make this more specific
/// </summary>
//List<PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem> ActiveCalls { get; } //List<PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem> ActiveCalls { get; }
} }
} }

View File

@@ -1,8 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
@@ -17,6 +13,12 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
/// </summary> /// </summary>
public class MockAC : AudioCodecBase public class MockAC : AudioCodecBase
{ {
/// <summary>
/// Constructor for MockAC
/// </summary>
/// <param name="key">Device key</param>
/// <param name="name">Device name</param>
/// <param name="props">MockAC properties configuration</param>
public MockAC(string key, string name, MockAcPropertiesConfig props) public MockAC(string key, string name, MockAcPropertiesConfig props)
: base(key, name) : base(key, name)
{ {
@@ -109,13 +111,10 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
Debug.LogMessage(LogEventLevel.Debug, this, "BEEP BOOP SendDTMF: {0}", s); Debug.LogMessage(LogEventLevel.Debug, this, "BEEP BOOP SendDTMF: {0}", s);
} }
/// <summary>
///
/// </summary>
/// <param name="url"></param>
/// <summary> /// <summary>
/// TestIncomingAudioCall method /// TestIncomingAudioCall method
/// </summary> /// </summary>
/// <param name="number">Phone number to call from</param>
public void TestIncomingAudioCall(string number) public void TestIncomingAudioCall(string number)
{ {
Debug.LogMessage(LogEventLevel.Debug, this, "TestIncomingAudioCall from {0}", number); Debug.LogMessage(LogEventLevel.Debug, this, "TestIncomingAudioCall from {0}", number);
@@ -133,6 +132,7 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
{ {
string _phoneNumber; string _phoneNumber;
/// <inheritdoc />
public override string PhoneNumber public override string PhoneNumber
{ {
get get
@@ -151,6 +151,9 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
/// </summary> /// </summary>
public class MockACFactory : EssentialsDeviceFactory<MockAC> public class MockACFactory : EssentialsDeviceFactory<MockAC>
{ {
/// <summary>
/// Constructor for MockACFactory
/// </summary>
public MockACFactory() public MockACFactory()
{ {
TypeNames = new List<string>() { "mockac" }; TypeNames = new List<string>() { "mockac" };

View File

@@ -1,12 +1,4 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.AudioCodec namespace PepperDash.Essentials.Devices.Common.AudioCodec
{ {
@@ -15,10 +7,10 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
/// </summary> /// </summary>
public class MockAcPropertiesConfig public class MockAcPropertiesConfig
{ {
[JsonProperty("phoneNumber")]
/// <summary> /// <summary>
/// Gets or sets the PhoneNumber /// Gets or sets the PhoneNumber
/// </summary> /// </summary>
[JsonProperty("phoneNumber")]
public string PhoneNumber { get; set; } public string PhoneNumber { get; set; }
} }
} }

View File

@@ -3,20 +3,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using System.Reflection;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Core.Presets; using PepperDash.Essentials.Core.Presets;
using PepperDash.Essentials.Devices.Common.Codec;
using Newtonsoft.Json;
using Serilog.Events; using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Cameras namespace PepperDash.Essentials.Devices.Common.Cameras
@@ -26,31 +20,52 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// </summary> /// </summary>
public enum eCameraCapabilities public enum eCameraCapabilities
{ {
/// <summary>
/// No camera capabilities
/// </summary>
None = 0, None = 0,
/// <summary>
/// Camera supports pan movement
/// </summary>
Pan = 1, Pan = 1,
Tilt = 2, /// <summary>
/// Camera supports tilt movement
/// </summary>
Tilt = 2,
/// <summary>
/// Camera supports zoom functionality
/// </summary>
Zoom = 4, Zoom = 4,
/// <summary>
/// Camera supports focus adjustment
/// </summary>
Focus = 8 Focus = 8
} }
/// <summary>
/// Abstract base class for camera devices that provides common camera functionality and capabilities
/// </summary>
public abstract class CameraBase : ReconfigurableDevice, IRoutingOutputs public abstract class CameraBase : ReconfigurableDevice, IRoutingOutputs
{ {
[JsonProperty("controlMode", NullValueHandling = NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the ControlMode /// Gets or sets the ControlMode
/// </summary> /// </summary>
[JsonProperty("controlMode", NullValueHandling = NullValueHandling.Ignore)]
public eCameraControlMode ControlMode { get; protected set; } public eCameraControlMode ControlMode { get; protected set; }
#region IRoutingOutputs Members #region IRoutingOutputs Members
[JsonIgnore]
/// <summary> /// <summary>
/// Gets or sets the OutputPorts /// Gets or sets the OutputPorts
/// </summary> /// </summary>
[JsonIgnore]
public RoutingPortCollection<RoutingOutputPort> OutputPorts { get; protected set; } public RoutingPortCollection<RoutingOutputPort> OutputPorts { get; protected set; }
#endregion #endregion
/// <summary>
/// Gets a value indicating whether this camera supports pan movement
/// </summary>
[JsonProperty("canPan", NullValueHandling = NullValueHandling.Ignore)] [JsonProperty("canPan", NullValueHandling = NullValueHandling.Ignore)]
public bool CanPan public bool CanPan
{ {
@@ -59,6 +74,10 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
return (Capabilities & eCameraCapabilities.Pan) == eCameraCapabilities.Pan; return (Capabilities & eCameraCapabilities.Pan) == eCameraCapabilities.Pan;
} }
} }
/// <summary>
/// Gets a value indicating whether this camera supports tilt movement
/// </summary>
[JsonProperty("canTilt", NullValueHandling = NullValueHandling.Ignore)] [JsonProperty("canTilt", NullValueHandling = NullValueHandling.Ignore)]
public bool CanTilt public bool CanTilt
{ {
@@ -67,6 +86,10 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
return (Capabilities & eCameraCapabilities.Tilt) == eCameraCapabilities.Tilt; return (Capabilities & eCameraCapabilities.Tilt) == eCameraCapabilities.Tilt;
} }
} }
/// <summary>
/// Gets a value indicating whether this camera supports zoom functionality
/// </summary>
[JsonProperty("canZoom", NullValueHandling = NullValueHandling.Ignore)] [JsonProperty("canZoom", NullValueHandling = NullValueHandling.Ignore)]
public bool CanZoom public bool CanZoom
{ {
@@ -75,6 +98,10 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
return (Capabilities & eCameraCapabilities.Zoom) == eCameraCapabilities.Zoom; return (Capabilities & eCameraCapabilities.Zoom) == eCameraCapabilities.Zoom;
} }
} }
/// <summary>
/// Gets a value indicating whether this camera supports focus adjustment
/// </summary>
[JsonProperty("canFocus", NullValueHandling = NullValueHandling.Ignore)] [JsonProperty("canFocus", NullValueHandling = NullValueHandling.Ignore)]
public bool CanFocus public bool CanFocus
{ {
@@ -84,23 +111,42 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
} }
} }
// A bitmasked value to indicate the movement capabilites of this camera /// <summary>
/// Gets or sets a bitmasked value to indicate the movement capabilities of this camera
/// </summary>
protected eCameraCapabilities Capabilities { get; set; } protected eCameraCapabilities Capabilities { get; set; }
protected CameraBase(DeviceConfig config) : base(config) /// <summary>
{ /// Initializes a new instance of the CameraBase class with the specified device configuration
OutputPorts = new RoutingPortCollection<RoutingOutputPort>(); /// </summary>
/// <param name="config">The device configuration</param>
ControlMode = eCameraControlMode.Manual; protected CameraBase(DeviceConfig config) : base(config)
}
protected CameraBase(string key, string name) :
this (new DeviceConfig{Name = name, Key = key})
{ {
OutputPorts = new RoutingPortCollection<RoutingOutputPort>();
ControlMode = eCameraControlMode.Manual;
} }
/// <summary>
/// Initializes a new instance of the CameraBase class with the specified key and name
/// </summary>
/// <param name="key">The unique key for this camera device</param>
/// <param name="name">The friendly name for this camera device</param>
protected CameraBase(string key, string name) :
this(new DeviceConfig { Name = name, Key = key })
{
}
/// <summary>
/// Links the camera device to the API bridge for control and feedback
/// </summary>
/// <param name="cameraDevice">The camera device to link</param>
/// <param name="trilist">The trilist for communication</param>
/// <param name="joinStart">The starting join number for the camera controls</param>
/// <param name="joinMapKey">The join map key for custom join mappings</param>
/// <param name="bridge">The EiscApiAdvanced bridge for advanced join mapping</param>
protected void LinkCameraToApi(CameraBase cameraDevice, BasicTriList trilist, uint joinStart, string joinMapKey, protected void LinkCameraToApi(CameraBase cameraDevice, BasicTriList trilist, uint joinStart, string joinMapKey,
EiscApiAdvanced bridge) EiscApiAdvanced bridge)
{ {
@@ -240,13 +286,13 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
{ {
int tempNum = i; int tempNum = i;
trilist.SetSigTrueAction((ushort) (joinMap.PresetRecallStart.JoinNumber + tempNum), () => trilist.SetSigTrueAction((ushort)(joinMap.PresetRecallStart.JoinNumber + tempNum), () =>
{ {
presetsCamera.PresetSelect(tempNum); presetsCamera.PresetSelect(tempNum);
}); });
trilist.SetSigTrueAction((ushort) (joinMap.PresetSaveStart.JoinNumber + tempNum), () => trilist.SetSigTrueAction((ushort)(joinMap.PresetSaveStart.JoinNumber + tempNum), () =>
{ {
var label = trilist.GetString((ushort) (joinMap.PresetLabelStart.JoinNumber + tempNum)); var label = trilist.GetString((ushort)(joinMap.PresetLabelStart.JoinNumber + tempNum));
presetsCamera.PresetStore(tempNum, label); presetsCamera.PresetStore(tempNum, label);
}); });
@@ -277,7 +323,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
trilist.SetString((ushort)(joinMap.PresetLabelStart.JoinNumber + tempNum), label); trilist.SetString((ushort)(joinMap.PresetLabelStart.JoinNumber + tempNum), label);
} }
} }
} }
/// <summary> /// <summary>
@@ -285,6 +331,13 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// </summary> /// </summary>
public class CameraPreset : PresetBase public class CameraPreset : PresetBase
{ {
/// <summary>
/// Initializes a new instance of the CameraPreset class
/// </summary>
/// <param name="id">The preset ID</param>
/// <param name="description">The preset description</param>
/// <param name="isDefined">Whether the preset is defined</param>
/// <param name="isDefinable">Whether the preset can be defined</param>
public CameraPreset(int id, string description, bool isDefined, bool isDefinable) public CameraPreset(int id, string description, bool isDefined, bool isDefinable)
: base(id, description, isDefined, isDefinable) : base(id, description, isDefined, isDefinable)
{ {
@@ -293,37 +346,37 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
} }
/// <summary> /// <summary>
/// Represents a CameraPropertiesConfig /// Represents a CameraPropertiesConfig
/// </summary> /// </summary>
public class CameraPropertiesConfig public class CameraPropertiesConfig
{ {
/// <summary> /// <summary>
/// Gets or sets the CommunicationMonitorProperties /// Gets or sets the CommunicationMonitorProperties
/// </summary> /// </summary>
public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; }
/// <summary> /// <summary>
/// Gets or sets the Control /// Gets or sets the Control
/// </summary> /// </summary>
public ControlPropertiesConfig Control { get; set; } public ControlPropertiesConfig Control { get; set; }
[JsonProperty("supportsAutoMode")]
/// <summary> /// <summary>
/// Gets or sets the SupportsAutoMode /// Gets or sets the SupportsAutoMode
/// </summary> /// </summary>
[JsonProperty("supportsAutoMode")]
public bool SupportsAutoMode { get; set; } public bool SupportsAutoMode { get; set; }
[JsonProperty("supportsOffMode")]
/// <summary> /// <summary>
/// Gets or sets the SupportsOffMode /// Gets or sets the SupportsOffMode
/// </summary> /// </summary>
[JsonProperty("supportsOffMode")]
public bool SupportsOffMode { get; set; } public bool SupportsOffMode { get; set; }
[JsonProperty("presets")]
/// <summary> /// <summary>
/// Gets or sets the Presets /// Gets or sets the Presets
/// </summary> /// </summary>
[JsonProperty("presets")]
public List<CameraPreset> Presets { get; set; } public List<CameraPreset> Presets { get; set; }
} }
} }

View File

@@ -1,326 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Enum for camera control modes
/// </summary>
public enum eCameraControlMode
{
/// <summary>
/// Manual control mode, where the camera is controlled directly by the user or system
/// </summary>
Manual = 0,
/// <summary>
/// Off control mode, where the camera is turned off or disabled
/// </summary>
Off,
/// <summary>
/// Auto control mode, where the camera automatically adjusts settings based on the environment or conditions
/// </summary>
Auto
}
/// <summary>
/// Interface for devices that have cameras
/// </summary>
public interface IHasCameras : IKeyName
{
/// <summary>
/// Event that is raised when a camera is selected
/// </summary>
event EventHandler<CameraSelectedEventArgs> CameraSelected;
/// <summary>
/// List of cameras on the device. This should be a list of CameraBase objects
/// </summary>
List<CameraBase> Cameras { get; }
/// <summary>
/// The currently selected camera. This should be a CameraBase object
/// </summary>
CameraBase SelectedCamera { get; }
/// <summary>
/// Feedback that indicates the currently selected camera
/// </summary>
StringFeedback SelectedCameraFeedback { get; }
/// <summary>
/// Selects a camera from the list of available cameras based on the provided key.
/// </summary>
/// <param name="key">The unique identifier or name of the camera to select.</param>
void SelectCamera(string key);
}
/// <summary>
/// Defines the contract for IHasCodecCameras
/// </summary>
public interface IHasCodecCameras : IHasCameras, IHasFarEndCameraControl
{
}
/// <summary>
/// To be implmented on codecs that can disable their camera(s) to blank the near end video
/// </summary>
public interface IHasCameraOff
{
/// <summary>
/// Feedback that indicates whether the camera is off
/// </summary>
BoolFeedback CameraIsOffFeedback { get; }
/// <summary>
/// Turns the camera off, blanking the near end video
/// </summary>
void CameraOff();
}
/// <summary>
/// Describes the ability to mute and unmute camera video
/// </summary>
public interface IHasCameraMute
{
/// <summary>
/// Feedback that indicates whether the camera is muted
/// </summary>
BoolFeedback CameraIsMutedFeedback { get; }
/// <summary>
/// Mutes the camera video, preventing it from being sent to the far end
/// </summary>
void CameraMuteOn();
/// <summary>
/// Unmutes the camera video, allowing it to be sent to the far end
/// </summary>
void CameraMuteOff();
/// <summary>
/// Toggles the camera mute state. If the camera is muted, it will be unmuted, and vice versa.
/// </summary>
void CameraMuteToggle();
}
/// <summary>
/// Interface for devices that can mute and unmute their camera video, with an event for unmute requests
/// </summary>
public interface IHasCameraMuteWithUnmuteReqeust : IHasCameraMute
{
/// <summary>
/// Event that is raised when a video unmute is requested, typically by the far end
/// </summary>
event EventHandler VideoUnmuteRequested;
}
/// <summary>
/// Event arguments for the CameraSelected event
/// </summary>
public class CameraSelectedEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the SelectedCamera
/// </summary>
public CameraBase SelectedCamera { get; private set; }
/// <summary>
/// Constructor for CameraSelectedEventArgs
/// </summary>
/// <param name="camera"></param>
public CameraSelectedEventArgs(CameraBase camera)
{
SelectedCamera = camera;
}
}
/// <summary>
/// Interface for devices that have a far end camera control
/// </summary>
public interface IHasFarEndCameraControl
{
/// <summary>
/// Gets the far end camera, which is typically a CameraBase object that represents the camera at the far end of a call
/// </summary>
CameraBase FarEndCamera { get; }
/// <summary>
/// Feedback that indicates whether the far end camera is being controlled
/// </summary>
BoolFeedback ControllingFarEndCameraFeedback { get; }
}
/// <summary>
/// Defines the contract for IAmFarEndCamera
/// </summary>
public interface IAmFarEndCamera
{
}
/// <summary>
/// Interface for devices that have camera controls
/// </summary>
public interface IHasCameraControls
{
}
/// <summary>
/// Defines the contract for IHasCameraPtzControl
/// </summary>
public interface IHasCameraPtzControl : IHasCameraPanControl, IHasCameraTiltControl, IHasCameraZoomControl
{
/// <summary>
/// Resets the camera position
/// </summary>
void PositionHome();
}
/// <summary>
/// Interface for camera pan control
/// </summary>
public interface IHasCameraPanControl : IHasCameraControls
{
/// <summary>
/// Pans the camera left
/// </summary>
void PanLeft();
/// <summary>
/// Pans the camera right
/// </summary>
void PanRight();
/// <summary>
/// Stops the camera pan movement
/// </summary>
void PanStop();
}
/// <summary>
/// Defines the contract for IHasCameraTiltControl
/// </summary>
public interface IHasCameraTiltControl : IHasCameraControls
{
/// <summary>
/// Tilts the camera down
/// </summary>
void TiltDown();
/// <summary>
/// Tilts the camera up
/// </summary>
void TiltUp();
/// <summary>
/// Stops the camera tilt movement
/// </summary>
void TiltStop();
}
/// <summary>
/// Defines the contract for IHasCameraZoomControl
/// </summary>
public interface IHasCameraZoomControl : IHasCameraControls
{
/// <summary>
/// Zooms the camera in
/// </summary>
void ZoomIn();
/// <summary>
/// Zooms the camera out
/// </summary>
void ZoomOut();
/// <summary>
/// Stops the camera zoom movement
/// </summary>
void ZoomStop();
}
/// <summary>
/// Defines the contract for IHasCameraFocusControl
/// </summary>
public interface IHasCameraFocusControl : IHasCameraControls
{
/// <summary>
/// Focuses the camera near
/// </summary>
void FocusNear();
/// <summary>
/// Focuses the camera far
/// </summary>
void FocusFar();
/// <summary>
/// Stops the camera focus movement
/// </summary>
void FocusStop();
/// <summary>
/// Triggers the camera's auto focus functionality, if available.
/// </summary>
void TriggerAutoFocus();
}
/// <summary>
/// Interface for devices that have auto focus mode control
/// </summary>
public interface IHasAutoFocusMode
{
/// <summary>
/// Sets the focus mode to auto or manual, or toggles between them.
/// </summary>
void SetFocusModeAuto();
/// <summary>
/// Sets the focus mode to manual, allowing for manual focus adjustments.
/// </summary>
void SetFocusModeManual();
/// <summary>
/// Toggles the focus mode between auto and manual.
/// </summary>
void ToggleFocusMode();
}
/// <summary>
/// Interface for devices that have camera auto mode control
/// </summary>
public interface IHasCameraAutoMode : IHasCameraControls
{
/// <summary>
/// Enables or disables the camera's auto mode, which may include automatic adjustments for focus, exposure, and other settings.
/// </summary>
void CameraAutoModeOn();
/// <summary>
/// Disables the camera's auto mode, allowing for manual control of camera settings.
/// </summary>
void CameraAutoModeOff();
/// <summary>
/// Toggles the camera's auto mode state. If the camera is in auto mode, it will switch to manual mode, and vice versa.
/// </summary>
void CameraAutoModeToggle();
/// <summary>
/// Feedback that indicates whether the camera's auto mode is currently enabled.
/// </summary>
BoolFeedback CameraAutoModeIsOnFeedback { get; }
}
}

View File

@@ -3,38 +3,33 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Devices.Common.Codec;
using System.Text.RegularExpressions;
using System.Reflection;
using Newtonsoft.Json;
using Serilog.Events; using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Cameras namespace PepperDash.Essentials.Devices.Common.Cameras
{ {
/// <summary> /// <summary>
/// Represents a CameraVisca /// Represents a CameraVisca
/// </summary> /// </summary>
public class CameraVisca : CameraBase, IHasCameraPtzControl, ICommunicationMonitor, IHasCameraPresets, IHasPowerControlWithFeedback, IBridgeAdvanced, IHasCameraFocusControl, IHasAutoFocusMode public class CameraVisca : CameraBase, IHasCameraPtzControl, ICommunicationMonitor, IHasCameraPresets, IHasPowerControlWithFeedback, IBridgeAdvanced, IHasCameraFocusControl, IHasAutoFocusMode
{ {
private readonly CameraViscaPropertiesConfig PropertiesConfig; private readonly CameraViscaPropertiesConfig PropertiesConfig;
/// <summary> /// <summary>
/// Gets or sets the Communication /// Gets or sets the Communication
/// </summary> /// </summary>
public IBasicCommunication Communication { get; private set; } public IBasicCommunication Communication { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the CommunicationMonitor /// Gets or sets the CommunicationMonitor
/// </summary> /// </summary>
public StatusMonitorBase CommunicationMonitor { get; private set; } public StatusMonitorBase CommunicationMonitor { get; private set; }
/// <summary> /// <summary>
/// Used to store the actions to parse inquiry responses as the inquiries are sent /// Used to store the actions to parse inquiry responses as the inquiries are sent
@@ -45,20 +40,41 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// Camera ID (Default 1) /// Camera ID (Default 1)
/// </summary> /// </summary>
public byte ID = 0x01; public byte ID = 0x01;
/// <summary>
/// Response ID used for VISCA communication
/// </summary>
public byte ResponseID; public byte ResponseID;
/// <summary>
/// Slow speed value for pan movement
/// </summary>
public byte PanSpeedSlow = 0x10;
public byte PanSpeedSlow = 0x10; /// <summary>
public byte TiltSpeedSlow = 0x10; /// Slow speed value for tilt movement
/// </summary>
public byte TiltSpeedSlow = 0x10;
/// <summary>
/// Fast speed value for pan movement
/// </summary>
public byte PanSpeedFast = 0x13; public byte PanSpeedFast = 0x13;
/// <summary>
/// Fast speed value for tilt movement
/// </summary>
public byte TiltSpeedFast = 0x13; public byte TiltSpeedFast = 0x13;
// private bool IsMoving; // private bool IsMoving;
private bool IsZooming; private bool IsZooming;
bool _powerIsOn; bool _powerIsOn;
public bool PowerIsOn
/// <summary>
/// Gets or sets a value indicating whether the camera power is on
/// </summary>
public bool PowerIsOn
{ {
get get
{ {
@@ -87,12 +103,23 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
long FastSpeedHoldTimeMs = 2000; long FastSpeedHoldTimeMs = 2000;
byte[] IncomingBuffer = new byte[] { }; byte[] IncomingBuffer = new byte[] { };
public BoolFeedback PowerIsOnFeedback { get; private set; }
/// <summary>
/// Feedback indicating whether the camera power is on
/// </summary>
public BoolFeedback PowerIsOnFeedback { get; private set; }
/// <summary>
/// Initializes a new instance of the CameraVisca class
/// </summary>
/// <param name="key">The unique key for this camera device</param>
/// <param name="name">The friendly name for this camera device</param>
/// <param name="comm">The communication interface for VISCA protocol</param>
/// <param name="props">The camera properties configuration</param>
public CameraVisca(string key, string name, IBasicCommunication comm, CameraViscaPropertiesConfig props) : public CameraVisca(string key, string name, IBasicCommunication comm, CameraViscaPropertiesConfig props) :
base(key, name) base(key, name)
{ {
InquiryResponseQueue = new CrestronQueue<Action<byte[]>>(15); InquiryResponseQueue = new CrestronQueue<Action<byte[]>>(15);
Presets = props.Presets; Presets = props.Presets;
@@ -107,8 +134,8 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
OutputPorts.Add(new RoutingOutputPort("videoOut", eRoutingSignalType.Video, eRoutingPortConnectionType.None, null, this, true)); OutputPorts.Add(new RoutingOutputPort("videoOut", eRoutingSignalType.Video, eRoutingPortConnectionType.None, null, this, true));
// Default to all capabilties // Default to all capabilties
Capabilities = eCameraCapabilities.Pan | eCameraCapabilities.Tilt | eCameraCapabilities.Zoom | eCameraCapabilities.Focus; Capabilities = eCameraCapabilities.Pan | eCameraCapabilities.Tilt | eCameraCapabilities.Zoom | eCameraCapabilities.Focus;
Communication = comm; Communication = comm;
if (comm is ISocketStatus socket) if (comm is ISocketStatus socket)
{ {
@@ -121,19 +148,19 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
} }
Communication.BytesReceived += new EventHandler<GenericCommMethodReceiveBytesArgs>(Communication_BytesReceived); Communication.BytesReceived += new EventHandler<GenericCommMethodReceiveBytesArgs>(Communication_BytesReceived);
PowerIsOnFeedback = new BoolFeedback(() => { return PowerIsOn; }); PowerIsOnFeedback = new BoolFeedback("powerIsOn", () => { return PowerIsOn; });
CameraIsOffFeedback = new BoolFeedback(() => { return !PowerIsOn; }); CameraIsOffFeedback = new BoolFeedback("cameraIsOff", () => { return !PowerIsOn; });
if (props.CommunicationMonitorProperties != null) if (props.CommunicationMonitorProperties != null)
{ {
CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties);
} }
else else
{ {
CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 20000, 120000, 300000, "\x81\x09\x04\x00\xFF"); CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 20000, 120000, 300000, "\x81\x09\x04\x00\xFF");
} }
DeviceManager.AddDevice(CommunicationMonitor); DeviceManager.AddDevice(CommunicationMonitor);
} }
/// <summary> /// <summary>
@@ -165,57 +192,57 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
} }
} }
/// <summary> /// <summary>
/// CustomActivate method /// CustomActivate method
/// </summary> /// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override bool CustomActivate() public override bool CustomActivate()
{ {
Communication.Connect(); Communication.Connect();
CommunicationMonitor.StatusChange += (o, a) => { Debug.LogMessage(LogEventLevel.Verbose, this, "Communication monitor state: {0}", CommunicationMonitor.Status); };
CommunicationMonitor.Start();
CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), "con" + Key, "", ConsoleAccessLevelEnum.AccessOperator); CommunicationMonitor.StatusChange += (o, a) => { Debug.LogMessage(LogEventLevel.Verbose, this, "Communication monitor state: {0}", CommunicationMonitor.Status); };
return true; CommunicationMonitor.Start();
}
/// <summary>
/// LinkToApi method
/// </summary>
public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
{
LinkCameraToApi(this, trilist, joinStart, joinMapKey, bridge);
}
void Socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), "con" + Key, "", ConsoleAccessLevelEnum.AccessOperator);
{ return true;
Debug.LogMessage(LogEventLevel.Verbose, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString()); }
if (e.Client.IsConnected) /// <summary>
{ /// LinkToApi method
/// </summary>
} public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
else {
{ LinkCameraToApi(this, trilist, joinStart, joinMapKey, bridge);
}
} void Socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e)
} {
Debug.LogMessage(LogEventLevel.Verbose, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString());
void SendBytes(byte[] b) if (e.Client.IsConnected)
{ {
if (Debug.Level == 2) // This check is here to prevent following string format from building unnecessarily on level 0 or 1
Debug.LogMessage(LogEventLevel.Verbose, this, "Sending:{0}", ComTextHelper.GetEscapedText(b));
Communication.SendBytes(b); }
} else
{
void Communication_BytesReceived(object sender, GenericCommMethodReceiveBytesArgs e) }
{ }
void SendBytes(byte[] b)
{
if (Debug.Level == 2) // This check is here to prevent following string format from building unnecessarily on level 0 or 1
Debug.LogMessage(LogEventLevel.Verbose, this, "Sending:{0}", ComTextHelper.GetEscapedText(b));
Communication.SendBytes(b);
}
void Communication_BytesReceived(object sender, GenericCommMethodReceiveBytesArgs e)
{
var newBytes = new byte[IncomingBuffer.Length + e.Bytes.Length]; var newBytes = new byte[IncomingBuffer.Length + e.Bytes.Length];
try try
@@ -355,10 +382,10 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// <summary> /// <summary>
/// Sends a pan/tilt command. If the command is not for fastSpeed then it starts a timer to initiate fast speed. /// Sends a pan/tilt command. If the command is not for fastSpeed then it starts a timer to initiate fast speed.
/// </summary> /// </summary>
/// <param name="cmd"></param> /// <param name="cmd">The VISCA command to send</param>
/// <param name="fastSpeed"></param> /// <param name="fastSpeedEnabled">Whether fast speed is enabled for this command</param>
private void SendPanTiltCommand (byte[] cmd, bool fastSpeedEnabled) private void SendPanTiltCommand(byte[] cmd, bool fastSpeedEnabled)
{ {
SendBytes(GetPanTiltCommand(cmd, fastSpeedEnabled)); SendBytes(GetPanTiltCommand(cmd, fastSpeedEnabled));
if (!fastSpeedEnabled) if (!fastSpeedEnabled)
@@ -372,7 +399,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
SpeedTimer = new CTimer((o) => SendPanTiltCommand(GetPanTiltCommand(cmd, true), true), FastSpeedHoldTimeMs); SpeedTimer = new CTimer((o) => SendPanTiltCommand(GetPanTiltCommand(cmd, true), true), FastSpeedHoldTimeMs);
} }
} }
private void StopSpeedTimer() private void StopSpeedTimer()
{ {
@@ -381,7 +408,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
SpeedTimer.Stop(); SpeedTimer.Stop();
SpeedTimer.Dispose(); SpeedTimer.Dispose();
SpeedTimer = null; SpeedTimer = null;
} }
} }
/// <summary> /// <summary>
@@ -424,14 +451,14 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
InquiryResponseQueue.Enqueue(HandlePowerResponse); InquiryResponseQueue.Enqueue(HandlePowerResponse);
} }
/// <summary> /// <summary>
/// PowerOn method /// PowerOn method
/// </summary> /// </summary>
public void PowerOn() public void PowerOn()
{ {
SendBytes(new byte[] { ID, 0x01, 0x04, 0x00, 0x02, 0xFF }); SendBytes(new byte[] { ID, 0x01, 0x04, 0x00, 0x02, 0xFF });
SendPowerQuery(); SendPowerQuery();
} }
void HandlePowerResponse(byte[] response) void HandlePowerResponse(byte[] response)
{ {
@@ -450,12 +477,12 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
} }
} }
/// <summary> /// <summary>
/// PowerOff method /// PowerOff method
/// </summary> /// </summary>
public void PowerOff() public void PowerOff()
{ {
SendBytes(new byte[] {ID, 0x01, 0x04, 0x00, 0x03, 0xFF}); SendBytes(new byte[] { ID, 0x01, 0x04, 0x00, 0x03, 0xFF });
SendPowerQuery(); SendPowerQuery();
} }
@@ -470,22 +497,22 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
PowerOn(); PowerOn();
} }
/// <summary> /// <summary>
/// PanLeft method /// PanLeft method
/// </summary> /// </summary>
public void PanLeft() public void PanLeft()
{ {
SendPanTiltCommand(new byte[] {0x01, 0x03}, false); SendPanTiltCommand(new byte[] { 0x01, 0x03 }, false);
// IsMoving = true; // IsMoving = true;
} }
/// <summary> /// <summary>
/// PanRight method /// PanRight method
/// </summary> /// </summary>
public void PanRight() public void PanRight()
{ {
SendPanTiltCommand(new byte[] { 0x02, 0x03 }, false); SendPanTiltCommand(new byte[] { 0x02, 0x03 }, false);
// IsMoving = true; // IsMoving = true;
} }
/// <summary> /// <summary>
/// PanStop method /// PanStop method
/// </summary> /// </summary>
@@ -493,22 +520,22 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
{ {
Stop(); Stop();
} }
/// <summary> /// <summary>
/// TiltDown method /// TiltDown method
/// </summary> /// </summary>
public void TiltDown() public void TiltDown()
{ {
SendPanTiltCommand(new byte[] { 0x03, 0x02 }, false); SendPanTiltCommand(new byte[] { 0x03, 0x02 }, false);
// IsMoving = true; // IsMoving = true;
} }
/// <summary> /// <summary>
/// TiltUp method /// TiltUp method
/// </summary> /// </summary>
public void TiltUp() public void TiltUp()
{ {
SendPanTiltCommand(new byte[] { 0x03, 0x01 }, false); SendPanTiltCommand(new byte[] { 0x03, 0x01 }, false);
// IsMoving = true; // IsMoving = true;
} }
/// <summary> /// <summary>
/// TiltStop method /// TiltStop method
/// </summary> /// </summary>
@@ -517,28 +544,28 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
Stop(); Stop();
} }
private void SendZoomCommand (byte cmd) private void SendZoomCommand(byte cmd)
{ {
SendBytes(new byte[] {ID, 0x01, 0x04, 0x07, cmd, 0xFF} ); SendBytes(new byte[] { ID, 0x01, 0x04, 0x07, cmd, 0xFF });
} }
/// <summary> /// <summary>
/// ZoomIn method /// ZoomIn method
/// </summary> /// </summary>
public void ZoomIn() public void ZoomIn()
{ {
SendZoomCommand(ZoomInCmd); SendZoomCommand(ZoomInCmd);
IsZooming = true; IsZooming = true;
} }
/// <summary> /// <summary>
/// ZoomOut method /// ZoomOut method
/// </summary> /// </summary>
public void ZoomOut() public void ZoomOut()
{ {
SendZoomCommand(ZoomOutCmd); SendZoomCommand(ZoomOutCmd);
IsZooming = true; IsZooming = true;
} }
/// <summary> /// <summary>
/// ZoomStop method /// ZoomStop method
/// </summary> /// </summary>
@@ -547,23 +574,23 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
Stop(); Stop();
} }
/// <summary> /// <summary>
/// Stop method /// Stop method
/// </summary> /// </summary>
public void Stop() public void Stop()
{ {
if (IsZooming) if (IsZooming)
{ {
SendZoomCommand(ZoomStopCmd); SendZoomCommand(ZoomStopCmd);
IsZooming = false; IsZooming = false;
} }
else else
{ {
StopSpeedTimer(); StopSpeedTimer();
SendPanTiltCommand(new byte[] { 0x03, 0x03 }, false); SendPanTiltCommand(new byte[] { 0x03, 0x03 }, false);
// IsMoving = false; // IsMoving = false;
} }
} }
/// <summary> /// <summary>
/// PositionHome method /// PositionHome method
/// </summary> /// </summary>
@@ -572,33 +599,39 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
SendBytes(new byte[] { ID, 0x01, 0x06, 0x02, PanSpeedFast, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }); SendBytes(new byte[] { ID, 0x01, 0x06, 0x02, PanSpeedFast, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF });
SendBytes(new byte[] { ID, 0x01, 0x04, 0x47, 0x00, 0x00, 0x00, 0x00, 0xFF }); SendBytes(new byte[] { ID, 0x01, 0x04, 0x47, 0x00, 0x00, 0x00, 0x00, 0xFF });
} }
/// <summary> /// <summary>
/// RecallPreset method /// RecallPreset method
/// </summary> /// </summary>
public void RecallPreset(int presetNumber) public void RecallPreset(int presetNumber)
{ {
SendBytes(new byte[] {ID, 0x01, 0x04, 0x3F, 0x02, (byte)presetNumber, 0xFF} ); SendBytes(new byte[] { ID, 0x01, 0x04, 0x3F, 0x02, (byte)presetNumber, 0xFF });
} }
/// <summary> /// <summary>
/// SavePreset method /// SavePreset method
/// </summary> /// </summary>
public void SavePreset(int presetNumber) public void SavePreset(int presetNumber)
{ {
SendBytes(new byte[] { ID, 0x01, 0x04, 0x3F, 0x01, (byte)presetNumber, 0xFF }); SendBytes(new byte[] { ID, 0x01, 0x04, 0x3F, 0x01, (byte)presetNumber, 0xFF });
} }
#region IHasCameraPresets Members #region IHasCameraPresets Members
/// <summary>
/// Event that is raised when the presets list has changed
/// </summary>
public event EventHandler<EventArgs> PresetsListHasChanged; public event EventHandler<EventArgs> PresetsListHasChanged;
protected void OnPresetsListHasChanged() /// <summary>
{ /// Raises the PresetsListHasChanged event
var handler = PresetsListHasChanged; /// </summary>
if (handler == null) protected void OnPresetsListHasChanged()
return; {
var handler = PresetsListHasChanged;
if (handler == null)
return;
handler.Invoke(this, EventArgs.Empty); handler.Invoke(this, EventArgs.Empty);
} }
/// <summary> /// <summary>
/// Gets or sets the Presets /// Gets or sets the Presets
@@ -741,6 +774,9 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// </summary> /// </summary>
public class CameraViscaFactory : EssentialsDeviceFactory<CameraVisca> public class CameraViscaFactory : EssentialsDeviceFactory<CameraVisca>
{ {
/// <summary>
/// Initializes a new instance of the CameraViscaFactory class
/// </summary>
public CameraViscaFactory() public CameraViscaFactory()
{ {
TypeNames = new List<string>() { "cameravisca" }; TypeNames = new List<string>() { "cameravisca" };
@@ -768,11 +804,9 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
{ {
/// <summary> /// <summary>
/// Control ID of the camera (1-7) /// Control ID of the camera (1-7)
/// </summary>
[JsonProperty("id")]
/// <summary>
/// Gets or sets the Id /// Gets or sets the Id
/// </summary> /// </summary>
[JsonProperty("id")]
public uint Id { get; set; } public uint Id { get; set; }
/// <summary> /// <summary>
@@ -790,7 +824,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// <summary> /// <summary>
/// Slow tilt speed (0-18) /// Slow tilt speed (0-18)
/// </summary> /// </summary>
[JsonProperty("tiltSpeedSlow")] [JsonProperty("tiltSpeedSlow")]
public uint TiltSpeedSlow { get; set; } public uint TiltSpeedSlow { get; set; }
/// <summary> /// <summary>

View File

@@ -1,8 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Common.Cameras namespace PepperDash.Essentials.Devices.Common.Cameras
{ {
@@ -11,12 +8,27 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// </summary> /// </summary>
public interface IHasCameraPresets public interface IHasCameraPresets
{ {
/// <summary>
/// Event that is raised when the presets list has changed
/// </summary>
event EventHandler<EventArgs> PresetsListHasChanged; event EventHandler<EventArgs> PresetsListHasChanged;
/// <summary>
/// Gets the list of camera presets
/// </summary>
List<CameraPreset> Presets { get; } List<CameraPreset> Presets { get; }
/// <summary>
/// Selects the specified preset
/// </summary>
/// <param name="preset">The preset number to select</param>
void PresetSelect(int preset); void PresetSelect(int preset);
/// <summary>
/// Stores a preset at the specified location with the given description
/// </summary>
/// <param name="preset">The preset number to store</param>
/// <param name="description">The description for the preset</param>
void PresetStore(int preset, string description); void PresetStore(int preset, string description);
} }
} }

View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Event arguments for the CameraSelected event
/// </summary>
[Obsolete("Use CameraSelectedEventArgs<T> instead. This class will be removed in a future version")]
public class CameraSelectedEventArgs : EventArgs
{
/// Gets or sets the SelectedCamera
/// </summary>
public CameraBase SelectedCamera { get; private set; }
/// <summary>
/// Constructor for CameraSelectedEventArgs
/// </summary>
/// <param name="camera"></param>
public CameraSelectedEventArgs(CameraBase camera)
{
SelectedCamera = camera;
}
}
/// <summary>
/// Event arguments for the CameraSelected event
/// </summary>
/// <typeparam name="T"></typeparam>
public class CameraSelectedEventArgs<T> : EventArgs
{
/// <summary>
/// Gets or sets the SelectedCamera
/// </summary>
public T SelectedCamera { get; private set; }
/// <summary>
/// Constructor for CameraSelectedEventArgs
/// </summary>
/// <param name="camera"></param>
public CameraSelectedEventArgs(T camera)
{
SelectedCamera = camera;
}
}
}

View File

@@ -0,0 +1,12 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Defines the contract for IAmFarEndCamera
/// </summary>
public interface IAmFarEndCamera : IKeyName
{
}
}

View File

@@ -0,0 +1,86 @@
using Newtonsoft.Json;
using PepperDash.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for camera capabilities
/// </summary>
public interface ICameraCapabilities: IKeyName
{
/// <summary>
/// Indicates whether the camera can pan
/// </summary>
[JsonProperty("canPan", NullValueHandling = NullValueHandling.Ignore)]
bool CanPan { get; }
/// <summary>
/// Indicates whether the camera can tilt
/// </summary>
[JsonProperty("canTilt", NullValueHandling = NullValueHandling.Ignore)]
bool CanTilt { get; }
/// <summary>
/// Indicates whether the camera can zoom
/// </summary>
[JsonProperty("canZoom", NullValueHandling = NullValueHandling.Ignore)]
bool CanZoom { get; }
/// <summary>
/// Indicates whether the camera can focus
/// </summary>
[JsonProperty("canFocus", NullValueHandling = NullValueHandling.Ignore)]
bool CanFocus { get; }
}
/// <summary>
/// Indicates the capabilities of a camera
/// </summary>
public class CameraCapabilities : ICameraCapabilities
{
/// <summary>
/// Unique Key
/// </summary>
[JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)]
public string Key { get; set; }
/// <summary>
/// Isn't it obvious :)
/// </summary>
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
/// <summary>
/// Indicates whether the camera can pan
/// </summary>
[JsonProperty("canPan", NullValueHandling = NullValueHandling.Ignore)]
public bool CanPan { get; set; }
/// <summary>
/// Indicates whether the camera can tilt
/// </summary>
[JsonProperty("canTilt", NullValueHandling = NullValueHandling.Ignore)]
public bool CanTilt { get; set; }
/// <summary>
/// Indicates whether the camera can zoom
/// </summary>
[JsonProperty("canZoom", NullValueHandling = NullValueHandling.Ignore)]
public bool CanZoom { get; set; }
/// <summary>
/// Indicates whether the camera can focus
/// </summary>
[JsonProperty("canFocus", NullValueHandling = NullValueHandling.Ignore)]
public bool CanFocus { get; set; }
}
}

View File

@@ -0,0 +1,24 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for devices that have auto focus mode control
/// </summary>
public interface IHasAutoFocusMode : IHasCameraControls
{
/// <summary>
/// Sets the focus mode to auto or manual, or toggles between them.
/// </summary>
void SetFocusModeAuto();
/// <summary>
/// Sets the focus mode to manual, allowing for manual focus adjustments.
/// </summary>
void SetFocusModeManual();
/// <summary>
/// Toggles the focus mode between auto and manual.
/// </summary>
void ToggleFocusMode();
}
}

View File

@@ -0,0 +1,31 @@
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for devices that have camera auto mode control
/// </summary>
public interface IHasCameraAutoMode : IHasCameraControls
{
/// <summary>
/// Enables or disables the camera's auto mode, which may include automatic adjustments for focus, exposure, and other settings.
/// </summary>
void CameraAutoModeOn();
/// <summary>
/// Disables the camera's auto mode, allowing for manual control of camera settings.
/// </summary>
void CameraAutoModeOff();
/// <summary>
/// Toggles the camera's auto mode state. If the camera is in auto mode, it will switch to manual mode, and vice versa.
/// </summary>
void CameraAutoModeToggle();
/// <summary>
/// Feedback that indicates whether the camera's auto mode is currently enabled.
/// </summary>
BoolFeedback CameraAutoModeIsOnFeedback { get; }
}
}

View File

@@ -0,0 +1,13 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for devices that have camera controls
/// </summary>
public interface IHasCameraControls : IKeyName
{
}
}

View File

@@ -0,0 +1,29 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Defines the contract for IHasCameraFocusControl
/// </summary>
public interface IHasCameraFocusControl : IHasCameraControls
{
/// <summary>
/// Focuses the camera near
/// </summary>
void FocusNear();
/// <summary>
/// Focuses the camera far
/// </summary>
void FocusFar();
/// <summary>
/// Stops the camera focus movement
/// </summary>
void FocusStop();
/// <summary>
/// Triggers the camera's auto focus functionality, if available.
/// </summary>
void TriggerAutoFocus();
}
}

View File

@@ -0,0 +1,31 @@
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Describes the ability to mute and unmute camera video
/// </summary>
public interface IHasCameraMute : IKeyName
{
/// <summary>
/// Feedback that indicates whether the camera is muted
/// </summary>
BoolFeedback CameraIsMutedFeedback { get; }
/// <summary>
/// Mutes the camera video, preventing it from being sent to the far end
/// </summary>
void CameraMuteOn();
/// <summary>
/// Unmutes the camera video, allowing it to be sent to the far end
/// </summary>
void CameraMuteOff();
/// <summary>
/// Toggles the camera mute state. If the camera is muted, it will be unmuted, and vice versa.
/// </summary>
void CameraMuteToggle();
}
}

View File

@@ -0,0 +1,16 @@
using System;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for devices that can mute and unmute their camera video, with an event for unmute requests
/// </summary>
public interface IHasCameraMuteWithUnmuteReqeust : IHasCameraMute
{
/// <summary>
/// Event that is raised when a video unmute is requested, typically by the far end
/// </summary>
event EventHandler VideoUnmuteRequested;
}
}

View File

@@ -0,0 +1,27 @@
using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// To be implmented on codecs that can disable their camera(s) to blank the near end video
/// </summary>
public interface IHasCameraOff : IHasCameraControls
{
/// <summary>
/// Feedback that indicates whether the camera is off
/// </summary>
BoolFeedback CameraIsOffFeedback { get; }
/// <summary>
/// Turns the camera off, blanking the near end video
/// </summary>
void CameraOff();
}
}

View File

@@ -0,0 +1,24 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for camera pan control
/// </summary>
public interface IHasCameraPanControl : IHasCameraControls
{
/// <summary>
/// Pans the camera left
/// </summary>
void PanLeft();
/// <summary>
/// Pans the camera right
/// </summary>
void PanRight();
/// <summary>
/// Stops the camera pan movement
/// </summary>
void PanStop();
}
}

View File

@@ -0,0 +1,14 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Defines the contract for IHasCameraPtzControl
/// </summary>
public interface IHasCameraPtzControl : IHasCameraPanControl, IHasCameraTiltControl, IHasCameraZoomControl
{
/// <summary>
/// Resets the camera position
/// </summary>
void PositionHome();
}
}

View File

@@ -0,0 +1,24 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Defines the contract for IHasCameraTiltControl
/// </summary>
public interface IHasCameraTiltControl : IHasCameraControls
{
/// <summary>
/// Tilts the camera down
/// </summary>
void TiltDown();
/// <summary>
/// Tilts the camera up
/// </summary>
void TiltUp();
/// <summary>
/// Stops the camera tilt movement
/// </summary>
void TiltStop();
}
}

View File

@@ -0,0 +1,24 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Defines the contract for IHasCameraZoomControl
/// </summary>
public interface IHasCameraZoomControl : IHasCameraControls
{
/// <summary>
/// Zooms the camera in
/// </summary>
void ZoomIn();
/// <summary>
/// Zooms the camera out
/// </summary>
void ZoomOut();
/// <summary>
/// Stops the camera zoom movement
/// </summary>
void ZoomStop();
}
}

View File

@@ -0,0 +1,41 @@
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for devices that have cameras
/// </summary>
[Obsolete("Use IHasCamerasWithControls instead. This interface will be removed in a future version")]
public interface IHasCameras : IKeyName
{
/// <summary>
/// Event that is raised when a camera is selected
/// </summary>
event EventHandler<CameraSelectedEventArgs> CameraSelected;
/// <summary>
/// List of cameras on the device. This should be a list of CameraBase objects
/// </summary>
List<CameraBase> Cameras { get; }
/// <summary>
/// The currently selected camera. This should be a CameraBase object
/// </summary>
CameraBase SelectedCamera { get; }
/// <summary>
/// Feedback that indicates the currently selected camera
/// </summary>
StringFeedback SelectedCameraFeedback { get; }
/// <summary>
/// Selects a camera from the list of available cameras based on the provided key.
/// </summary>
/// <param name="key">The unique identifier or name of the camera to select.</param>
void SelectCamera(string key);
}
}

View File

@@ -0,0 +1,40 @@
using PepperDash.Core;
using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for devices that have cameras with controls
/// </summary>
public interface IHasCamerasWithControls : IKeyName, IKeyed
{
/// <summary>
/// List of cameras on the device. This should be a list of IHasCameraControls objects
/// </summary>
List<IHasCameraControls> Cameras { get; }
/// <summary>
/// The currently selected camera. This should be an IHasCameraControls object
/// </summary>
IHasCameraControls SelectedCamera { get; }
/// <summary>
/// Feedback that indicates the currently selected camera
/// </summary>
StringFeedback SelectedCameraFeedback { get; }
/// <summary>
/// Event that is raised when a camera is selected
/// </summary>
event EventHandler<CameraSelectedEventArgs<IHasCameraControls>> CameraSelected;
/// <summary>
/// Selects a camera from the list of available cameras based on the provided key.
/// </summary>
/// <param name="key"></param>
void SelectCamera(string key);
}
}

View File

@@ -0,0 +1,13 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Defines the contract for IHasCodecCameras
/// </summary>
public interface IHasCodecCameras : IHasCameras, IHasFarEndCameraControl
{
}
}

View File

@@ -0,0 +1,23 @@
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for devices that have a far end camera control
/// </summary>
public interface IHasFarEndCameraControl : IKeyName
{
/// <summary>
/// Gets the far end camera, which is typically a CameraBase object that represents the camera at the far end of a call
/// </summary>
CameraBase FarEndCamera { get; }
/// <summary>
/// Feedback that indicates whether the far end camera is being controlled
/// </summary>
BoolFeedback ControllingFarEndCameraFeedback { get; }
}
}

View File

@@ -0,0 +1,24 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Enum for camera control modes
/// </summary>
public enum eCameraControlMode
{
/// <summary>
/// Manual control mode, where the camera is controlled directly by the user or system
/// </summary>
Manual = 0,
/// <summary>
/// Off control mode, where the camera is turned off or disabled
/// </summary>
Off,
/// <summary>
/// Auto control mode, where the camera automatically adjusts settings based on the environment or conditions
/// </summary>
Auto
}
}

View File

@@ -0,0 +1,26 @@
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a Call
/// </summary>
public class Call
{
/// <summary>
/// Gets or sets the Number
/// </summary>
public string Number { get; set; }
/// <summary>
/// Gets or sets the Protocol
/// </summary>
public string Protocol { get; set; }
/// <summary>
/// Gets or sets the CallRate
/// </summary>
public string CallRate { get; set; }
/// <summary>
/// Gets or sets the CallType
/// </summary>
public string CallType { get; set; }
}
}

View File

@@ -1,10 +1,5 @@
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Codec.Cisco namespace PepperDash.Essentials.Devices.Common.Codec.Cisco
{ {

View File

@@ -1,10 +1,5 @@
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Codec.Cisco namespace PepperDash.Essentials.Devices.Common.Codec.Cisco
{ {

View File

@@ -1,10 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
@@ -17,55 +13,55 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
public class CodecActiveCallItem public class CodecActiveCallItem
{ {
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the Name /// Gets or sets the Name
/// </summary> /// </summary>
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; } public string Name { get; set; }
[JsonProperty("number", NullValueHandling = NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the Number /// Gets or sets the Number
/// </summary> /// </summary>
[JsonProperty("number", NullValueHandling = NullValueHandling.Ignore)]
public string Number { get; set; } public string Number { get; set; }
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
/// <summary> /// <summary>
/// Gets or sets the Type /// Gets or sets the Type
/// </summary> /// </summary>
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
public eCodecCallType Type { get; set; } public eCodecCallType Type { get; set; }
[JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
/// <summary> /// <summary>
/// Gets or sets the Status /// Gets or sets the Status
/// </summary> /// </summary>
[JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
public eCodecCallStatus Status { get; set; } public eCodecCallStatus Status { get; set; }
[JsonProperty("direction", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
/// <summary> /// <summary>
/// Gets or sets the Direction /// Gets or sets the Direction
/// </summary> /// </summary>
[JsonProperty("direction", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
public eCodecCallDirection Direction { get; set; } public eCodecCallDirection Direction { get; set; }
[JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the Id /// Gets or sets the Id
/// </summary> /// </summary>
[JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
public string Id { get; set; } public string Id { get; set; }
[JsonProperty("isOnHold", NullValueHandling = NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the IsOnHold /// Gets or sets the IsOnHold
/// </summary> /// </summary>
[JsonProperty("isOnHold", NullValueHandling = NullValueHandling.Ignore)]
public bool IsOnHold { get; set; } public bool IsOnHold { get; set; }
[JsonProperty("duration", NullValueHandling = NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the Duration /// Gets or sets the Duration
/// </summary> /// </summary>
[JsonProperty("duration", NullValueHandling = NullValueHandling.Ignore)]
public TimeSpan Duration { get; set; } public TimeSpan Duration { get; set; }
//public object CallMetaData { get; set; } //public object CallMetaData { get; set; }
@@ -81,7 +77,7 @@ namespace PepperDash.Essentials.Devices.Common.Codec
{ {
return !(Status == eCodecCallStatus.Disconnected return !(Status == eCodecCallStatus.Disconnected
|| Status == eCodecCallStatus.Disconnecting || Status == eCodecCallStatus.Disconnecting
|| Status == eCodecCallStatus.Idle || Status == eCodecCallStatus.Idle
|| Status == eCodecCallStatus.Unknown); || Status == eCodecCallStatus.Unknown);
} }
} }
@@ -97,6 +93,10 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
public CodecActiveCallItem CallItem { get; private set; } public CodecActiveCallItem CallItem { get; private set; }
/// <summary>
/// Initializes a new instance of the CodecCallStatusItemChangeEventArgs class
/// </summary>
/// <param name="item">The call item that changed</param>
public CodecCallStatusItemChangeEventArgs(CodecActiveCallItem item) public CodecCallStatusItemChangeEventArgs(CodecActiveCallItem item)
{ {
CallItem = item; CallItem = item;

View File

@@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a codec directory
/// </summary>
public class CodecDirectory
{
/// <summary>
/// Represents the contents of the directory
/// We don't want to serialize this for messages to MobileControl. MC can combine Contacts and Folders to get the same data
/// </summary>
[JsonIgnore]
public List<DirectoryItem> CurrentDirectoryResults { get; private set; }
/// <summary>
/// Gets the Contacts in the CurrentDirectoryResults
/// </summary>
[JsonProperty("contacts")]
public List<DirectoryItem> Contacts
{
get
{
return CurrentDirectoryResults.OfType<DirectoryContact>().Cast<DirectoryItem>().ToList();
}
}
/// <summary>
/// Gets the Folders in the CurrentDirectoryResults
/// </summary>
[JsonProperty("folders")]
public List<DirectoryItem> Folders
{
get
{
return CurrentDirectoryResults.OfType<DirectoryFolder>().Cast<DirectoryItem>().ToList();
}
}
/// <summary>
/// Used to store the ID of the current folder for CurrentDirectoryResults
/// Gets or sets the ResultsFolderId
/// </summary>
[JsonProperty("resultsFolderId")]
public string ResultsFolderId { get; set; }
/// <summary>
/// Constructor for <see cref="CodecDirectory"/>
/// </summary>
public CodecDirectory()
{
CurrentDirectoryResults = new List<DirectoryItem>();
}
/// <summary>
/// Adds folders to the directory
/// </summary>
/// <param name="folders"></param>
public void AddFoldersToDirectory(List<DirectoryItem> folders)
{
if (folders != null)
CurrentDirectoryResults.AddRange(folders);
SortDirectory();
}
/// <summary>
/// Adds contacts to the directory
/// </summary>
/// <param name="contacts"></param>
public void AddContactsToDirectory(List<DirectoryItem> contacts)
{
if (contacts != null)
CurrentDirectoryResults.AddRange(contacts);
SortDirectory();
}
/// <summary>
/// Filters the CurrentDirectoryResults by the predicate
/// </summary>
/// <param name="predicate"></param>
public void FilterContacts(Func<DirectoryItem, bool> predicate)
{
CurrentDirectoryResults = CurrentDirectoryResults.Where(predicate).ToList();
}
/// <summary>
/// Sorts the DirectoryResults list to display all folders alphabetically, then all contacts alphabetically
/// </summary>
private void SortDirectory()
{
var sortedFolders = new List<DirectoryItem>();
sortedFolders.AddRange(CurrentDirectoryResults.Where(f => f is DirectoryFolder));
sortedFolders.OrderBy(f => f.Name);
var sortedContacts = new List<DirectoryItem>();
sortedContacts.AddRange(CurrentDirectoryResults.Where(c => c is DirectoryContact));
sortedFolders.OrderBy(c => c.Name);
CurrentDirectoryResults.Clear();
CurrentDirectoryResults.AddRange(sortedFolders);
CurrentDirectoryResults.AddRange(sortedContacts);
}
}
}

View File

@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a CodecScheduleAwareness
/// </summary>
public class CodecScheduleAwareness
{
List<Meeting> _meetings;
/// <summary>
/// Event that is raised when a meeting event changes
/// </summary>
public event EventHandler<MeetingEventArgs> MeetingEventChange;
/// <summary>
/// Event that is raised when the meetings list has changed
/// </summary>
public event EventHandler<EventArgs> MeetingsListHasChanged;
private int _meetingWarningMinutes = 5;
//private Meeting _previousChangedMeeting;
//private eMeetingEventChangeType _previousChangeType = eMeetingEventChangeType.Unknown;
/// <summary>
/// Gets or sets the number of minutes before a meeting to issue a warning
/// </summary>
public int MeetingWarningMinutes
{
get { return _meetingWarningMinutes; }
set { _meetingWarningMinutes = value; }
}
/// <summary>
/// Setter triggers MeetingsListHasChanged event
/// </summary>
public List<Meeting> Meetings
{
get
{
return _meetings;
}
set
{
_meetings = value;
MeetingsListHasChanged?.Invoke(this, new EventArgs());
}
}
private readonly CTimer _scheduleChecker;
/// <summary>
/// Initializes a new instance of the CodecScheduleAwareness class with default poll time
/// </summary>
public CodecScheduleAwareness()
{
Meetings = new List<Meeting>();
_scheduleChecker = new CTimer(CheckSchedule, null, 1000, 1000);
}
/// <summary>
/// Initializes a new instance of the CodecScheduleAwareness class with specified poll time
/// </summary>
/// <param name="pollTime">The poll time in milliseconds for checking schedule changes</param>
public CodecScheduleAwareness(long pollTime)
{
Meetings = new List<Meeting>();
_scheduleChecker = new CTimer(CheckSchedule, null, pollTime, pollTime);
}
/// <summary>
/// Helper method to fire MeetingEventChange. Should only fire once for each changeType on each meeting
/// </summary>
/// <param name="changeType"></param>
/// <param name="meeting"></param>
private void OnMeetingChange(eMeetingEventChangeType changeType, Meeting meeting)
{
Debug.LogMessage(LogEventLevel.Verbose, "*****************OnMeetingChange. id: {0} changeType: {1}**********************", meeting.Id, changeType);
if (changeType != (changeType & meeting.NotifiedChangeTypes))
{
// Add this change type to the NotifiedChangeTypes
meeting.NotifiedChangeTypes |= changeType;
MeetingEventChange?.Invoke(this, new MeetingEventArgs() { ChangeType = changeType, Meeting = meeting });
}
else
{
Debug.LogMessage(LogEventLevel.Verbose, "Meeting: {0} already notified of changeType: {1}", meeting.Id, changeType);
}
}
/// <summary>
/// Checks the schedule to see if any MeetingEventChange updates should be fired
/// </summary>
/// <param name="o"></param>
private void CheckSchedule(object o)
{
// Iterate the meeting list and check if any meeting need to do anything
const double meetingTimeEpsilon = 0.05;
foreach (var m in Meetings)
{
var changeType = eMeetingEventChangeType.Unknown;
if (eMeetingEventChangeType.MeetingStartWarning != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingStartWarning) && m.TimeToMeetingStart.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes && m.TimeToMeetingStart.Seconds > 0) // Meeting is about to start
{
Debug.LogMessage(LogEventLevel.Verbose, "********************* MeetingStartWarning. TotalMinutes: {0} Seconds: {1}", m.TimeToMeetingStart.TotalMinutes, m.TimeToMeetingStart.Seconds);
changeType = eMeetingEventChangeType.MeetingStartWarning;
}
else if (eMeetingEventChangeType.MeetingStart != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingStart) && Math.Abs(m.TimeToMeetingStart.TotalMinutes) < meetingTimeEpsilon) // Meeting Start
{
Debug.LogMessage(LogEventLevel.Verbose, "********************* MeetingStart");
changeType = eMeetingEventChangeType.MeetingStart;
}
else if (eMeetingEventChangeType.MeetingEndWarning != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingEndWarning) && m.TimeToMeetingEnd.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes && m.TimeToMeetingEnd.Seconds > 0) // Meeting is about to end
{
Debug.LogMessage(LogEventLevel.Verbose, "********************* MeetingEndWarning. TotalMinutes: {0} Seconds: {1}", m.TimeToMeetingEnd.TotalMinutes, m.TimeToMeetingEnd.Seconds);
changeType = eMeetingEventChangeType.MeetingEndWarning;
}
else if (eMeetingEventChangeType.MeetingEnd != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingEnd) && Math.Abs(m.TimeToMeetingEnd.TotalMinutes) < meetingTimeEpsilon) // Meeting has ended
{
Debug.LogMessage(LogEventLevel.Verbose, "********************* MeetingEnd");
changeType = eMeetingEventChangeType.MeetingEnd;
}
if (changeType != eMeetingEventChangeType.Unknown)
{
OnMeetingChange(changeType, m);
}
}
}
}
}

View File

@@ -0,0 +1,37 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a ContactMethod
/// </summary>
public class ContactMethod
{
/// <summary>
/// Gets or sets the ContactMethodId
/// </summary>
[JsonProperty("contactMethodId")]
public string ContactMethodId { get; set; }
/// <summary>
/// Gets or sets the Number
/// </summary>
[JsonProperty("number")]
public string Number { get; set; }
/// <summary>
/// Gets or sets the Device
/// </summary>
[JsonProperty("device")]
[JsonConverter(typeof(StringEnumConverter))]
public eContactMethodDevice Device { get; set; }
/// <summary>
/// Gets or sets the CallType
/// </summary>
[JsonProperty("callType")]
[JsonConverter(typeof(StringEnumConverter))]
public eContactMethodCallType CallType { get; set; }
}
}

View File

@@ -0,0 +1,39 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a DirectoryContact
/// </summary>
public class DirectoryContact : DirectoryItem
{
/// <summary>
/// Gets or sets the ContactId
/// </summary>
[JsonProperty("contactId")]
public string ContactId { get; set; }
/// <summary>
/// Gets or sets the Title
/// </summary>
[JsonProperty("title")]
public string Title { get; set; }
/// <summary>
/// Gets or sets the ContactMethods
/// </summary>
[JsonProperty("contactMethods")]
public List<ContactMethod> ContactMethods { get; set; }
/// <summary>
/// Constructor for <see cref="DirectoryContact"/>
/// </summary>
public DirectoryContact()
{
ContactMethods = new List<ContactMethod>();
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a DirectoryEventArgs
/// </summary>
public class DirectoryEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the Directory
/// </summary>
public CodecDirectory Directory { get; set; }
/// <summary>
/// Gets or sets the DirectoryIsOnRoot
/// </summary>
public bool DirectoryIsOnRoot { get; set; }
}
}

View File

@@ -0,0 +1,27 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a DirectoryFolder
/// </summary>
public class DirectoryFolder : DirectoryItem
{
/// <summary>
/// Gets or sets the Contacts
/// </summary>
[JsonProperty("contacts")]
public List<DirectoryContact> Contacts { get; set; }
/// <summary>
/// Constructor for <see cref="DirectoryFolder"/>
/// </summary>
public DirectoryFolder()
{
Contacts = new List<DirectoryContact>();
}
}
}

View File

@@ -0,0 +1,41 @@
using System;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a DirectoryItem
/// </summary>
public class DirectoryItem : ICloneable
{
/// <summary>
/// Clone method
/// </summary>
public object Clone()
{
return MemberwiseClone();
}
/// <summary>
/// Gets or sets the FolderId
/// </summary>
[JsonProperty("folderId")]
public string FolderId { get; set; }
/// <summary>
/// Gets or sets the Name
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the ParentFolderId
/// </summary>
[JsonProperty("parentFolderId")]
public string ParentFolderId { get; set; }
}
}

View File

@@ -1,31 +1,65 @@
using Crestron.SimplSharpPro; namespace PepperDash.Essentials.Devices.Common.Codec
using System; {
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary> /// <summary>
/// Describes a cisco codec device that can allow configuration of cameras /// Describes a cisco codec device that can allow configuration of cameras
/// </summary> /// </summary>
public interface ICiscoCodecCameraConfig public interface ICiscoCodecCameraConfig
{ {
/// <summary>
/// Sets the assigned serial number for the specified camera
/// </summary>
/// <param name="cameraId">The camera identifier</param>
/// <param name="serialNumber">The serial number to assign</param>
void SetCameraAssignedSerialNumber(uint cameraId, string serialNumber); void SetCameraAssignedSerialNumber(uint cameraId, string serialNumber);
/// <summary>
/// Sets the name for the camera on the specified video connector
/// </summary>
/// <param name="videoConnectorId">The video connector identifier</param>
/// <param name="name">The name to assign</param>
void SetCameraName(uint videoConnectorId, string name); void SetCameraName(uint videoConnectorId, string name);
/// <summary>
/// Sets the input source type for the specified video connector
/// </summary>
/// <param name="videoConnectorId">The video connector identifier</param>
/// <param name="sourceType">The source type to set</param>
void SetInputSourceType(uint videoConnectorId, eCiscoCodecInputSourceType sourceType); void SetInputSourceType(uint videoConnectorId, eCiscoCodecInputSourceType sourceType);
} }
/// <summary>
/// Enumeration of Cisco codec input source types
/// </summary>
public enum eCiscoCodecInputSourceType public enum eCiscoCodecInputSourceType
{ {
/// <summary>
/// PC source type
/// </summary>
PC, PC,
/// <summary>
/// Camera source type
/// </summary>
camera, camera,
/// <summary>
/// Document camera source type
/// </summary>
document_camera, document_camera,
/// <summary>
/// Media player source type
/// </summary>
mediaplayer, mediaplayer,
/// <summary>
/// Other source type
/// </summary>
other, other,
/// <summary>
/// Whiteboard source type
/// </summary>
whiteboard whiteboard
} }
} }

View File

@@ -1,10 +1,4 @@
using System; namespace PepperDash.Essentials.Devices.Common.Codec
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Common.Codec
{ {
/// <summary> /// <summary>
/// Defines the contract for IHasCallHold /// Defines the contract for IHasCallHold

View File

@@ -0,0 +1,14 @@
using System.Collections.Generic;
using PepperDash.Essentials.Devices.Common.Codec;
/// <summary>
/// Defines the contract for IHasDirectoryHistoryStack
/// </summary>
public interface IHasDirectoryHistoryStack : IHasDirectory
{
/// <summary>
/// Gets the DirectoryBrowseHistoryStack
/// </summary>
Stack<CodecDirectory> DirectoryBrowseHistoryStack { get; }
}

View File

@@ -1,10 +1,4 @@
using System; using PepperDash.Essentials.Core;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Codec namespace PepperDash.Essentials.Devices.Common.Codec
{ {
@@ -34,6 +28,9 @@ namespace PepperDash.Essentials.Devices.Common.Codec
void ToggleDoNotDisturbMode(); void ToggleDoNotDisturbMode();
} }
/// <summary>
/// Defines the contract for devices that support Do Not Disturb mode with timeout functionality
/// </summary>
public interface IHasDoNotDisturbModeWithTimeout : IHasDoNotDisturbMode public interface IHasDoNotDisturbModeWithTimeout : IHasDoNotDisturbMode
{ {
/// <summary> /// <summary>

View File

@@ -1,25 +1,54 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Devices.Common.VideoCodec.Cisco; using PepperDash.Essentials.Devices.Common.VideoCodec.Cisco;
namespace PepperDash.Essentials.Devices.Common.Codec namespace PepperDash.Essentials.Devices.Common.Codec
{ {
/// <summary> /// <summary>
/// Defines the contract for IHasExternalSourceSwitching /// Defines the contract for IHasExternalSourceSwitching
/// </summary> /// </summary>
public interface IHasExternalSourceSwitching public interface IHasExternalSourceSwitching
{ {
/// <summary>
/// Gets a value indicating whether the external source list is enabled
/// </summary>
bool ExternalSourceListEnabled { get; } bool ExternalSourceListEnabled { get; }
string ExternalSourceInputPort { get; }
/// <summary>
/// Gets the external source input port identifier
/// </summary>
string ExternalSourceInputPort { get; }
/// <summary>
/// Adds an external source to the available sources
/// </summary>
/// <param name="connectorId">The connector identifier</param>
/// <param name="key">The unique key for the source</param>
/// <param name="name">The display name for the source</param>
/// <param name="type">The type of external source</param>
void AddExternalSource(string connectorId, string key, string name, eExternalSourceType type); void AddExternalSource(string connectorId, string key, string name, eExternalSourceType type);
/// <summary>
/// Sets the state of the specified external source
/// </summary>
/// <param name="key">The unique key of the external source</param>
/// <param name="mode">The mode to set for the source</param>
void SetExternalSourceState(string key, eExternalSourceMode mode); void SetExternalSourceState(string key, eExternalSourceMode mode);
/// <summary>
/// Clears all external sources from the list
/// </summary>
void ClearExternalSources(); void ClearExternalSources();
void SetSelectedSource(string key);
Action<string, string> RunRouteAction { set;} /// <summary>
/// Sets the selected source by its key
/// </summary>
/// <param name="key">The unique key of the source to select</param>
void SetSelectedSource(string key);
/// <summary>
/// Sets the action to run when routing between sources
/// </summary>
Action<string, string> RunRouteAction { set; }
} }
} }

View File

@@ -0,0 +1,13 @@
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Defines the contract for IInvitableContact
/// </summary>
public interface IInvitableContact
{
/// <summary>
/// Gets a value indicating whether this contact is invitable
/// </summary>
bool IsInvitableContact { get; }
}
}

View File

@@ -0,0 +1,22 @@
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents an InvitableDirectoryContact
/// </summary>
public class InvitableDirectoryContact : DirectoryContact, IInvitableContact
{
/// <summary>
/// Gets a value indicating whether this contact is invitable
/// </summary>
[JsonProperty("isInvitableContact")]
public bool IsInvitableContact
{
get
{
return this is IInvitableContact;
}
}
}
}

View File

@@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a Meeting
/// </summary>
public class Meeting
{
/// <summary>
/// Minutes before the meeting to show warning
/// </summary>
[JsonProperty("minutesBeforeMeeting")]
public int MinutesBeforeMeeting;
/// <summary>
/// Gets or sets the meeting ID
/// </summary>
[JsonProperty("id")]
public string Id { get; set; }
/// <summary>
/// Gets or sets the meeting organizer
/// </summary>
[JsonProperty("organizer")]
public string Organizer { get; set; }
/// <summary>
/// Gets or sets the Title
/// </summary>
[JsonProperty("title")]
public string Title { get; set; }
/// <summary>
/// Gets or sets the Agenda
/// </summary>
[JsonProperty("agenda")]
public string Agenda { get; set; }
/// <summary>
/// Gets the meeting warning time span in minutes before the meeting starts
/// </summary>
[JsonProperty("meetingWarningMinutes")]
public TimeSpan MeetingWarningMinutes
{
get { return TimeSpan.FromMinutes(MinutesBeforeMeeting); }
}
/// <summary>
/// Gets the time remaining until the meeting starts
/// </summary>
[JsonProperty("timeToMeetingStart")]
public TimeSpan TimeToMeetingStart
{
get
{
return StartTime - DateTime.Now;
}
}
/// <summary>
/// Gets the time remaining until the meeting ends
/// </summary>
[JsonProperty("timeToMeetingEnd")]
public TimeSpan TimeToMeetingEnd
{
get
{
return EndTime - DateTime.Now;
}
}
/// <summary>
/// Gets or sets the StartTime
/// </summary>
[JsonProperty("startTime")]
public DateTime StartTime { get; set; }
/// <summary>
/// Gets or sets the EndTime
/// </summary>
[JsonProperty("endTime")]
public DateTime EndTime { get; set; }
/// <summary>
/// Gets the duration of the meeting
/// </summary>
[JsonProperty("duration")]
public TimeSpan Duration
{
get
{
return EndTime - StartTime;
}
}
/// <summary>
/// Gets or sets the Privacy
/// </summary>
[JsonProperty("privacy")]
public eMeetingPrivacy Privacy { get; set; }
/// <summary>
/// Gets a value indicating whether the meeting can be joined
/// </summary>
[JsonProperty("joinable")]
public bool Joinable
{
get
{
var joinable = StartTime.AddMinutes(-MinutesBeforeMeeting) <= DateTime.Now
&& DateTime.Now <= EndTime.AddSeconds(-_joinableCooldownSeconds);
//Debug.LogMessage(LogEventLevel.Verbose, "Meeting Id: {0} joinable: {1}", Id, joinable);
return joinable;
}
}
/// <summary>
/// Gets or sets the Dialable
/// </summary>
[JsonProperty("dialable")]
public bool Dialable { get; set; }
//public string ConferenceNumberToDial { get; set; }
/// <summary>
/// Gets or sets the ConferencePassword
/// </summary>
[JsonProperty("conferencePassword")]
public string ConferencePassword { get; set; }
/// <summary>
/// Gets or sets the IsOneButtonToPushMeeting
/// </summary>
[JsonProperty("isOneButtonToPushMeeting")]
public bool IsOneButtonToPushMeeting { get; set; }
/// <summary>
/// Gets or sets the Calls
/// </summary>
[JsonProperty("calls")]
public List<Call> Calls { get; private set; }
/// <summary>
/// Tracks the change types that have already been notified for
/// Gets or sets the NotifiedChangeTypes
/// </summary>
[JsonIgnore]
public eMeetingEventChangeType NotifiedChangeTypes { get; set; }
[JsonIgnore] private readonly int _joinableCooldownSeconds;
/// <summary>
/// Constructor for Meeting <see cref="Meeting"/>
/// </summary>
public Meeting()
{
Calls = new List<Call>();
_joinableCooldownSeconds = 300;
}
/// <summary>
/// Constructor for Meeting <see cref="Meeting"/>
/// </summary>
/// <param name="joinableCooldownSeconds">Number of seconds after meeting start when it is no longer joinable</param>
public Meeting(int joinableCooldownSeconds)
{
Calls = new List<Call>();
_joinableCooldownSeconds = joinableCooldownSeconds;
}
#region Overrides of Object
/// <summary>
/// ToString method
/// </summary>
/// <inheritdoc />
public override string ToString()
{
return string.Format("{0}:{1}: {2}-{3}", Title, Agenda, StartTime, EndTime);
}
#endregion
}
}

View File

@@ -0,0 +1,22 @@
using System;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a MeetingEventArgs
/// </summary>
public class MeetingEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the ChangeType
/// </summary>
public eMeetingEventChangeType ChangeType { get; set; }
/// <summary>
/// Gets or sets the Meeting
/// </summary>
public Meeting Meeting { get; set; }
}
}

View File

@@ -1,10 +1,4 @@
using System; namespace PepperDash.Essentials.Devices.Common.Codec
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Common.Codec
{ {
/// <summary> /// <summary>
@@ -12,7 +6,20 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
public enum eCodecCallDirection public enum eCodecCallDirection
{ {
Unknown = 0, Incoming, Outgoing /// <summary>
/// Unknown call direction
/// </summary>
Unknown = 0,
/// <summary>
/// Incoming call direction
/// </summary>
Incoming,
/// <summary>
/// Outgoing call direction
/// </summary>
Outgoing
} }
/// <summary> /// <summary>

View File

@@ -1,27 +1,68 @@
using System; namespace PepperDash.Essentials.Devices.Common.Codec
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Common.Codec
{ {
/// <summary> /// <summary>
/// Enumeration of eCodecCallStatus values /// Enumeration of eCodecCallStatus values
/// </summary> /// </summary>
public enum eCodecCallStatus public enum eCodecCallStatus
{ {
/// <summary>
/// Unknown call status
/// </summary>
Unknown = 0, Unknown = 0,
Connected,
Connecting, /// <summary>
Dialing, /// Call is connected
/// </summary>
Connected,
/// <summary>
/// Call is connecting
/// </summary>
Connecting,
/// <summary>
/// Call is dialing
/// </summary>
Dialing,
/// <summary>
/// Call is disconnected
/// </summary>
Disconnected, Disconnected,
Disconnecting,
EarlyMedia, /// <summary>
/// Call is disconnecting
/// </summary>
Disconnecting,
/// <summary>
/// Early media is being sent/received
/// </summary>
EarlyMedia,
/// <summary>
/// Call is idle
/// </summary>
Idle, Idle,
OnHold,
Ringing, /// <summary>
Preserved, /// Call is on hold
/// </summary>
OnHold,
/// <summary>
/// Call is ringing
/// </summary>
Ringing,
/// <summary>
/// Call is preserved
/// </summary>
Preserved,
/// <summary>
/// Call is remote preserved
/// </summary>
RemotePreserved, RemotePreserved,
} }

View File

@@ -1,10 +1,4 @@
using System; namespace PepperDash.Essentials.Devices.Common.Codec
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Common.Codec
{ {
/// <summary> /// <summary>
@@ -12,10 +6,29 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
public enum eCodecCallType public enum eCodecCallType
{ {
Unknown = 0, /// <summary>
Audio, /// Unknown call type
Video, /// </summary>
AudioCanEscalate, Unknown = 0,
/// <summary>
/// Audio-only call type
/// </summary>
Audio,
/// <summary>
/// Video call type
/// </summary>
Video,
/// <summary>
/// Audio call that can be escalated to video
/// </summary>
AudioCanEscalate,
/// <summary>
/// Forward all call type
/// </summary>
ForwardAllCall ForwardAllCall
} }

View File

@@ -0,0 +1,21 @@
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Enumeration of eContactMethodCallType values
/// </summary>
public enum eContactMethodCallType
{
/// <summary>
/// Unknown call type
/// </summary>
Unknown = 0,
/// <summary>
/// Audio call type
/// </summary>
Audio,
/// <summary>
/// Video call type
/// </summary>
Video
}
}

View File

@@ -0,0 +1,29 @@
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Enumeration of eContactMethodDevice values
/// </summary>
public enum eContactMethodDevice
{
/// <summary>
/// Unknown contact method
/// </summary>
Unknown = 0,
/// <summary>
/// Mobile contact method
/// </summary>
Mobile,
/// <summary>
/// Other contact method
/// </summary>
Other,
/// <summary>
/// Telephone contact method
/// </summary>
Telephone,
/// <summary>
/// Video contact method
/// </summary>
Video
}
}

View File

@@ -0,0 +1,35 @@
using System;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Enumeration of eMeetingEventChangeType values
/// </summary>
[Flags]
public enum eMeetingEventChangeType
{
/// <summary>
/// Unknown change type
/// </summary>
Unknown = 0,
/// <summary>
/// Meeting start warning
/// </summary>
MeetingStartWarning = 1,
/// <summary>
/// Meeting start
/// </summary>
MeetingStart = 2,
/// <summary>
/// Meeting end warning
/// </summary>
MeetingEndWarning = 4,
/// <summary>
/// Meeting end
/// </summary>
MeetingEnd = 8
}
}

View File

@@ -1,18 +1,23 @@
using System; namespace PepperDash.Essentials.Devices.Common.Codec
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Common.Codec
{ {
/// <summary> /// <summary>
/// Enumeration of eMeetingPrivacy values /// Enumeration of eMeetingPrivacy values
/// </summary> /// </summary>
public enum eMeetingPrivacy public enum eMeetingPrivacy
{ {
/// <summary>
/// Unknown meeting privacy level
/// </summary>
Unknown = 0, Unknown = 0,
/// <summary>
/// Public meeting
/// </summary>
Public, Public,
/// <summary>
/// Private meeting
/// </summary>
Private Private
} }

View File

@@ -1,10 +1,4 @@
using System; using PepperDash.Essentials.Core;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Codec namespace PepperDash.Essentials.Devices.Common.Codec
{ {

View File

@@ -1,8 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Common.Codec namespace PepperDash.Essentials.Devices.Common.Codec
{ {
@@ -11,6 +7,9 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
public interface IHasCallFavorites public interface IHasCallFavorites
{ {
/// <summary>
/// Gets the call favorites for this device
/// </summary>
CodecCallFavorites CallFavorites { get; } CodecCallFavorites CallFavorites { get; }
} }
@@ -24,6 +23,9 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
public List<CodecActiveCallItem> Favorites { get; set; } public List<CodecActiveCallItem> Favorites { get; set; }
/// <summary>
/// Initializes a new instance of the CodecCallFavorites class
/// </summary>
public CodecCallFavorites() public CodecCallFavorites()
{ {
Favorites = new List<CodecActiveCallItem>(); Favorites = new List<CodecActiveCallItem>();

View File

@@ -2,10 +2,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using PepperDash.Essentials.Devices.Common.VideoCodec;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
using PepperDash.Essentials.Devices.Common.VideoCodec;
namespace PepperDash.Essentials.Devices.Common.Codec namespace PepperDash.Essentials.Devices.Common.Codec
{ {
@@ -15,8 +14,15 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
public interface IHasCallHistory public interface IHasCallHistory
{ {
/// <summary>
/// Gets the call history for this device
/// </summary>
CodecCallHistory CallHistory { get; } CodecCallHistory CallHistory { get; }
/// <summary>
/// Removes the specified call history entry
/// </summary>
/// <param name="entry">The call history entry to remove</param>
void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry); void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry);
} }
@@ -25,9 +31,24 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
public enum eCodecOccurrenceType public enum eCodecOccurrenceType
{ {
/// <summary>
/// Unknown occurrence type
/// </summary>
Unknown = 0, Unknown = 0,
/// <summary>
/// Call was placed (outgoing)
/// </summary>
Placed = 1, Placed = 1,
/// <summary>
/// Call was received (incoming)
/// </summary>
Received = 2, Received = 2,
/// <summary>
/// Call received no answer
/// </summary>
NoAnswer = 3, NoAnswer = 3,
} }
@@ -36,6 +57,9 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
public class CodecCallHistory public class CodecCallHistory
{ {
/// <summary>
/// Event that is raised when the recent calls list has changed
/// </summary>
public event EventHandler<EventArgs> RecentCallsListHasChanged; public event EventHandler<EventArgs> RecentCallsListHasChanged;
/// <summary> /// <summary>
@@ -48,6 +72,9 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
CallHistoryEntry ListEmptyEntry; CallHistoryEntry ListEmptyEntry;
/// <summary>
/// Initializes a new instance of the CodecCallHistory class
/// </summary>
public CodecCallHistory() public CodecCallHistory()
{ {
ListEmptyEntry = new CallHistoryEntry() { Name = "No Recent Calls" }; ListEmptyEntry = new CallHistoryEntry() { Name = "No Recent Calls" };
@@ -80,15 +107,22 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
public class CallHistoryEntry : CodecActiveCallItem public class CallHistoryEntry : CodecActiveCallItem
{ {
[JsonConverter(typeof(IsoDateTimeConverter))]
[JsonProperty("startTime")]
/// <summary> /// <summary>
/// Gets or sets the StartTime /// Gets or sets the StartTime
/// </summary> /// </summary>
[JsonConverter(typeof(IsoDateTimeConverter))]
[JsonProperty("startTime")]
public DateTime StartTime { get; set; } public DateTime StartTime { get; set; }
/// <summary>
/// Gets or sets the occurrence type for this call history entry
/// </summary>
[JsonConverter(typeof(StringEnumConverter))] [JsonConverter(typeof(StringEnumConverter))]
[JsonProperty("occurrenceType")] [JsonProperty("occurrenceType")]
public eCodecOccurrenceType OccurrenceType { get; set; } public eCodecOccurrenceType OccurrenceType { get; set; }
/// <summary>
/// Gets or sets the occurrence history identifier
/// </summary>
[JsonProperty("occurrenceHistoryId")] [JsonProperty("occurrenceHistoryId")]
public string OccurrenceHistoryId { get; set; } public string OccurrenceHistoryId { get; set; }
} }
@@ -119,7 +153,7 @@ namespace PepperDash.Essentials.Devices.Common.Codec
} }
// Check if list is empty and if so, add an item to display No Recent Calls // Check if list is empty and if so, add an item to display No Recent Calls
if(genericEntries.Count == 0) if (genericEntries.Count == 0)
genericEntries.Add(ListEmptyEntry); genericEntries.Add(ListEmptyEntry);
RecentCalls = genericEntries; RecentCalls = genericEntries;

View File

@@ -1,11 +1,4 @@
using System; using PepperDash.Essentials.Core;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Codec namespace PepperDash.Essentials.Devices.Common.Codec
@@ -15,12 +8,29 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
public interface IHasContentSharing public interface IHasContentSharing
{ {
/// <summary>
/// Gets feedback indicating whether content sharing is currently active
/// </summary>
BoolFeedback SharingContentIsOnFeedback { get; } BoolFeedback SharingContentIsOnFeedback { get; }
/// <summary>
/// Gets feedback about the current sharing source
/// </summary>
StringFeedback SharingSourceFeedback { get; } StringFeedback SharingSourceFeedback { get; }
/// <summary>
/// Gets a value indicating whether content should be automatically shared while in a call
/// </summary>
bool AutoShareContentWhileInCall { get; } bool AutoShareContentWhileInCall { get; }
/// <summary>
/// Starts content sharing
/// </summary>
void StartSharing(); void StartSharing();
/// <summary>
/// Stops content sharing
/// </summary>
void StopSharing(); void StopSharing();
} }

View File

@@ -1,10 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Codec namespace PepperDash.Essentials.Devices.Common.Codec
{ {
@@ -15,15 +9,49 @@ namespace PepperDash.Essentials.Devices.Common.Codec
{ {
// Add requirements for Dialer functionality // Add requirements for Dialer functionality
/// <summary>
/// Event that is raised when call status changes
/// </summary>
event EventHandler<CodecCallStatusItemChangeEventArgs> CallStatusChange; event EventHandler<CodecCallStatusItemChangeEventArgs> CallStatusChange;
/// <summary>
/// Dials the specified number
/// </summary>
/// <param name="number">The number to dial</param>
void Dial(string number); void Dial(string number);
/// <summary>
/// Ends the specified active call
/// </summary>
/// <param name="activeCall">The active call to end</param>
void EndCall(CodecActiveCallItem activeCall); void EndCall(CodecActiveCallItem activeCall);
/// <summary>
/// Ends all active calls
/// </summary>
void EndAllCalls(); void EndAllCalls();
/// <summary>
/// Accepts the specified incoming call
/// </summary>
/// <param name="item">The call item to accept</param>
void AcceptCall(CodecActiveCallItem item); void AcceptCall(CodecActiveCallItem item);
/// <summary>
/// Rejects the specified incoming call
/// </summary>
/// <param name="item">The call item to reject</param>
void RejectCall(CodecActiveCallItem item); void RejectCall(CodecActiveCallItem item);
/// <summary>
/// Sends DTMF digits during a call
/// </summary>
/// <param name="digit">The DTMF digit(s) to send</param>
void SendDtmf(string digit); void SendDtmf(string digit);
/// <summary>
/// Gets a value indicating whether the device is currently in a call
/// </summary>
bool IsInCall { get; } bool IsInCall { get; }
} }

View File

@@ -1,319 +1,59 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Devices.Common.VideoCodec; using PepperDash.Essentials.Devices.Common.VideoCodec;
namespace PepperDash.Essentials.Devices.Common.Codec namespace PepperDash.Essentials.Devices.Common.Codec
{ {
/// <summary> /// <summary>
/// Defines the API for codecs with a directory /// Defines the API for codecs with a directory
/// </summary> /// </summary>
public interface IHasDirectory public interface IHasDirectory
{ {
/// <summary>
/// Event that fires when a directory result is returned from the codec
/// </summary>
event EventHandler<DirectoryEventArgs> DirectoryResultReturned; event EventHandler<DirectoryEventArgs> DirectoryResultReturned;
/// <summary>
/// Gets the DirectoryRoot
/// </summary>
CodecDirectory DirectoryRoot { get; } CodecDirectory DirectoryRoot { get; }
/// <summary>
/// Gets the CurrentDirectoryResult
/// </summary>
CodecDirectory CurrentDirectoryResult { get; } CodecDirectory CurrentDirectoryResult { get; }
/// <summary>
/// Gets the PhonebookSyncState
/// </summary>
CodecPhonebookSyncState PhonebookSyncState { get; } CodecPhonebookSyncState PhonebookSyncState { get; }
/// <summary>
/// Method to initiate a search of the directory on the server
/// </summary>
void SearchDirectory(string searchString); void SearchDirectory(string searchString);
/// <summary>
/// Method to get the contents of a specific folder in the directory on the server
/// </summary>
void GetDirectoryFolderContents(string folderId); void GetDirectoryFolderContents(string folderId);
/// <summary>
/// Method to set the current directory to the root folder
/// </summary>
void SetCurrentDirectoryToRoot(); void SetCurrentDirectoryToRoot();
/// <summary>
/// Method to get the contents of the parent folder in the directory on the server
/// </summary>
void GetDirectoryParentFolderContents(); void GetDirectoryParentFolderContents();
/// <summary>
/// Gets the CurrentDirectoryResultIsNotDirectoryRoot
/// </summary>
BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; } BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; }
} }
/// <summary>
/// Defines the contract for IHasDirectoryHistoryStack
/// </summary>
public interface IHasDirectoryHistoryStack : IHasDirectory
{
Stack<CodecDirectory> DirectoryBrowseHistoryStack { get; }
}
/// <summary>
/// Represents a DirectoryEventArgs
/// </summary>
public class DirectoryEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the Directory
/// </summary>
public CodecDirectory Directory { get; set; }
/// <summary>
/// Gets or sets the DirectoryIsOnRoot
/// </summary>
public bool DirectoryIsOnRoot { get; set; }
}
/// <summary>
/// Represents a codec directory
/// </summary>
public class CodecDirectory
{
/// <summary>
/// Represents the contents of the directory
/// We don't want to serialize this for messages to MobileControl. MC can combine Contacts and Folders to get the same data
/// </summary>
[JsonIgnore]
public List<DirectoryItem> CurrentDirectoryResults { get; private set; }
[JsonProperty("contacts")]
public List<DirectoryItem> Contacts
{
get
{
return CurrentDirectoryResults.OfType<DirectoryContact>().Cast<DirectoryItem>().ToList();
}
}
[JsonProperty("folders")]
public List<DirectoryItem> Folders
{
get
{
return CurrentDirectoryResults.OfType<DirectoryFolder>().Cast<DirectoryItem>().ToList();
}
}
/// <summary>
/// Used to store the ID of the current folder for CurrentDirectoryResults
/// </summary>
[JsonProperty("resultsFolderId")]
/// <summary>
/// Gets or sets the ResultsFolderId
/// </summary>
public string ResultsFolderId { get; set; }
public CodecDirectory()
{
CurrentDirectoryResults = new List<DirectoryItem>();
}
/// <summary>
/// Adds folders to the directory
/// </summary>
/// <param name="folders"></param>
/// <summary>
/// AddFoldersToDirectory method
/// </summary>
public void AddFoldersToDirectory(List<DirectoryItem> folders)
{
if(folders != null)
CurrentDirectoryResults.AddRange(folders);
SortDirectory();
}
/// <summary>
/// Adds contacts to the directory
/// </summary>
/// <param name="contacts"></param>
/// <summary>
/// AddContactsToDirectory method
/// </summary>
public void AddContactsToDirectory(List<DirectoryItem> contacts)
{
if(contacts != null)
CurrentDirectoryResults.AddRange(contacts);
SortDirectory();
}
/// <summary>
/// Filters the CurrentDirectoryResults by the predicate
/// </summary>
/// <param name="predicate"></param>
/// <summary>
/// FilterContacts method
/// </summary>
public void FilterContacts(Func<DirectoryItem, bool> predicate)
{
CurrentDirectoryResults = CurrentDirectoryResults.Where(predicate).ToList();
}
/// <summary>
/// Sorts the DirectoryResults list to display all folders alphabetically, then all contacts alphabetically
/// </summary>
private void SortDirectory()
{
var sortedFolders = new List<DirectoryItem>();
sortedFolders.AddRange(CurrentDirectoryResults.Where(f => f is DirectoryFolder));
sortedFolders.OrderBy(f => f.Name);
var sortedContacts = new List<DirectoryItem>();
sortedContacts.AddRange(CurrentDirectoryResults.Where(c => c is DirectoryContact));
sortedFolders.OrderBy(c => c.Name);
CurrentDirectoryResults.Clear();
CurrentDirectoryResults.AddRange(sortedFolders);
CurrentDirectoryResults.AddRange(sortedContacts);
}
}
/// <summary>
/// Defines the contract for IInvitableContact
/// </summary>
public interface IInvitableContact
{
bool IsInvitableContact { get; }
}
public class InvitableDirectoryContact : DirectoryContact, IInvitableContact
{
[JsonProperty("isInvitableContact")]
public bool IsInvitableContact
{
get
{
return this is IInvitableContact;
}
}
}
/// <summary>
/// Represents a DirectoryItem
/// </summary>
public class DirectoryItem : ICloneable
{
/// <summary>
/// Clone method
/// </summary>
public object Clone()
{
return this.MemberwiseClone();
}
[JsonProperty("folderId")]
public string FolderId { get; set; }
[JsonProperty("name")]
/// <summary>
/// Gets or sets the Name
/// </summary>
public string Name { get; set; }
[JsonProperty("parentFolderId")]
/// <summary>
/// Gets or sets the ParentFolderId
/// </summary>
public string ParentFolderId { get; set; }
}
/// <summary>
/// Represents a DirectoryFolder
/// </summary>
public class DirectoryFolder : DirectoryItem
{
[JsonProperty("contacts")]
/// <summary>
/// Gets or sets the Contacts
/// </summary>
public List<DirectoryContact> Contacts { get; set; }
public DirectoryFolder()
{
Contacts = new List<DirectoryContact>();
}
}
/// <summary>
/// Represents a DirectoryContact
/// </summary>
public class DirectoryContact : DirectoryItem
{
[JsonProperty("contactId")]
/// <summary>
/// Gets or sets the ContactId
/// </summary>
public string ContactId { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("contactMethods")]
public List<ContactMethod> ContactMethods { get; set; }
public DirectoryContact()
{
ContactMethods = new List<ContactMethod>();
}
}
/// <summary>
/// Represents a ContactMethod
/// </summary>
public class ContactMethod
{
[JsonProperty("contactMethodId")]
/// <summary>
/// Gets or sets the ContactMethodId
/// </summary>
public string ContactMethodId { get; set; }
[JsonProperty("number")]
public string Number { get; set; }
[JsonProperty("device")]
[JsonConverter(typeof(StringEnumConverter))]
/// <summary>
/// Gets or sets the Device
/// </summary>
public eContactMethodDevice Device { get; set; }
[JsonProperty("callType")]
[JsonConverter(typeof(StringEnumConverter))]
/// <summary>
/// Gets or sets the CallType
/// </summary>
public eContactMethodCallType CallType { get; set; }
}
/// <summary>
/// Enumeration of eContactMethodDevice values
/// </summary>
public enum eContactMethodDevice
{
Unknown = 0,
Mobile,
Other,
Telephone,
Video
}
/// <summary>
/// Enumeration of eContactMethodCallType values
/// </summary>
public enum eContactMethodCallType
{
Unknown = 0,
Audio,
Video
}
} }

View File

@@ -1,339 +1,20 @@
namespace PepperDash.Essentials.Devices.Common.Codec
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using Newtonsoft.Json;
using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Codec
{ {
[Flags]
/// <summary>
/// Enumeration of eMeetingEventChangeType values
/// </summary>
public enum eMeetingEventChangeType
{
Unknown = 0,
MeetingStartWarning = 1,
MeetingStart = 2,
MeetingEndWarning = 4,
MeetingEnd = 8
}
/// <summary> /// <summary>
/// Defines the contract for IHasScheduleAwareness /// Defines the contract for IHasScheduleAwareness
/// </summary> /// </summary>
public interface IHasScheduleAwareness public interface IHasScheduleAwareness
{ {
/// <summary>
/// Gets the CodecScheduleAwareness instance
/// </summary>
CodecScheduleAwareness CodecSchedule { get; } CodecScheduleAwareness CodecSchedule { get; }
/// <summary>
/// Method to initiate getting the schedule from the server
/// </summary>
void GetSchedule(); void GetSchedule();
} }
/// <summary>
/// Represents a CodecScheduleAwareness
/// </summary>
public class CodecScheduleAwareness
{
List<Meeting> _meetings;
public event EventHandler<MeetingEventArgs> MeetingEventChange;
public event EventHandler<EventArgs> MeetingsListHasChanged;
private int _meetingWarningMinutes = 5;
//private Meeting _previousChangedMeeting;
//private eMeetingEventChangeType _previousChangeType = eMeetingEventChangeType.Unknown;
public int MeetingWarningMinutes
{
get { return _meetingWarningMinutes; }
set { _meetingWarningMinutes = value; }
}
/// <summary>
/// Setter triggers MeetingsListHasChanged event
/// </summary>
public List<Meeting> Meetings
{
get
{
return _meetings;
}
set
{
_meetings = value;
MeetingsListHasChanged?.Invoke(this, new EventArgs());
}
}
private readonly CTimer _scheduleChecker;
public CodecScheduleAwareness()
{
Meetings = new List<Meeting>();
_scheduleChecker = new CTimer(CheckSchedule, null, 1000, 1000);
}
public CodecScheduleAwareness(long pollTime)
{
Meetings = new List<Meeting>();
_scheduleChecker = new CTimer(CheckSchedule, null, pollTime, pollTime);
}
/// <summary>
/// Helper method to fire MeetingEventChange. Should only fire once for each changeType on each meeting
/// </summary>
/// <param name="changeType"></param>
/// <param name="meeting"></param>
private void OnMeetingChange(eMeetingEventChangeType changeType, Meeting meeting)
{
Debug.LogMessage(LogEventLevel.Verbose, "*****************OnMeetingChange. id: {0} changeType: {1}**********************", meeting.Id, changeType);
if (changeType != (changeType & meeting.NotifiedChangeTypes))
{
// Add this change type to the NotifiedChangeTypes
meeting.NotifiedChangeTypes |= changeType;
MeetingEventChange?.Invoke(this, new MeetingEventArgs() { ChangeType = changeType, Meeting = meeting });
}
else
{
Debug.LogMessage(LogEventLevel.Verbose, "Meeting: {0} already notified of changeType: {1}", meeting.Id, changeType);
}
}
/// <summary>
/// Checks the schedule to see if any MeetingEventChange updates should be fired
/// </summary>
/// <param name="o"></param>
private void CheckSchedule(object o)
{
// Iterate the meeting list and check if any meeting need to do anything
const double meetingTimeEpsilon = 0.05;
foreach (var m in Meetings)
{
var changeType = eMeetingEventChangeType.Unknown;
if (eMeetingEventChangeType.MeetingStartWarning != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingStartWarning) && m.TimeToMeetingStart.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes && m.TimeToMeetingStart.Seconds > 0) // Meeting is about to start
{
Debug.LogMessage(LogEventLevel.Verbose, "********************* MeetingStartWarning. TotalMinutes: {0} Seconds: {1}", m.TimeToMeetingStart.TotalMinutes, m.TimeToMeetingStart.Seconds);
changeType = eMeetingEventChangeType.MeetingStartWarning;
}
else if (eMeetingEventChangeType.MeetingStart != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingStart) && Math.Abs(m.TimeToMeetingStart.TotalMinutes) < meetingTimeEpsilon) // Meeting Start
{
Debug.LogMessage(LogEventLevel.Verbose, "********************* MeetingStart");
changeType = eMeetingEventChangeType.MeetingStart;
}
else if (eMeetingEventChangeType.MeetingEndWarning != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingEndWarning) && m.TimeToMeetingEnd.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes && m.TimeToMeetingEnd.Seconds > 0) // Meeting is about to end
{
Debug.LogMessage(LogEventLevel.Verbose, "********************* MeetingEndWarning. TotalMinutes: {0} Seconds: {1}", m.TimeToMeetingEnd.TotalMinutes, m.TimeToMeetingEnd.Seconds);
changeType = eMeetingEventChangeType.MeetingEndWarning;
}
else if (eMeetingEventChangeType.MeetingEnd != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingEnd) && Math.Abs(m.TimeToMeetingEnd.TotalMinutes) < meetingTimeEpsilon) // Meeting has ended
{
Debug.LogMessage(LogEventLevel.Verbose, "********************* MeetingEnd");
changeType = eMeetingEventChangeType.MeetingEnd;
}
if (changeType != eMeetingEventChangeType.Unknown)
{
OnMeetingChange(changeType, m);
}
}
}
}
/// <summary>
/// Represents a Meeting
/// </summary>
public class Meeting
{
[JsonProperty("minutesBeforeMeeting")]
public int MinutesBeforeMeeting;
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("organizer")]
public string Organizer { get; set; }
[JsonProperty("title")]
/// <summary>
/// Gets or sets the Title
/// </summary>
public string Title { get; set; }
[JsonProperty("agenda")]
/// <summary>
/// Gets or sets the Agenda
/// </summary>
public string Agenda { get; set; }
[JsonProperty("meetingWarningMinutes")]
public TimeSpan MeetingWarningMinutes
{
get { return TimeSpan.FromMinutes(MinutesBeforeMeeting); }
}
[JsonProperty("timeToMeetingStart")]
public TimeSpan TimeToMeetingStart
{
get
{
return StartTime - DateTime.Now;
}
}
[JsonProperty("timeToMeetingEnd")]
public TimeSpan TimeToMeetingEnd
{
get
{
return EndTime - DateTime.Now;
}
}
[JsonProperty("startTime")]
/// <summary>
/// Gets or sets the StartTime
/// </summary>
public DateTime StartTime { get; set; }
[JsonProperty("endTime")]
/// <summary>
/// Gets or sets the EndTime
/// </summary>
public DateTime EndTime { get; set; }
[JsonProperty("duration")]
public TimeSpan Duration
{
get
{
return EndTime - StartTime;
}
}
[JsonProperty("privacy")]
/// <summary>
/// Gets or sets the Privacy
/// </summary>
public eMeetingPrivacy Privacy { get; set; }
[JsonProperty("joinable")]
public bool Joinable
{
get
{
var joinable = StartTime.AddMinutes(-MinutesBeforeMeeting) <= DateTime.Now
&& DateTime.Now <= EndTime.AddSeconds(-_joinableCooldownSeconds);
//Debug.LogMessage(LogEventLevel.Verbose, "Meeting Id: {0} joinable: {1}", Id, joinable);
return joinable;
}
}
[JsonProperty("dialable")]
/// <summary>
/// Gets or sets the Dialable
/// </summary>
public bool Dialable { get; set; }
//public string ConferenceNumberToDial { get; set; }
[JsonProperty("conferencePassword")]
/// <summary>
/// Gets or sets the ConferencePassword
/// </summary>
public string ConferencePassword { get; set; }
[JsonProperty("isOneButtonToPushMeeting")]
/// <summary>
/// Gets or sets the IsOneButtonToPushMeeting
/// </summary>
public bool IsOneButtonToPushMeeting { get; set; }
[JsonProperty("calls")]
/// <summary>
/// Gets or sets the Calls
/// </summary>
public List<Call> Calls { get; private set; }
/// <summary>
/// Tracks the change types that have already been notified for
/// </summary>
[JsonIgnore]
/// <summary>
/// Gets or sets the NotifiedChangeTypes
/// </summary>
public eMeetingEventChangeType NotifiedChangeTypes { get; set; }
[JsonIgnore] private readonly int _joinableCooldownSeconds;
public Meeting()
{
Calls = new List<Call>();
_joinableCooldownSeconds = 300;
}
public Meeting(int joinableCooldownSeconds)
{
Calls = new List<Call>();
_joinableCooldownSeconds = joinableCooldownSeconds;
}
#region Overrides of Object
/// <summary>
/// ToString method
/// </summary>
/// <inheritdoc />
public override string ToString()
{
return String.Format("{0}:{1}: {2}-{3}", Title, Agenda, StartTime, EndTime);
}
#endregion
}
/// <summary>
/// Represents a Call
/// </summary>
public class Call
{
/// <summary>
/// Gets or sets the Number
/// </summary>
public string Number { get; set; }
/// <summary>
/// Gets or sets the Protocol
/// </summary>
public string Protocol { get; set; }
/// <summary>
/// Gets or sets the CallRate
/// </summary>
public string CallRate { get; set; }
/// <summary>
/// Gets or sets the CallType
/// </summary>
public string CallType { get; set; }
}
/// <summary>
/// Represents a MeetingEventArgs
/// </summary>
public class MeetingEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the ChangeType
/// </summary>
public eMeetingEventChangeType ChangeType { get; set; }
/// <summary>
/// Gets or sets the Meeting
/// </summary>
public Meeting Meeting { get; set; }
}
} }

View File

@@ -1,61 +1,84 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Devices.Common.Codec;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.DeviceTypeInterfaces; using PepperDash.Essentials.Core.DeviceTypeInterfaces;
namespace PepperDash.Essentials.Devices.Common.DSP namespace PepperDash.Essentials.Devices.Common.DSP
{ {
public abstract class DspBase : EssentialsDevice, ILevelControls /// <summary>
{ /// Base class for DSP devices
public Dictionary<string,IBasicVolumeWithFeedback> LevelControlPoints { get; private set; } /// </summary>
public abstract class DspBase : EssentialsDevice, ILevelControls
{
/// <summary>
/// Gets the collection of level control points
/// </summary>
public Dictionary<string, IBasicVolumeWithFeedback> LevelControlPoints { get; private set; }
/// <summary>
/// Gets the collection of dialer control points
/// </summary>
public Dictionary<string, DspControlPoint> DialerControlPoints { get; private set; } public Dictionary<string, DspControlPoint> DialerControlPoints { get; private set; }
/// <summary>
/// Gets the collection of switcher control points
/// </summary>
public Dictionary<string, DspControlPoint> SwitcherControlPoints { get; private set; } public Dictionary<string, DspControlPoint> SwitcherControlPoints { get; private set; }
public DspBase(string key, string name) : /// <summary>
base(key, name) /// Initializes a new instance of the DspBase class
{ /// </summary>
/// <param name="key">The device key</param>
/// <param name="name">The device name</param>
public DspBase(string key, string name) :
base(key, name)
{
LevelControlPoints = new Dictionary<string, IBasicVolumeWithFeedback>(); LevelControlPoints = new Dictionary<string, IBasicVolumeWithFeedback>();
DialerControlPoints = new Dictionary<string, DspControlPoint>(); DialerControlPoints = new Dictionary<string, DspControlPoint>();
SwitcherControlPoints = new Dictionary<string, DspControlPoint>(); SwitcherControlPoints = new Dictionary<string, DspControlPoint>();
} }
// in audio call feedback // in audio call feedback
// VOIP // VOIP
// Phone dialer // Phone dialer
} }
// Fusion // Fusion
// Privacy state // Privacy state
// Online state // Online state
// level/mutes ? // level/mutes ?
// AC Log call stats
// Typical presets:
// call default preset to restore levels and mutes
public abstract class DspControlPoint :IKeyed // AC Log call stats
{
// Typical presets:
// call default preset to restore levels and mutes
/// <summary>
/// Base class for DSP control points
/// </summary>
public abstract class DspControlPoint : IKeyed
{
/// <summary> /// <summary>
/// Gets or sets the Key /// Gets or sets the Key
/// </summary> /// </summary>
public string Key { get; } public string Key { get; }
/// <summary>
/// Initializes a new instance of the DspControlPoint class
/// </summary>
/// <param name="key">The control point key</param>
protected DspControlPoint(string key) => Key = key; protected DspControlPoint(string key) => Key = key;
} }
public abstract class DspLevelControlPoint :DspControlPoint, IBasicVolumeWithFeedback /// <summary>
/// Base class for DSP level control points with volume and mute functionality
/// </summary>
public abstract class DspLevelControlPoint : DspControlPoint, IBasicVolumeWithFeedback
{ {
/// <summary> /// <summary>
/// Gets or sets the MuteFeedback /// Gets or sets the MuteFeedback
@@ -66,30 +89,63 @@ namespace PepperDash.Essentials.Devices.Common.DSP
/// </summary> /// </summary>
public IntFeedback VolumeLevelFeedback { get; } public IntFeedback VolumeLevelFeedback { get; }
/// <summary>
/// Initializes a new instance of the DspLevelControlPoint class
/// </summary>
/// <param name="key">The control point key</param>
/// <param name="muteFeedbackFunc">Function to get mute status</param>
/// <param name="volumeLevelFeedbackFunc">Function to get volume level</param>
protected DspLevelControlPoint(string key, Func<bool> muteFeedbackFunc, Func<int> volumeLevelFeedbackFunc) : base(key) protected DspLevelControlPoint(string key, Func<bool> muteFeedbackFunc, Func<int> volumeLevelFeedbackFunc) : base(key)
{ {
MuteFeedback = new BoolFeedback(muteFeedbackFunc); MuteFeedback = new BoolFeedback("mute", muteFeedbackFunc);
VolumeLevelFeedback = new IntFeedback(volumeLevelFeedbackFunc); VolumeLevelFeedback = new IntFeedback("volume", volumeLevelFeedbackFunc);
} }
/// <summary>
/// Turns mute off
/// </summary>
public abstract void MuteOff(); public abstract void MuteOff();
/// <summary>
/// Turns mute on
/// </summary>
public abstract void MuteOn(); public abstract void MuteOn();
/// <summary>
/// Toggles mute state
/// </summary>
public abstract void MuteToggle(); public abstract void MuteToggle();
/// <summary>
/// Sets the volume level
/// </summary>
/// <param name="level">The volume level to set</param>
public abstract void SetVolume(ushort level); public abstract void SetVolume(ushort level);
/// <summary>
/// Decreases volume
/// </summary>
/// <param name="pressRelease">True when pressed, false when released</param>
public abstract void VolumeDown(bool pressRelease); public abstract void VolumeDown(bool pressRelease);
/// <summary>
/// Increases volume
/// </summary>
/// <param name="pressRelease">True when pressed, false when released</param>
public abstract void VolumeUp(bool pressRelease); public abstract void VolumeUp(bool pressRelease);
} }
/// <summary>
public abstract class DspDialerBase:DspControlPoint /// Base class for DSP dialer control points
{ /// </summary>
public abstract class DspDialerBase : DspControlPoint
{
/// <summary>
/// Initializes a new instance of the DspDialerBase class
/// </summary>
/// <param name="key">The dialer control point key</param>
protected DspDialerBase(string key) : base(key) { } protected DspDialerBase(string key) : base(key) { }
} }
// Main program // Main program
// VTC // VTC
// ATC // ATC
// Mics, unusual // Mics, unusual
} }

View File

@@ -14,7 +14,9 @@ namespace PepperDash.Essentials.Devices.Common
/// </summary> /// </summary>
public class DeviceFactory public class DeviceFactory
{ {
/// <summary>
/// Initializes a new instance of the DeviceFactory class
/// </summary>
public DeviceFactory() public DeviceFactory()
{ {
var assy = Assembly.GetExecutingAssembly(); var assy = Assembly.GetExecutingAssembly();

View File

@@ -11,37 +11,53 @@ using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Displays namespace PepperDash.Essentials.Devices.Common.Displays
{ {
/// <summary> /// <summary>
/// Represents a BasicIrDisplay /// Represents a BasicIrDisplay
/// </summary> /// </summary>
public class BasicIrDisplay : DisplayBase, IBasicVolumeControls, IBridgeAdvanced public class BasicIrDisplay : DisplayBase, IBasicVolumeControls, IBridgeAdvanced
{ {
/// <summary> /// <summary>
/// Gets or sets the IrPort /// Gets or sets the IrPort
/// </summary> /// </summary>
public IrOutputPortController IrPort { get; private set; } public IrOutputPortController IrPort { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the IrPulseTime /// Gets or sets the IrPulseTime
/// </summary> /// </summary>
public ushort IrPulseTime { get; set; } public ushort IrPulseTime { get; set; }
protected Func<bool> PowerIsOnFeedbackFunc /// <summary>
{ /// Gets the power is on feedback function
get { return () => _PowerIsOn; } /// </summary>
} protected Func<bool> PowerIsOnFeedbackFunc
{
get { return () => _PowerIsOn; }
}
/// <summary>
/// Gets the is cooling down feedback function
/// </summary>
protected override Func<bool> IsCoolingDownFeedbackFunc protected override Func<bool> IsCoolingDownFeedbackFunc
{ {
get { return () => _IsCoolingDown; } get { return () => _IsCoolingDown; }
} }
/// <summary>
/// Gets the is warming up feedback function
/// </summary>
protected override Func<bool> IsWarmingUpFeedbackFunc protected override Func<bool> IsWarmingUpFeedbackFunc
{ {
get { return () => _IsWarmingUp; } get { return () => _IsWarmingUp; }
} }
bool _PowerIsOn; bool _PowerIsOn;
bool _IsWarmingUp; bool _IsWarmingUp;
bool _IsCoolingDown; bool _IsCoolingDown;
/// <summary>
/// Initializes a new instance of the BasicIrDisplay class
/// </summary>
/// <param name="key">The device key</param>
/// <param name="name">The device name</param>
/// <param name="port">The IR output port</param>
/// <param name="irDriverFilepath">The path to the IR driver file</param>
public BasicIrDisplay(string key, string name, IROutputPort port, string irDriverFilepath) public BasicIrDisplay(string key, string name, IROutputPort port, string irDriverFilepath)
: base(key, name) : base(key, name)
{ {
@@ -53,74 +69,74 @@ namespace PepperDash.Essentials.Devices.Common.Displays
InputPorts.AddRange(new RoutingPortCollection<RoutingInputPort> InputPorts.AddRange(new RoutingPortCollection<RoutingInputPort>
{ {
new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.Audio | eRoutingSignalType.Video, new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, new Action(Hdmi1), this, false), eRoutingPortConnectionType.Hdmi, new Action(Hdmi1), this, false),
new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video, new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, new Action(Hdmi2), this, false), eRoutingPortConnectionType.Hdmi, new Action(Hdmi2), this, false),
new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.Audio | eRoutingSignalType.Video, new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, new Action(Hdmi3), this, false), eRoutingPortConnectionType.Hdmi, new Action(Hdmi3), this, false),
new RoutingInputPort(RoutingPortNames.HdmiIn4, eRoutingSignalType.Audio | eRoutingSignalType.Video, new RoutingInputPort(RoutingPortNames.HdmiIn4, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, new Action(Hdmi4), this, false), eRoutingPortConnectionType.Hdmi, new Action(Hdmi4), this, false),
new RoutingInputPort(RoutingPortNames.ComponentIn, eRoutingSignalType.Audio | eRoutingSignalType.Video, new RoutingInputPort(RoutingPortNames.ComponentIn, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, new Action(Component1), this, false), eRoutingPortConnectionType.Hdmi, new Action(Component1), this, false),
new RoutingInputPort(RoutingPortNames.CompositeIn, eRoutingSignalType.Audio | eRoutingSignalType.Video, new RoutingInputPort(RoutingPortNames.CompositeIn, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, new Action(Video1), this, false), eRoutingPortConnectionType.Hdmi, new Action(Video1), this, false),
new RoutingInputPort(RoutingPortNames.AntennaIn, eRoutingSignalType.Audio | eRoutingSignalType.Video, new RoutingInputPort(RoutingPortNames.AntennaIn, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, new Action(Antenna), this, false), eRoutingPortConnectionType.Hdmi, new Action(Antenna), this, false),
}); });
} }
/// <summary> /// <summary>
/// Hdmi1 method /// Hdmi1 method
/// </summary> /// </summary>
public void Hdmi1() public void Hdmi1()
{ {
IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_1, IrPulseTime); IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_1, IrPulseTime);
} }
/// <summary> /// <summary>
/// Hdmi2 method /// Hdmi2 method
/// </summary> /// </summary>
public void Hdmi2() public void Hdmi2()
{ {
IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_2, IrPulseTime); IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_2, IrPulseTime);
} }
/// <summary> /// <summary>
/// Hdmi3 method /// Hdmi3 method
/// </summary> /// </summary>
public void Hdmi3() public void Hdmi3()
{ {
IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_3, IrPulseTime); IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_3, IrPulseTime);
} }
/// <summary> /// <summary>
/// Hdmi4 method /// Hdmi4 method
/// </summary> /// </summary>
public void Hdmi4() public void Hdmi4()
{ {
IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_4, IrPulseTime); IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_4, IrPulseTime);
} }
/// <summary> /// <summary>
/// Component1 method /// Component1 method
/// </summary> /// </summary>
public void Component1() public void Component1()
{ {
IrPort.Pulse(IROutputStandardCommands.IROut_COMPONENT_1, IrPulseTime); IrPort.Pulse(IROutputStandardCommands.IROut_COMPONENT_1, IrPulseTime);
} }
/// <summary> /// <summary>
/// Video1 method /// Video1 method
/// </summary> /// </summary>
public void Video1() public void Video1()
{ {
IrPort.Pulse(IROutputStandardCommands.IROut_VIDEO_1, IrPulseTime); IrPort.Pulse(IROutputStandardCommands.IROut_VIDEO_1, IrPulseTime);
} }
/// <summary> /// <summary>
/// Antenna method /// Antenna method
/// </summary> /// </summary>
public void Antenna() public void Antenna()
{ {
IrPort.Pulse(IROutputStandardCommands.IROut_ANTENNA, IrPulseTime); IrPort.Pulse(IROutputStandardCommands.IROut_ANTENNA, IrPulseTime);
@@ -128,31 +144,31 @@ namespace PepperDash.Essentials.Devices.Common.Displays
#region IPower Members #region IPower Members
/// <summary> /// <summary>
/// PowerOn method /// PowerOn method
/// </summary> /// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override void PowerOn() public override void PowerOn()
{ {
IrPort.Pulse(IROutputStandardCommands.IROut_POWER_ON, IrPulseTime); IrPort.Pulse(IROutputStandardCommands.IROut_POWER_ON, IrPulseTime);
_PowerIsOn = true; _PowerIsOn = true;
} }
/// <summary> /// <summary>
/// PowerOff method /// PowerOff method
/// </summary> /// </summary>
public override void PowerOff() public override void PowerOff()
{ {
_PowerIsOn = false; _PowerIsOn = false;
IrPort.Pulse(IROutputStandardCommands.IROut_POWER_OFF, IrPulseTime); IrPort.Pulse(IROutputStandardCommands.IROut_POWER_OFF, IrPulseTime);
} }
/// <summary> /// <summary>
/// PowerToggle method /// PowerToggle method
/// </summary> /// </summary>
public override void PowerToggle() public override void PowerToggle()
{ {
_PowerIsOn = false; _PowerIsOn = false;
IrPort.Pulse(IROutputStandardCommands.IROut_POWER, IrPulseTime); IrPort.Pulse(IROutputStandardCommands.IROut_POWER, IrPulseTime);
} }
@@ -160,25 +176,25 @@ namespace PepperDash.Essentials.Devices.Common.Displays
#region IBasicVolumeControls Members #region IBasicVolumeControls Members
/// <summary> /// <summary>
/// VolumeUp method /// VolumeUp method
/// </summary> /// </summary>
public void VolumeUp(bool pressRelease) public void VolumeUp(bool pressRelease)
{ {
IrPort.PressRelease(IROutputStandardCommands.IROut_VOL_PLUS, pressRelease); IrPort.PressRelease(IROutputStandardCommands.IROut_VOL_PLUS, pressRelease);
} }
/// <summary> /// <summary>
/// VolumeDown method /// VolumeDown method
/// </summary> /// </summary>
public void VolumeDown(bool pressRelease) public void VolumeDown(bool pressRelease)
{ {
IrPort.PressRelease(IROutputStandardCommands.IROut_VOL_MINUS, pressRelease); IrPort.PressRelease(IROutputStandardCommands.IROut_VOL_MINUS, pressRelease);
} }
/// <summary> /// <summary>
/// MuteToggle method /// MuteToggle method
/// </summary> /// </summary>
public void MuteToggle() public void MuteToggle()
{ {
IrPort.Pulse(IROutputStandardCommands.IROut_MUTE, 200); IrPort.Pulse(IROutputStandardCommands.IROut_MUTE, 200);
@@ -190,7 +206,8 @@ namespace PepperDash.Essentials.Devices.Common.Displays
{ {
_IsWarmingUp = true; _IsWarmingUp = true;
IsWarmingUpFeedback.FireUpdate(); IsWarmingUpFeedback.FireUpdate();
new CTimer(o => { new CTimer(o =>
{
_IsWarmingUp = false; _IsWarmingUp = false;
IsWarmingUpFeedback.FireUpdate(); IsWarmingUpFeedback.FireUpdate();
}, 10000); }, 10000);
@@ -213,13 +230,13 @@ namespace PepperDash.Essentials.Devices.Common.Displays
/// Typically called by the discovery routing algorithm. /// Typically called by the discovery routing algorithm.
/// </summary> /// </summary>
/// <param name="inputSelector">A delegate containing the input selector method to call</param> /// <param name="inputSelector">A delegate containing the input selector method to call</param>
/// <summary> /// <summary>
/// ExecuteSwitch method /// ExecuteSwitch method
/// </summary> /// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override void ExecuteSwitch(object inputSelector) public override void ExecuteSwitch(object inputSelector)
{ {
Debug.LogMessage(LogEventLevel.Verbose, this, "Switching to input '{0}'", (inputSelector as Action).ToString()); Debug.LogMessage(LogEventLevel.Verbose, this, "Switching to input '{0}'", (inputSelector as Action).ToString());
Action finishSwitch = () => Action finishSwitch = () =>
{ {
@@ -246,42 +263,45 @@ namespace PepperDash.Essentials.Devices.Common.Displays
#endregion #endregion
/// <summary> /// <summary>
/// LinkToApi method /// LinkToApi method
/// </summary> /// </summary>
public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
{ {
LinkDisplayToApi(this, trilist, joinStart, joinMapKey, bridge); LinkDisplayToApi(this, trilist, joinStart, joinMapKey, bridge);
} }
} }
/// <summary> /// <summary>
/// Represents a BasicIrDisplayFactory /// Represents a BasicIrDisplayFactory
/// </summary> /// </summary>
public class BasicIrDisplayFactory : EssentialsDeviceFactory<BasicIrDisplay> public class BasicIrDisplayFactory : EssentialsDeviceFactory<BasicIrDisplay>
{ {
public BasicIrDisplayFactory() /// <summary>
{ /// Initializes a new instance of the BasicIrDisplayFactory class
TypeNames = new List<string>() { "basicirdisplay" }; /// </summary>
} public BasicIrDisplayFactory()
{
TypeNames = new List<string>() { "basicirdisplay" };
}
/// <summary> /// <summary>
/// BuildDevice method /// BuildDevice method
/// </summary> /// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override EssentialsDevice BuildDevice(DeviceConfig dc) public override EssentialsDevice BuildDevice(DeviceConfig dc)
{ {
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new BasicIrDisplay Device"); Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new BasicIrDisplay Device");
var ir = IRPortHelper.GetIrPort(dc.Properties); var ir = IRPortHelper.GetIrPort(dc.Properties);
if (ir != null) if (ir != null)
{ {
var display = new BasicIrDisplay(dc.Key, dc.Name, ir.Port, ir.FileName); var display = new BasicIrDisplay(dc.Key, dc.Name, ir.Port, ir.FileName);
display.IrPulseTime = 200; // Set default pulse time for IR commands. display.IrPulseTime = 200; // Set default pulse time for IR commands.
return display; return display;
} }
return null; return null;
} }
} }
} }

View File

@@ -19,7 +19,7 @@ namespace PepperDash.Essentials.Devices.Common.Displays
/// Abstract base class for display devices that provides common display functionality /// Abstract base class for display devices that provides common display functionality
/// including power control, input switching, and routing capabilities. /// including power control, input switching, and routing capabilities.
/// </summary> /// </summary>
public abstract class DisplayBase : EssentialsDevice, IDisplay, ICurrentSources public abstract class DisplayBase : EssentialsDevice, IDisplay, ICurrentSources, IHasFeedback
{ {
private RoutingInputPort _currentInputPort; private RoutingInputPort _currentInputPort;
@@ -73,13 +73,11 @@ namespace PepperDash.Essentials.Devices.Common.Displays
var handler = CurrentSourceChange; var handler = CurrentSourceChange;
if (handler != null) handler?.Invoke(_CurrentSourceInfo, ChangeType.WillChange);
handler(_CurrentSourceInfo, ChangeType.WillChange);
_CurrentSourceInfo = value; _CurrentSourceInfo = value;
if (handler != null) handler?.Invoke(_CurrentSourceInfo, ChangeType.DidChange);
handler(_CurrentSourceInfo, ChangeType.DidChange);
} }
} }
SourceListItem _CurrentSourceInfo; SourceListItem _CurrentSourceInfo;
@@ -160,6 +158,9 @@ namespace PepperDash.Essentials.Devices.Common.Displays
IsCoolingDownFeedback = new BoolFeedback("IsCoolingDown", IsCoolingDownFeedbackFunc); IsCoolingDownFeedback = new BoolFeedback("IsCoolingDown", IsCoolingDownFeedbackFunc);
IsWarmingUpFeedback = new BoolFeedback("IsWarmingUp", IsWarmingUpFeedbackFunc); IsWarmingUpFeedback = new BoolFeedback("IsWarmingUp", IsWarmingUpFeedbackFunc);
Feedbacks.Add(IsCoolingDownFeedback);
Feedbacks.Add(IsWarmingUpFeedback);
InputPorts = new RoutingPortCollection<RoutingInputPort>(); InputPorts = new RoutingPortCollection<RoutingInputPort>();
CurrentSources = new Dictionary<eRoutingSignalType, SourceListItem> CurrentSources = new Dictionary<eRoutingSignalType, SourceListItem>
@@ -194,17 +195,8 @@ namespace PepperDash.Essentials.Devices.Common.Displays
/// Gets the collection of feedback objects for this display device. /// Gets the collection of feedback objects for this display device.
/// </summary> /// </summary>
/// <inheritdoc /> /// <inheritdoc />
public virtual FeedbackCollection<Feedback> Feedbacks public virtual FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>();
{
get
{
return new FeedbackCollection<Feedback>
{
IsCoolingDownFeedback,
IsWarmingUpFeedback
};
}
}
/// <summary> /// <summary>
/// Executes a switch to the specified input on the display device. Must be implemented by derived classes. /// Executes a switch to the specified input on the display device. Must be implemented by derived classes.
@@ -252,47 +244,35 @@ namespace PepperDash.Essentials.Devices.Common.Displays
/// <param name="joinMap">The join map configuration for the device.</param> /// <param name="joinMap">The join map configuration for the device.</param>
protected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, DisplayControllerJoinMap joinMap) protected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, DisplayControllerJoinMap joinMap)
{ {
Debug.LogMessage(LogEventLevel.Debug, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); this.LogDebug("Linking to Trilist {ipId}", trilist.ID.ToString("X"));
Debug.LogMessage(LogEventLevel.Information, "Linking to Display: {0}", displayDevice.Name); this.LogDebug("Linking to Display: {displayName}", displayDevice.Name);
trilist.StringInput[joinMap.Name.JoinNumber].StringValue = displayDevice.Name; trilist.StringInput[joinMap.Name.JoinNumber].StringValue = displayDevice.Name;
var commMonitor = displayDevice as ICommunicationMonitor; if (displayDevice is ICommunicationMonitor commMonitor)
if (commMonitor != null)
{ {
commMonitor.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); commMonitor.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]);
} }
// TODO: revisit this as there could be issues with this approach
var inputNumber = 0; var inputNumber = 0;
var inputKeys = new List<string>(); var inputKeys = new List<string>();
var inputNumberFeedback = new IntFeedback(() => inputNumber); var inputNumberFeedback = new IntFeedback("inputNumber", () => inputNumber);
// Two way feedbacks // Add input number feedback to the device feedback collection to keep it around...
var twoWayDisplay = displayDevice as TwoWayDisplayBase; Feedbacks.Add(inputNumberFeedback);
if (twoWayDisplay != null) // Two way feedbacks
if (displayDevice is TwoWayDisplayBase twoWayDisplay)
{ {
trilist.SetBool(joinMap.IsTwoWayDisplay.JoinNumber, true); trilist.SetBool(joinMap.IsTwoWayDisplay.JoinNumber, true);
twoWayDisplay.CurrentInputFeedback.OutputChange += (o, a) => Debug.LogMessage(LogEventLevel.Information, "CurrentInputFeedback_OutputChange {0}", a.StringValue); twoWayDisplay.CurrentInputFeedback.OutputChange += (o, a) => this.LogDebug("CurrentInputFeedback_OutputChange {input}", a.StringValue);
inputNumberFeedback.LinkInputSig(trilist.UShortInput[joinMap.InputSelect.JoinNumber]); inputNumberFeedback.LinkInputSig(trilist.UShortInput[joinMap.InputSelect.JoinNumber]);
}
// Power Off twoWayDisplay.PowerIsOnFeedback.OutputChange += (o, a) =>
trilist.SetSigTrueAction(joinMap.PowerOff.JoinNumber, () =>
{
inputNumber = 102;
inputNumberFeedback.FireUpdate();
displayDevice.PowerOff();
});
var twoWayDisplayDevice = displayDevice as TwoWayDisplayBase;
if (twoWayDisplayDevice != null)
{
twoWayDisplayDevice.PowerIsOnFeedback.OutputChange += (o, a) =>
{ {
if (!a.BoolValue) if (!a.BoolValue)
{ {
@@ -307,10 +287,18 @@ namespace PepperDash.Essentials.Devices.Common.Displays
} }
}; };
twoWayDisplayDevice.PowerIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.PowerOff.JoinNumber]); twoWayDisplay.PowerIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.PowerOff.JoinNumber]);
twoWayDisplayDevice.PowerIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PowerOn.JoinNumber]); twoWayDisplay.PowerIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PowerOn.JoinNumber]);
} }
// Power Off
trilist.SetSigTrueAction(joinMap.PowerOff.JoinNumber, () =>
{
inputNumber = 102;
inputNumberFeedback.FireUpdate();
displayDevice.PowerOff();
});
// PowerOn // PowerOn
trilist.SetSigTrueAction(joinMap.PowerOn.JoinNumber, () => trilist.SetSigTrueAction(joinMap.PowerOn.JoinNumber, () =>
{ {
@@ -320,44 +308,61 @@ namespace PepperDash.Essentials.Devices.Common.Displays
}); });
for (int i = 0; i < displayDevice.InputPorts.Count; i++) for (int i = 0; i < displayDevice.InputPorts.Count; i++)
{ {
if (i < joinMap.InputNamesOffset.JoinSpan) var localindex = i;
if (localindex >= joinMap.InputNamesOffset.JoinSpan)
{ {
inputKeys.Add(displayDevice.InputPorts[i].Key); this.LogWarning("Device has {inputCount} inputs. The Join Map allows up to {joinSpan} inputs. Discarding inputs {discardStart} - {discardEnd} from bridge.",
var tempKey = inputKeys.ElementAt(i); displayDevice.InputPorts.Count, joinMap.InputNamesOffset.JoinSpan, localindex + 1, displayDevice.InputPorts.Count);
trilist.SetSigTrueAction((ushort)(joinMap.InputSelectOffset.JoinNumber + i),
() => displayDevice.ExecuteSwitch(displayDevice.InputPorts[tempKey].Selector)); continue;
Debug.LogMessage(LogEventLevel.Verbose, displayDevice, "Setting Input Select Action on Digital Join {0} to Input: {1}",
joinMap.InputSelectOffset.JoinNumber + i, displayDevice.InputPorts[tempKey].Key.ToString());
trilist.StringInput[(ushort)(joinMap.InputNamesOffset.JoinNumber + i)].StringValue = displayDevice.InputPorts[i].Key.ToString();
} }
else else
Debug.LogMessage(LogEventLevel.Information, displayDevice, "Device has {0} inputs. The Join Map allows up to {1} inputs. Discarding inputs {2} - {3} from bridge.", {
displayDevice.InputPorts.Count, joinMap.InputNamesOffset.JoinSpan, i + 1, displayDevice.InputPorts.Count); inputKeys.Add(displayDevice.InputPorts[localindex].Key);
var tempKey = inputKeys.ElementAt(localindex);
trilist.SetSigTrueAction((ushort)(joinMap.InputSelectOffset.JoinNumber + localindex), () => displayDevice.ExecuteSwitch(displayDevice.InputPorts[tempKey].Selector));
this.LogDebug("Setting Input Select Action on Digital Join {joinNumber} to Input: {input}", joinMap.InputSelectOffset.JoinNumber + localindex, displayDevice.InputPorts[tempKey].Key);
trilist.SetString((uint)(joinMap.InputNamesOffset.JoinNumber + localindex), displayDevice.InputPorts[localindex].Key);
}
} }
Debug.LogMessage(LogEventLevel.Verbose, displayDevice, "Setting Input Select Action on Analog Join {0}", joinMap.InputSelect); this.LogDebug("Setting Input Select Action on Analog Join {inputSelectJoin}", joinMap.InputSelect);
trilist.SetUShortSigAction(joinMap.InputSelect.JoinNumber, (a) =>
trilist.SetUShortSigAction(joinMap.InputSelect.JoinNumber, (requestedInput) =>
{ {
if (a == 0) if (requestedInput == 0)
{ {
displayDevice.PowerOff(); displayDevice.PowerOff();
inputNumber = 0; inputNumber = 0;
return;
} }
else if (a > 0 && a < displayDevice.InputPorts.Count && a != inputNumber)
// using 1-based indexing for inputs coming from SIMPL, so need to check if the input is <= the count, not <
if (requestedInput > 0 && requestedInput <= displayDevice.InputPorts.Count && requestedInput != inputNumber)
{ {
displayDevice.ExecuteSwitch(displayDevice.InputPorts.ElementAt(a - 1).Selector); displayDevice.ExecuteSwitch(displayDevice.InputPorts.ElementAt(requestedInput - 1).Selector);
inputNumber = a;
inputNumber = requestedInput;
return;
} }
else if (a == 102)
if (requestedInput == 102)
{ {
displayDevice.PowerToggle(); displayDevice.PowerToggle();
return;
}
if (displayDevice is TwoWayDisplayBase)
{
inputNumberFeedback?.FireUpdate();
} }
if (twoWayDisplay != null)
inputNumberFeedback.FireUpdate();
}); });
@@ -430,95 +435,4 @@ namespace PepperDash.Essentials.Devices.Common.Displays
} }
} }
/// <summary>
/// Abstract base class for two-way display devices that provide feedback capabilities.
/// Extends DisplayBase with routing feedback and power control feedback functionality.
/// </summary>
public abstract class TwoWayDisplayBase : DisplayBase, IRoutingFeedback, IHasPowerControlWithFeedback
{
/// <summary>
/// Gets feedback for the current input selection on the display.
/// </summary>
public StringFeedback CurrentInputFeedback { get; private set; }
/// <summary>
/// Abstract function that must be implemented by derived classes to provide the current input feedback value.
/// Must be implemented by concrete sub-classes.
/// </summary>
abstract protected Func<string> CurrentInputFeedbackFunc { get; }
/// <summary>
/// Gets feedback indicating whether the display is currently powered on.
/// </summary>
public BoolFeedback PowerIsOnFeedback { get; protected set; }
/// <summary>
/// Abstract function that must be implemented by derived classes to provide the power state feedback value.
/// Must be implemented by concrete sub-classes.
/// </summary>
abstract protected Func<bool> PowerIsOnFeedbackFunc { get; }
/// <summary>
/// Gets the default mock display instance for testing and development purposes.
/// </summary>
public static MockDisplay DefaultDisplay
{
get
{
if (_DefaultDisplay == null)
_DefaultDisplay = new MockDisplay("default", "Default Display");
return _DefaultDisplay;
}
}
static MockDisplay _DefaultDisplay;
/// <summary>
/// Initializes a new instance of the TwoWayDisplayBase class.
/// </summary>
/// <param name="key">The unique key identifier for this display device.</param>
/// <param name="name">The friendly name for this display device.</param>
public TwoWayDisplayBase(string key, string name)
: base(key, name)
{
CurrentInputFeedback = new StringFeedback(CurrentInputFeedbackFunc);
WarmupTime = 7000;
CooldownTime = 15000;
PowerIsOnFeedback = new BoolFeedback("PowerOnFeedback", PowerIsOnFeedbackFunc);
Feedbacks.Add(CurrentInputFeedback);
Feedbacks.Add(PowerIsOnFeedback);
PowerIsOnFeedback.OutputChange += PowerIsOnFeedback_OutputChange;
}
void PowerIsOnFeedback_OutputChange(object sender, EventArgs e)
{
if (UsageTracker != null)
{
if (PowerIsOnFeedback.BoolValue)
UsageTracker.StartDeviceUsage();
else
UsageTracker.EndDeviceUsage();
}
}
/// <summary>
/// Event that is raised when a numeric switch change occurs on the display.
/// </summary>
public event EventHandler<RoutingNumericEventArgs> NumericSwitchChange;
/// <summary>
/// Raise an event when the status of a switch object changes.
/// </summary>
/// <param name="e">Arguments defined as IKeyName sender, output, input, and eRoutingSignalType</param>
protected void OnSwitchChange(RoutingNumericEventArgs e)
{
var newEvent = NumericSwitchChange;
if (newEvent != null) newEvent(this, e);
}
}
} }

View File

@@ -1,38 +1,89 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Displays namespace PepperDash.Essentials.Devices.Displays
{ {
/// <summary> /// <summary>
/// Defines the contract for IInputHdmi1 /// Defines the contract for IInputHdmi1
/// </summary> /// </summary>
public interface IInputHdmi1 { void InputHdmi1(); } [Obsolete()]
public interface IInputHdmi1
{
/// <summary>
/// Switches to HDMI 1 input
/// </summary>
void InputHdmi1();
}
/// <summary> /// <summary>
/// Defines the contract for IInputHdmi2 /// Defines the contract for IInputHdmi2
/// </summary> /// </summary>
public interface IInputHdmi2 { void InputHdmi2(); } [Obsolete()]
public interface IInputHdmi2
{
/// <summary>
/// Switches to HDMI 2 input
/// </summary>
void InputHdmi2();
}
/// <summary> /// <summary>
/// Defines the contract for IInputHdmi3 /// Defines the contract for IInputHdmi3
/// </summary> /// </summary>
public interface IInputHdmi3 { void InputHdmi3(); } [Obsolete()]
public interface IInputHdmi3
{
/// <summary>
/// Switches to HDMI 3 input
/// </summary>
void InputHdmi3();
}
/// <summary> /// <summary>
/// Defines the contract for IInputHdmi4 /// Defines the contract for IInputHdmi4
/// </summary> /// </summary>
public interface IInputHdmi4 { void InputHdmi4(); } [Obsolete()]
public interface IInputHdmi4
{
/// <summary>
/// Switches to HDMI 4 input
/// </summary>
void InputHdmi4();
}
/// <summary> /// <summary>
/// Defines the contract for IInputDisplayPort1 /// Defines the contract for IInputDisplayPort1
/// </summary> /// </summary>
public interface IInputDisplayPort1 { void InputDisplayPort1(); } [Obsolete()]
public interface IInputDisplayPort1
{
/// <summary>
/// Switches to DisplayPort 1 input
/// </summary>
void InputDisplayPort1();
}
/// <summary> /// <summary>
/// Defines the contract for IInputDisplayPort2 /// Defines the contract for IInputDisplayPort2
/// </summary> /// </summary>
public interface IInputDisplayPort2 { void InputDisplayPort2(); } [Obsolete()]
public interface IInputDisplayPort2
{
/// <summary>
/// Switches to DisplayPort 2 input
/// </summary>
void InputDisplayPort2();
}
/// <summary> /// <summary>
/// Defines the contract for IInputVga1 /// Defines the contract for IInputVga1
/// </summary> /// </summary>
public interface IInputVga1 { void InputVga1(); } [Obsolete()]
public interface IInputVga1
{
/// <summary>
/// Switches to VGA 1 input
/// </summary>
void InputVga1();
}
} }

View File

@@ -16,16 +16,19 @@ namespace PepperDash.Essentials.Devices.Common.Displays
/// Represents a MockDisplay /// Represents a MockDisplay
/// </summary> /// </summary>
public class MockDisplay : TwoWayDisplayBase, IBasicVolumeWithFeedback, IBridgeAdvanced, IHasInputs<string>, IRoutingSinkWithSwitchingWithInputPort, IHasPowerControlWithFeedback public class MockDisplay : TwoWayDisplayBase, IBasicVolumeWithFeedback, IBridgeAdvanced, IHasInputs<string>, IRoutingSinkWithSwitchingWithInputPort, IHasPowerControlWithFeedback
{ {
/// <summary> /// <summary>
/// Gets or sets the Inputs /// Gets or sets the Inputs
/// </summary> /// </summary>
public ISelectableItems<string> Inputs { get; private set; } public ISelectableItems<string> Inputs { get; private set; }
bool _PowerIsOn; bool _PowerIsOn;
bool _IsWarmingUp; bool _IsWarmingUp;
bool _IsCoolingDown; bool _IsCoolingDown;
/// <summary>
/// Gets the power is on feedback function
/// </summary>
protected override Func<bool> PowerIsOnFeedbackFunc protected override Func<bool> PowerIsOnFeedbackFunc
{ {
get get
@@ -34,8 +37,12 @@ namespace PepperDash.Essentials.Devices.Common.Displays
{ {
return _PowerIsOn; return _PowerIsOn;
}; };
} } }
protected override Func<bool> IsCoolingDownFeedbackFunc }
/// <summary>
/// Gets the is cooling down feedback function
/// </summary>
protected override Func<bool> IsCoolingDownFeedbackFunc
{ {
get get
{ {
@@ -45,7 +52,10 @@ namespace PepperDash.Essentials.Devices.Common.Displays
}; };
} }
} }
protected override Func<bool> IsWarmingUpFeedbackFunc /// <summary>
/// Gets the is warming up feedback function
/// </summary>
protected override Func<bool> IsWarmingUpFeedbackFunc
{ {
get get
{ {
@@ -55,120 +65,128 @@ namespace PepperDash.Essentials.Devices.Common.Displays
}; };
} }
} }
/// <summary>
/// Gets the current input feedback function
/// </summary>
protected override Func<string> CurrentInputFeedbackFunc { get { return () => Inputs.CurrentItem; } } protected override Func<string> CurrentInputFeedbackFunc { get { return () => Inputs.CurrentItem; } }
int VolumeHeldRepeatInterval = 200; int VolumeHeldRepeatInterval = 200;
ushort VolumeInterval = 655; ushort VolumeInterval = 655;
ushort _FakeVolumeLevel = 31768; ushort _FakeVolumeLevel = 31768;
bool _IsMuted; bool _IsMuted;
public MockDisplay(string key, string name) /// <summary>
: base(key, name) /// Initializes a new instance of the MockDisplay class
{ /// </summary>
/// <param name="key">The device key</param>
/// <param name="name">The device name</param>
public MockDisplay(string key, string name)
: base(key, name)
{
Inputs = new MockDisplayInputs Inputs = new MockDisplayInputs
{ {
Items = new Dictionary<string, ISelectableItem> Items = new Dictionary<string, ISelectableItem>
{ {
{ "HDMI1", new MockDisplayInput ( "HDMI1", "HDMI 1",this ) }, { "HDMI1", new MockDisplayInput ( "HDMI1", "HDMI 1",this ) },
{ "HDMI2", new MockDisplayInput ("HDMI2", "HDMI 2",this ) }, { "HDMI2", new MockDisplayInput ("HDMI2", "HDMI 2",this ) },
{ "HDMI3", new MockDisplayInput ("HDMI3", "HDMI 3",this ) }, { "HDMI3", new MockDisplayInput ("HDMI3", "HDMI 3",this ) },
{ "HDMI4", new MockDisplayInput ("HDMI4", "HDMI 4",this )}, { "HDMI4", new MockDisplayInput ("HDMI4", "HDMI 4",this )},
{ "DP", new MockDisplayInput ("DP", "DisplayPort", this ) } { "DP", new MockDisplayInput ("DP", "DisplayPort", this ) }
} }
}; };
Inputs.CurrentItemChanged += (o, a) => CurrentInputFeedback.FireUpdate(); Inputs.CurrentItemChanged += (o, a) => CurrentInputFeedback.FireUpdate();
var hdmiIn1 = new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, "HDMI1", this);
var hdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, "HDMI2", this);
var hdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, "HDMI3", this);
var hdmiIn4 = new RoutingInputPort(RoutingPortNames.HdmiIn4, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, "HDMI4", this);
var dpIn = new RoutingInputPort(RoutingPortNames.DisplayPortIn, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.DisplayPort, "DP", this);
InputPorts.AddRange(new[] { hdmiIn1, hdmiIn2, hdmiIn3, hdmiIn4, dpIn });
VolumeLevelFeedback = new IntFeedback(() => { return _FakeVolumeLevel; }); var hdmiIn1 = new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo,
MuteFeedback = new BoolFeedback("MuteOn", () => _IsMuted); eRoutingPortConnectionType.Hdmi, "HDMI1", this);
var hdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, "HDMI2", this);
var hdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, "HDMI3", this);
var hdmiIn4 = new RoutingInputPort(RoutingPortNames.HdmiIn4, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, "HDMI4", this);
var dpIn = new RoutingInputPort(RoutingPortNames.DisplayPortIn, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.DisplayPort, "DP", this);
InputPorts.AddRange(new[] { hdmiIn1, hdmiIn2, hdmiIn3, hdmiIn4, dpIn });
VolumeLevelFeedback = new IntFeedback("volume", () => { return _FakeVolumeLevel; });
MuteFeedback = new BoolFeedback("muteOn", () => _IsMuted);
WarmupTime = 10000; WarmupTime = 10000;
CooldownTime = 10000; CooldownTime = 10000;
} }
/// <summary> /// <summary>
/// PowerOn method /// PowerOn method
/// </summary> /// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override void PowerOn() public override void PowerOn()
{ {
if (!PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown) if (!PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown)
{ {
_IsWarmingUp = true; _IsWarmingUp = true;
IsWarmingUpFeedback.InvokeFireUpdate(); IsWarmingUpFeedback.InvokeFireUpdate();
// Fake power-up cycle // Fake power-up cycle
WarmupTimer = new CTimer(o => WarmupTimer = new CTimer(o =>
{ {
_IsWarmingUp = false; _IsWarmingUp = false;
_PowerIsOn = true; _PowerIsOn = true;
IsWarmingUpFeedback.InvokeFireUpdate(); IsWarmingUpFeedback.InvokeFireUpdate();
PowerIsOnFeedback.InvokeFireUpdate(); PowerIsOnFeedback.InvokeFireUpdate();
}, WarmupTime); }, WarmupTime);
} }
} }
/// <summary> /// <summary>
/// PowerOff method /// PowerOff method
/// </summary> /// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override void PowerOff() public override void PowerOff()
{ {
// If a display has unreliable-power off feedback, just override this and // If a display has unreliable-power off feedback, just override this and
// remove this check. // remove this check.
if (PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown) if (PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown)
{ {
_IsCoolingDown = true; _IsCoolingDown = true;
IsCoolingDownFeedback.InvokeFireUpdate(); IsCoolingDownFeedback.InvokeFireUpdate();
// Fake cool-down cycle // Fake cool-down cycle
CooldownTimer = new CTimer(o => CooldownTimer = new CTimer(o =>
{ {
Debug.LogMessage(LogEventLevel.Verbose, "Cooldown timer ending", this); Debug.LogMessage(LogEventLevel.Verbose, "Cooldown timer ending", this);
_IsCoolingDown = false; _IsCoolingDown = false;
IsCoolingDownFeedback.InvokeFireUpdate(); IsCoolingDownFeedback.InvokeFireUpdate();
_PowerIsOn = false; _PowerIsOn = false;
PowerIsOnFeedback.InvokeFireUpdate(); PowerIsOnFeedback.InvokeFireUpdate();
}, CooldownTime); }, CooldownTime);
} }
} }
/// <summary>
/// PowerToggle method
/// </summary>
/// <inheritdoc />
public override void PowerToggle()
{
if (PowerIsOnFeedback.BoolValue && !IsWarmingUpFeedback.BoolValue)
PowerOff();
else if (!PowerIsOnFeedback.BoolValue && !IsCoolingDownFeedback.BoolValue)
PowerOn();
}
/// <summary> /// <summary>
/// ExecuteSwitch method /// PowerToggle method
/// </summary> /// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override void ExecuteSwitch(object selector) public override void PowerToggle()
{ {
if (PowerIsOnFeedback.BoolValue && !IsWarmingUpFeedback.BoolValue)
PowerOff();
else if (!PowerIsOnFeedback.BoolValue && !IsCoolingDownFeedback.BoolValue)
PowerOn();
}
/// <summary>
/// ExecuteSwitch method
/// </summary>
/// <inheritdoc />
public override void ExecuteSwitch(object selector)
{
try try
{ {
Debug.LogMessage(LogEventLevel.Verbose, "ExecuteSwitch: {0}", this, selector); Debug.LogMessage(LogEventLevel.Verbose, "ExecuteSwitch: {0}", this, selector);
if (!_PowerIsOn) if (!_PowerIsOn)
{ {
PowerOn(); PowerOn();
} }
if (!Inputs.Items.TryGetValue(selector.ToString(), out var input)) if (!Inputs.Items.TryGetValue(selector.ToString(), out var input))
return; return;
@@ -184,13 +202,14 @@ namespace PepperDash.Essentials.Devices.Common.Displays
if (inputPort == null) if (inputPort == null)
{ {
Debug.LogMessage(LogEventLevel.Verbose, "Unable to find input port for selector {selector}", this, selector); Debug.LogMessage(LogEventLevel.Verbose, "Unable to find input port for selector {selector}", this, selector);
return; return;
} }
Debug.LogMessage(LogEventLevel.Verbose, "Setting current input port to {inputPort}", this, inputPort); Debug.LogMessage(LogEventLevel.Verbose, "Setting current input port to {inputPort}", this, inputPort);
CurrentInputPort = inputPort; CurrentInputPort = inputPort;
} catch (Exception ex) }
catch (Exception ex)
{ {
Debug.LogMessage(ex, "Error making switch: {Exception}", this, ex); Debug.LogMessage(ex, "Error making switch: {Exception}", this, ex);
} }
@@ -201,14 +220,14 @@ namespace PepperDash.Essentials.Devices.Common.Displays
/// </summary> /// </summary>
public void SetInput(string selector) public void SetInput(string selector)
{ {
ISelectableItem currentInput = null; ISelectableItem currentInput = null;
try
{
currentInput = Inputs.Items.SingleOrDefault(Inputs => Inputs.Value.IsSelected).Value;
}
catch { }
try
{
currentInput = Inputs.Items.SingleOrDefault(Inputs => Inputs.Value.IsSelected).Value;
}
catch { }
if (currentInput != null) if (currentInput != null)
{ {
@@ -216,12 +235,12 @@ namespace PepperDash.Essentials.Devices.Common.Displays
currentInput.IsSelected = false; currentInput.IsSelected = false;
} }
if (!Inputs.Items.TryGetValue(selector, out var input)) if (!Inputs.Items.TryGetValue(selector, out var input))
return; return;
input.IsSelected = true; input.IsSelected = true;
Inputs.CurrentItem = selector; Inputs.CurrentItem = selector;
} }
@@ -232,37 +251,37 @@ namespace PepperDash.Essentials.Devices.Common.Displays
/// </summary> /// </summary>
public IntFeedback VolumeLevelFeedback { get; private set; } public IntFeedback VolumeLevelFeedback { get; private set; }
/// <summary> /// <summary>
/// SetVolume method /// SetVolume method
/// </summary> /// </summary>
public void SetVolume(ushort level) public void SetVolume(ushort level)
{ {
_FakeVolumeLevel = level; _FakeVolumeLevel = level;
VolumeLevelFeedback.InvokeFireUpdate(); VolumeLevelFeedback.InvokeFireUpdate();
} }
/// <summary> /// <summary>
/// MuteOn method /// MuteOn method
/// </summary> /// </summary>
public void MuteOn() public void MuteOn()
{ {
_IsMuted = true; _IsMuted = true;
MuteFeedback.InvokeFireUpdate(); MuteFeedback.InvokeFireUpdate();
} }
/// <summary> /// <summary>
/// MuteOff method /// MuteOff method
/// </summary> /// </summary>
public void MuteOff() public void MuteOff()
{ {
_IsMuted = false; _IsMuted = false;
MuteFeedback.InvokeFireUpdate(); MuteFeedback.InvokeFireUpdate();
} }
/// <summary> /// <summary>
/// Gets or sets the MuteFeedback /// Gets or sets the MuteFeedback
/// </summary> /// </summary>
public BoolFeedback MuteFeedback { get; private set; } public BoolFeedback MuteFeedback { get; private set; }
#endregion #endregion
@@ -273,74 +292,77 @@ namespace PepperDash.Essentials.Devices.Common.Displays
/// VolumeUp method /// VolumeUp method
/// </summary> /// </summary>
public void VolumeUp(bool pressRelease) public void VolumeUp(bool pressRelease)
{ {
//while (pressRelease) //while (pressRelease)
//{ //{
Debug.LogMessage(LogEventLevel.Verbose, this, "Volume Down {0}", pressRelease); Debug.LogMessage(LogEventLevel.Verbose, this, "Volume Down {0}", pressRelease);
if (pressRelease) if (pressRelease)
{ {
var newLevel = _FakeVolumeLevel + VolumeInterval; var newLevel = _FakeVolumeLevel + VolumeInterval;
SetVolume((ushort)newLevel); SetVolume((ushort)newLevel);
CrestronEnvironment.Sleep(VolumeHeldRepeatInterval); CrestronEnvironment.Sleep(VolumeHeldRepeatInterval);
} }
//} //}
} }
/// <summary> /// <summary>
/// VolumeDown method /// VolumeDown method
/// </summary> /// </summary>
public void VolumeDown(bool pressRelease) public void VolumeDown(bool pressRelease)
{ {
//while (pressRelease) //while (pressRelease)
//{ //{
Debug.LogMessage(LogEventLevel.Verbose, this, "Volume Up {0}", pressRelease); Debug.LogMessage(LogEventLevel.Verbose, this, "Volume Up {0}", pressRelease);
if (pressRelease) if (pressRelease)
{ {
var newLevel = _FakeVolumeLevel - VolumeInterval; var newLevel = _FakeVolumeLevel - VolumeInterval;
SetVolume((ushort)newLevel); SetVolume((ushort)newLevel);
CrestronEnvironment.Sleep(VolumeHeldRepeatInterval); CrestronEnvironment.Sleep(VolumeHeldRepeatInterval);
} }
//} //}
} }
/// <summary> /// <summary>
/// MuteToggle method /// MuteToggle method
/// </summary> /// </summary>
public void MuteToggle() public void MuteToggle()
{ {
_IsMuted = !_IsMuted; _IsMuted = !_IsMuted;
MuteFeedback.InvokeFireUpdate(); MuteFeedback.InvokeFireUpdate();
} }
#endregion #endregion
/// <summary> /// <summary>
/// LinkToApi method /// LinkToApi method
/// </summary> /// </summary>
public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
{ {
LinkDisplayToApi(this, trilist, joinStart, joinMapKey, bridge); LinkDisplayToApi(this, trilist, joinStart, joinMapKey, bridge);
} }
} }
/// <summary> /// <summary>
/// Represents a MockDisplayFactory /// Represents a MockDisplayFactory
/// </summary> /// </summary>
public class MockDisplayFactory : EssentialsDeviceFactory<MockDisplay> public class MockDisplayFactory : EssentialsDeviceFactory<MockDisplay>
{ {
public MockDisplayFactory() /// <summary>
{ /// Initializes a new instance of the MockDisplayFactory class
TypeNames = new List<string>() { "mockdisplay" , "mockdisplay2" }; /// </summary>
} public MockDisplayFactory()
{
TypeNames = new List<string>() { "mockdisplay", "mockdisplay2" };
}
/// <summary> /// <summary>
/// BuildDevice method /// BuildDevice method
/// </summary> /// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override EssentialsDevice BuildDevice(DeviceConfig dc) public override EssentialsDevice BuildDevice(DeviceConfig dc)
{ {
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Mock Display Device"); Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Mock Display Device");
return new MockDisplay(dc.Key, dc.Name); return new MockDisplay(dc.Key, dc.Name);
} }
} }
} }

View File

@@ -1,10 +1,6 @@
using PepperDash.Essentials.Core.DeviceTypeInterfaces; using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Displays namespace PepperDash.Essentials.Devices.Common.Displays
{ {
@@ -15,6 +11,9 @@ namespace PepperDash.Essentials.Devices.Common.Displays
{ {
private Dictionary<string, ISelectableItem> _items; private Dictionary<string, ISelectableItem> _items;
/// <summary>
/// Gets or sets the collection of selectable items
/// </summary>
public Dictionary<string, ISelectableItem> Items public Dictionary<string, ISelectableItem> Items
{ {
get get
@@ -34,8 +33,11 @@ namespace PepperDash.Essentials.Devices.Common.Displays
private string _currentItem; private string _currentItem;
/// <summary>
/// Gets or sets the currently selected item
/// </summary>
public string CurrentItem public string CurrentItem
{ {
get get
{ {
return _currentItem; return _currentItem;
@@ -51,7 +53,13 @@ namespace PepperDash.Essentials.Devices.Common.Displays
} }
} }
/// <summary>
/// Occurs when the items collection is updated
/// </summary>
public event EventHandler ItemsUpdated; public event EventHandler ItemsUpdated;
/// <summary>
/// Occurs when the current item changes
/// </summary>
public event EventHandler CurrentItemChanged; public event EventHandler CurrentItemChanged;
} }
@@ -63,7 +71,10 @@ namespace PepperDash.Essentials.Devices.Common.Displays
private MockDisplay _parent; private MockDisplay _parent;
private bool _isSelected; private bool _isSelected;
/// <summary>
/// Gets or sets a value indicating whether this input is selected
/// </summary>
public bool IsSelected public bool IsSelected
{ {
get get
@@ -91,8 +102,17 @@ namespace PepperDash.Essentials.Devices.Common.Displays
/// </summary> /// </summary>
public string Key { get; set; } public string Key { get; set; }
/// <summary>
/// Occurs when this item is updated
/// </summary>
public event EventHandler ItemUpdated; public event EventHandler ItemUpdated;
/// <summary>
/// Initializes a new instance of the MockDisplayInput class
/// </summary>
/// <param name="key">The input key</param>
/// <param name="name">The input name</param>
/// <param name="parent">The parent mock display</param>
public MockDisplayInput(string key, string name, MockDisplay parent) public MockDisplayInput(string key, string name, MockDisplay parent)
{ {
Key = key; Key = key;
@@ -107,7 +127,7 @@ namespace PepperDash.Essentials.Devices.Common.Displays
{ {
if (!_parent.PowerIsOnFeedback.BoolValue) _parent.PowerOn(); if (!_parent.PowerIsOnFeedback.BoolValue) _parent.PowerOn();
foreach(var input in _parent.Inputs.Items) foreach (var input in _parent.Inputs.Items)
{ {
input.Value.IsSelected = input.Key == this.Key; input.Value.IsSelected = input.Key == this.Key;
} }

View File

@@ -1,14 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.CrestronIO; using PepperDash.Essentials.Core.CrestronIO;
using PepperDash.Essentials.Core.DeviceTypeInterfaces; using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using PepperDash.Essentials.Devices.Common;
using Serilog.Events; using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Shades namespace PepperDash.Essentials.Devices.Common.Shades
@@ -28,6 +25,9 @@ namespace PepperDash.Essentials.Devices.Common.Shades
ISwitchedOutput LowerRelay; ISwitchedOutput LowerRelay;
ISwitchedOutput LatchedRelay; ISwitchedOutput LatchedRelay;
/// <summary>
/// Gets or sets the InUpPosition
/// </summary>
public bool InUpPosition public bool InUpPosition
{ {
get { return _isInUpPosition; } get { return _isInUpPosition; }
@@ -41,10 +41,12 @@ namespace PepperDash.Essentials.Devices.Common.Shades
} }
private bool _isInUpPosition { get; set; } private bool _isInUpPosition { get; set; }
/// <summary> /// <summary>
/// Gets or sets the Type /// Gets or sets the Type
/// </summary> /// </summary>
public eScreenLiftControlType Type { get; private set; } public eScreenLiftControlType Type { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the Mode /// Gets or sets the Mode
/// </summary> /// </summary>
@@ -54,13 +56,20 @@ namespace PepperDash.Essentials.Devices.Common.Shades
/// Gets or sets the DisplayDeviceKey /// Gets or sets the DisplayDeviceKey
/// </summary> /// </summary>
public string DisplayDeviceKey { get; private set; } public string DisplayDeviceKey { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the IsInUpPosition /// Gets or sets the IsInUpPosition
/// </summary> /// </summary>
public BoolFeedback IsInUpPosition { get; private set; } public BoolFeedback IsInUpPosition { get; private set; }
/// <summary>
/// Event that fires when the position changes
/// </summary>
public event EventHandler<EventArgs> PositionChanged; public event EventHandler<EventArgs> PositionChanged;
/// <summary>
/// Constructor for ScreenLiftController
/// </summary>
public ScreenLiftController(string key, string name, ScreenLiftControllerConfigProperties config) public ScreenLiftController(string key, string name, ScreenLiftControllerConfigProperties config)
: base(key, name) : base(key, name)
{ {
@@ -69,7 +78,7 @@ namespace PepperDash.Essentials.Devices.Common.Shades
Mode = Config.Mode; Mode = Config.Mode;
Type = Config.Type; Type = Config.Type;
IsInUpPosition = new BoolFeedback(() => _isInUpPosition); IsInUpPosition = new BoolFeedback("isInUpPosition", () => _isInUpPosition);
switch (Mode) switch (Mode)
{ {
@@ -206,14 +215,14 @@ namespace PepperDash.Essentials.Devices.Common.Shades
/// <summary> /// <summary>
/// Attempts to get the port on teh specified device from config /// Attempts to get the port on teh specified device from config
/// </summary> /// </summary>
/// <param name="relayConfig"></param> /// <param name="relayKey"></param>
/// <returns></returns> /// <returns></returns>
ISwitchedOutput GetSwitchedOutputFromDevice(string relayKey) ISwitchedOutput GetSwitchedOutputFromDevice(string relayKey)
{ {
var portDevice = DeviceManager.GetDeviceForKey(relayKey); var portDevice = DeviceManager.GetDeviceForKey(relayKey);
if (portDevice != null) if (portDevice != null)
{ {
return (portDevice as ISwitchedOutput); return portDevice as ISwitchedOutput;
} }
else else
{ {
@@ -238,58 +247,14 @@ namespace PepperDash.Essentials.Devices.Common.Shades
} }
/// <summary>
/// Represents a ScreenLiftControllerConfigProperties
/// </summary>
public class ScreenLiftControllerConfigProperties
{
[JsonProperty("displayDeviceKey")]
/// <summary>
/// Gets or sets the DisplayDeviceKey
/// </summary>
public string DisplayDeviceKey { get; set; }
[JsonProperty("type")]
[JsonConverter(typeof(StringEnumConverter))]
/// <summary>
/// Gets or sets the Type
/// </summary>
public eScreenLiftControlType Type { get; set; }
[JsonProperty("mode")]
[JsonConverter(typeof(StringEnumConverter))]
/// <summary>
/// Gets or sets the Mode
/// </summary>
public eScreenLiftControlMode Mode { get; set; }
[JsonProperty("relays")]
public Dictionary<string,ScreenLiftRelaysConfig> Relays { get; set; }
}
/// <summary>
/// Represents a ScreenLiftRelaysConfig
/// </summary>
public class ScreenLiftRelaysConfig
{
[JsonProperty("deviceKey")]
/// <summary>
/// Gets or sets the DeviceKey
/// </summary>
public string DeviceKey { get; set; }
[JsonProperty("pulseTimeInMs")]
/// <summary>
/// Gets or sets the PulseTimeInMs
/// </summary>
public int PulseTimeInMs { get; set; }
}
/// <summary> /// <summary>
/// Represents a ScreenLiftControllerFactory /// Represents a ScreenLiftControllerFactory
/// </summary> /// </summary>
public class ScreenLiftControllerFactory : EssentialsDeviceFactory<RelayControlledShade> public class ScreenLiftControllerFactory : EssentialsDeviceFactory<RelayControlledShade>
{ {
/// <summary>
/// Constructor for ScreenLiftControllerFactory
/// </summary>
public ScreenLiftControllerFactory() public ScreenLiftControllerFactory()
{ {
TypeNames = new List<string>() { "screenliftcontroller" }; TypeNames = new List<string>() { "screenliftcontroller" };
@@ -307,13 +272,4 @@ namespace PepperDash.Essentials.Devices.Common.Shades
return new ScreenLiftController(dc.Key, dc.Name, props); return new ScreenLiftController(dc.Key, dc.Name, props);
} }
} }
/// <summary>
/// Enumeration of eScreenLiftControlMode values
/// </summary>
public enum eScreenLiftControlMode
{
momentary,
latched
}
} }

View File

@@ -0,0 +1,41 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
namespace PepperDash.Essentials.Devices.Common.Shades
{
/// <summary>
/// Represents a ScreenLiftControllerConfigProperties
/// </summary>
public class ScreenLiftControllerConfigProperties
{
/// <summary>
/// Gets or sets the DisplayDeviceKey
/// </summary>
[JsonProperty("displayDeviceKey")]
public string DisplayDeviceKey { get; set; }
/// <summary>
/// Gets or sets the Type
/// </summary>
[JsonProperty("type")]
[JsonConverter(typeof(StringEnumConverter))]
public eScreenLiftControlType Type { get; set; }
/// <summary>
/// Gets or sets the Mode
/// </summary>
[JsonProperty("mode")]
[JsonConverter(typeof(StringEnumConverter))]
public eScreenLiftControlMode Mode { get; set; }
/// <summary>
/// Gets or sets the Relays
/// </summary>
[JsonProperty("relays")]
public Dictionary<string, ScreenLiftRelaysConfig> Relays { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.Shades
{
/// <summary>
/// Represents a ScreenLiftRelaysConfig
/// </summary>
public class ScreenLiftRelaysConfig
{
/// <summary>
/// Gets or sets the DeviceKey
/// </summary>
[JsonProperty("deviceKey")]
public string DeviceKey { get; set; }
/// <summary>
/// Gets or sets the PulseTimeInMs
/// </summary>
[JsonProperty("pulseTimeInMs")]
public int PulseTimeInMs { get; set; }
}
}

View File

@@ -0,0 +1,95 @@
using System;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Displays
{
/// <summary>
/// Abstract base class for two-way display devices that provide feedback capabilities.
/// Extends DisplayBase with routing feedback and power control feedback functionality.
/// </summary>
public abstract class TwoWayDisplayBase : DisplayBase, IRoutingFeedback, IHasPowerControlWithFeedback
{
/// <summary>
/// Gets feedback for the current input selection on the display.
/// </summary>
public StringFeedback CurrentInputFeedback { get; private set; }
/// <summary>
/// Abstract function that must be implemented by derived classes to provide the current input feedback value.
/// Must be implemented by concrete sub-classes.
/// </summary>
abstract protected Func<string> CurrentInputFeedbackFunc { get; }
/// <summary>
/// Gets feedback indicating whether the display is currently powered on.
/// </summary>
public BoolFeedback PowerIsOnFeedback { get; protected set; }
/// <summary>
/// Abstract function that must be implemented by derived classes to provide the power state feedback value.
/// Must be implemented by concrete sub-classes.
/// </summary>
abstract protected Func<bool> PowerIsOnFeedbackFunc { get; }
/// <summary>
/// Gets the default mock display instance for testing and development purposes.
/// </summary>
public static MockDisplay DefaultDisplay
{
get
{
if (_DefaultDisplay == null)
_DefaultDisplay = new MockDisplay("default", "Default Display");
return _DefaultDisplay;
}
}
static MockDisplay _DefaultDisplay;
/// <summary>
/// Initializes a new instance of the TwoWayDisplayBase class.
/// </summary>
/// <param name="key">The unique key identifier for this display device.</param>
/// <param name="name">The friendly name for this display device.</param>
public TwoWayDisplayBase(string key, string name)
: base(key, name)
{
CurrentInputFeedback = new StringFeedback("currentInput", CurrentInputFeedbackFunc);
WarmupTime = 7000;
CooldownTime = 15000;
PowerIsOnFeedback = new BoolFeedback("PowerOnFeedback", PowerIsOnFeedbackFunc);
Feedbacks.Add(CurrentInputFeedback);
Feedbacks.Add(PowerIsOnFeedback);
PowerIsOnFeedback.OutputChange += PowerIsOnFeedback_OutputChange;
}
void PowerIsOnFeedback_OutputChange(object sender, EventArgs e)
{
if (UsageTracker != null)
{
if (PowerIsOnFeedback.BoolValue)
UsageTracker.StartDeviceUsage();
else
UsageTracker.EndDeviceUsage();
}
}
/// <summary>
/// Event that is raised when a numeric switch change occurs on the display.
/// </summary>
public event EventHandler<RoutingNumericEventArgs> NumericSwitchChange;
/// <summary>
/// Raise an event when the status of a switch object changes.
/// </summary>
/// <param name="e">Arguments defined as IKeyName sender, output, input, and eRoutingSignalType</param>
protected void OnSwitchChange(RoutingNumericEventArgs e)
{
NumericSwitchChange?.Invoke(this, e);
}
}
}

View File

@@ -0,0 +1,17 @@
namespace PepperDash.Essentials.Devices.Common.Shades
{
/// <summary>
/// Enumeration of eScreenLiftControlMode values
/// </summary>
public enum eScreenLiftControlMode
{
/// <summary>
/// Momentary control mode for screen lift
/// </summary>
momentary,
/// <summary>
/// Latched control mode for screen lift
/// </summary>
latched
}
}

View File

@@ -1,9 +1,8 @@
using PepperDash.Core; using System.Collections.Generic;
using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using Serilog.Events; using Serilog.Events;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.Devices.Common.Generic namespace PepperDash.Essentials.Devices.Common.Generic
{ {
@@ -12,11 +11,16 @@ namespace PepperDash.Essentials.Devices.Common.Generic
/// </summary> /// </summary>
public class GenericSink : EssentialsDevice, IRoutingSinkWithInputPort public class GenericSink : EssentialsDevice, IRoutingSinkWithInputPort
{ {
/// <summary>
/// Initializes a new instance of the GenericSink class
/// </summary>
/// <param name="key">The device key</param>
/// <param name="name">The device name</param>
public GenericSink(string key, string name) : base(key, name) public GenericSink(string key, string name) : base(key, name)
{ {
InputPorts = new RoutingPortCollection<RoutingInputPort>(); InputPorts = new RoutingPortCollection<RoutingInputPort>();
var inputPort = new RoutingInputPort(RoutingPortNames.AnyVideoIn, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this); var inputPort = new RoutingInputPort(RoutingPortNames.AnyVideoIn, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this);
InputPorts.Add(inputPort); InputPorts.Add(inputPort);
} }
@@ -35,10 +39,12 @@ namespace PepperDash.Essentials.Devices.Common.Generic
/// <summary> /// <summary>
/// Gets or sets the CurrentSourceInfo /// Gets or sets the CurrentSourceInfo
/// </summary> /// </summary>
public SourceListItem CurrentSourceInfo { public SourceListItem CurrentSourceInfo
{
get => _currentSource; get => _currentSource;
set { set
if(value == _currentSource) {
if (value == _currentSource)
{ {
return; return;
} }
@@ -51,8 +57,14 @@ namespace PepperDash.Essentials.Devices.Common.Generic
} }
} }
/// <summary>
/// Gets the current input port
/// </summary>
public RoutingInputPort CurrentInputPort => InputPorts[0]; public RoutingInputPort CurrentInputPort => InputPorts[0];
/// <summary>
/// Event fired when the current source changes
/// </summary>
public event SourceInfoChangeHandler CurrentSourceChange; public event SourceInfoChangeHandler CurrentSourceChange;
} }
@@ -61,6 +73,9 @@ namespace PepperDash.Essentials.Devices.Common.Generic
/// </summary> /// </summary>
public class GenericSinkFactory : EssentialsDeviceFactory<GenericSink> public class GenericSinkFactory : EssentialsDeviceFactory<GenericSink>
{ {
/// <summary>
/// Initializes a new instance of the GenericSinkFactory class
/// </summary>
public GenericSinkFactory() public GenericSinkFactory()
{ {
TypeNames = new List<string>() { "genericsink", "genericdestination" }; TypeNames = new List<string>() { "genericsink", "genericdestination" };

View File

@@ -1,10 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
@@ -13,38 +7,43 @@ using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common namespace PepperDash.Essentials.Devices.Common
{ {
/// <summary> /// <summary>
/// Represents a GenericSource /// Represents a GenericSource
/// </summary> /// </summary>
public class GenericSource : EssentialsDevice, IUiDisplayInfo, IRoutingSource, IUsageTracking public class GenericSource : EssentialsDevice, IUiDisplayInfo, IRoutingSource, IUsageTracking
{ {
/// <summary> /// <summary>
/// Gets or sets the DisplayUiType /// Gets or sets the DisplayUiType
/// </summary> /// </summary>
public uint DisplayUiType { get { return DisplayUiConstants.TypeNoControls; } } public uint DisplayUiType { get { return DisplayUiConstants.TypeNoControls; } }
/// <summary>
/// Initializes a new instance of the GenericSource class
/// </summary>
/// <param name="key">The device key</param>
/// <param name="name">The device name</param>
public GenericSource(string key, string name) public GenericSource(string key, string name)
: base(key, name) : base(key, name)
{ {
AnyOut = new RoutingOutputPort(RoutingPortNames.AnyOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, AnyOut = new RoutingOutputPort(RoutingPortNames.AnyOut, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, null, this); eRoutingPortConnectionType.Hdmi, null, this);
OutputPorts = new RoutingPortCollection<RoutingOutputPort> { AnyOut }; OutputPorts = new RoutingPortCollection<RoutingOutputPort> { AnyOut };
} }
#region IRoutingOutputs Members #region IRoutingOutputs Members
/// <summary> /// <summary>
/// Gets or sets the AnyOut /// Gets or sets the AnyOut
/// </summary> /// </summary>
public RoutingOutputPort AnyOut { get; private set; } public RoutingOutputPort AnyOut { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the OutputPorts /// Gets or sets the OutputPorts
/// </summary> /// </summary>
public RoutingPortCollection<RoutingOutputPort> OutputPorts { get; private set; } public RoutingPortCollection<RoutingOutputPort> OutputPorts { get; private set; }
#endregion #endregion
#region IUsageTracking Members #region IUsageTracking Members
@@ -54,13 +53,16 @@ namespace PepperDash.Essentials.Devices.Common
public UsageTracking UsageTracker { get; set; } public UsageTracking UsageTracker { get; set; }
#endregion #endregion
} }
/// <summary> /// <summary>
/// Represents a GenericSourceFactory /// Represents a GenericSourceFactory
/// </summary> /// </summary>
public class GenericSourceFactory : EssentialsDeviceFactory<GenericSource> public class GenericSourceFactory : EssentialsDeviceFactory<GenericSource>
{ {
/// <summary>
/// Initializes a new instance of the GenericSourceFactory class
/// </summary>
public GenericSourceFactory() public GenericSourceFactory()
{ {
TypeNames = new List<string>() { "genericsource" }; TypeNames = new List<string>() { "genericsource" };

View File

@@ -3,9 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json; using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
@@ -16,134 +13,165 @@ using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Lighting namespace PepperDash.Essentials.Devices.Common.Lighting
{ {
public abstract class LightingBase : EssentialsBridgeableDevice, ILightingScenes
{
#region ILightingScenes Members
public event EventHandler<LightingSceneChangeEventArgs> LightingSceneChange;
/// <summary>
/// Gets or sets the LightingScenes
/// </summary>
public List<LightingScene> LightingScenes { get; protected set; }
/// <summary>
/// Gets or sets the CurrentLightingScene
/// </summary>
public LightingScene CurrentLightingScene { get; protected set; }
/// <summary> /// <summary>
/// Gets or sets the CurrentLightingSceneFeedback /// Base class for lighting devices that support scenes
/// </summary> /// </summary>
public IntFeedback CurrentLightingSceneFeedback { get; protected set; } public abstract class LightingBase : EssentialsBridgeableDevice, ILightingScenes
{
#region ILightingScenes Members
#endregion /// <summary>
/// Event fired when lighting scene changes
/// </summary>
public event EventHandler<LightingSceneChangeEventArgs> LightingSceneChange;
protected LightingBase(string key, string name) /// <summary>
: base(key, name) /// Gets or sets the LightingScenes
{ /// </summary>
LightingScenes = new List<LightingScene>(); public List<LightingScene> LightingScenes { get; protected set; }
CurrentLightingScene = new LightingScene(); /// <summary>
//CurrentLightingSceneFeedback = new IntFeedback(() => { return int.Parse(this.CurrentLightingScene.ID); }); /// Gets or sets the CurrentLightingScene
} /// </summary>
public LightingScene CurrentLightingScene { get; protected set; }
public abstract void SelectScene(LightingScene scene); /// <summary>
/// Gets or sets the CurrentLightingSceneFeedback
/// </summary>
public IntFeedback CurrentLightingSceneFeedback { get; protected set; }
/// <summary> #endregion
/// SimulateSceneSelect method
/// </summary>
public void SimulateSceneSelect(string sceneName)
{
Debug.LogMessage(LogEventLevel.Debug, this, "Simulating selection of scene '{0}'", sceneName);
var scene = LightingScenes.FirstOrDefault(s => s.Name.Equals(sceneName)); /// <summary>
/// Initializes a new instance of the LightingBase class
/// </summary>
/// <param name="key">The device key</param>
/// <param name="name">The device name</param>
protected LightingBase(string key, string name)
: base(key, name)
{
LightingScenes = new List<LightingScene>();
if (scene != null) CurrentLightingScene = new LightingScene();
{ //CurrentLightingSceneFeedback = new IntFeedback(() => { return int.Parse(this.CurrentLightingScene.ID); });
CurrentLightingScene = scene; }
OnLightingSceneChange();
}
}
/// <summary> /// <summary>
/// Sets the IsActive property on each scene and fires the LightingSceneChange event /// Selects the specified lighting scene
/// </summary> /// </summary>
protected void OnLightingSceneChange() /// <param name="scene">The lighting scene to select</param>
{ public abstract void SelectScene(LightingScene scene);
foreach (var scene in LightingScenes)
{
if (scene == CurrentLightingScene)
scene.IsActive = true;
else
scene.IsActive = false;
}
LightingSceneChange?.Invoke(this, new LightingSceneChangeEventArgs(CurrentLightingScene));
}
protected GenericLightingJoinMap LinkLightingToApi(LightingBase lightingDevice, BasicTriList trilist, uint joinStart, /// <summary>
string joinMapKey, EiscApiAdvanced bridge) /// SimulateSceneSelect method
{ /// </summary>
var joinMap = new GenericLightingJoinMap(joinStart); public void SimulateSceneSelect(string sceneName)
{
Debug.LogMessage(LogEventLevel.Debug, this, "Simulating selection of scene '{0}'", sceneName);
var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); var scene = LightingScenes.FirstOrDefault(s => s.Name.Equals(sceneName));
if (!string.IsNullOrEmpty(joinMapSerialized)) if (scene != null)
joinMap = JsonConvert.DeserializeObject<GenericLightingJoinMap>(joinMapSerialized);
if (bridge != null)
{
bridge.AddJoinMap(Key, joinMap);
}
else
{
Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
}
return LinkLightingToApi(lightingDevice, trilist, joinMap);
}
protected GenericLightingJoinMap LinkLightingToApi(LightingBase lightingDevice, BasicTriList trilist, GenericLightingJoinMap joinMap)
{ {
Debug.LogMessage(LogEventLevel.Debug, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); CurrentLightingScene = scene;
OnLightingSceneChange();
}
}
Debug.LogMessage(LogEventLevel.Information, "Linking to Lighting Type {0}", lightingDevice.GetType().Name.ToString()); /// <summary>
/// Sets the IsActive property on each scene and fires the LightingSceneChange event
/// </summary>
protected void OnLightingSceneChange()
{
foreach (var scene in LightingScenes)
{
if (scene == CurrentLightingScene)
scene.IsActive = true;
// GenericLighitng Actions & FeedBack else
trilist.SetUShortSigAction(joinMap.SelectScene.JoinNumber, u => lightingDevice.SelectScene(lightingDevice.LightingScenes[u])); scene.IsActive = false;
}
LightingSceneChange?.Invoke(this, new LightingSceneChangeEventArgs(CurrentLightingScene));
}
var sceneIndex = 0; /// <summary>
/// Links the lighting device to API with join map configuration
/// </summary>
/// <param name="lightingDevice">The lighting device to link</param>
/// <param name="trilist">The trilist to link to</param>
/// <param name="joinStart">The starting join number</param>
/// <param name="joinMapKey">The join map key</param>
/// <param name="bridge">The EISC API bridge</param>
/// <returns>The configured join map</returns>
protected GenericLightingJoinMap LinkLightingToApi(LightingBase lightingDevice, BasicTriList trilist, uint joinStart,
string joinMapKey, EiscApiAdvanced bridge)
{
var joinMap = new GenericLightingJoinMap(joinStart);
var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);
if (!string.IsNullOrEmpty(joinMapSerialized))
joinMap = JsonConvert.DeserializeObject<GenericLightingJoinMap>(joinMapSerialized);
if (bridge != null)
{
bridge.AddJoinMap(Key, joinMap);
}
else
{
Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
}
return LinkLightingToApi(lightingDevice, trilist, joinMap);
}
/// <summary>
/// Links the lighting device to API using an existing join map
/// </summary>
/// <param name="lightingDevice">The lighting device to link</param>
/// <param name="trilist">The trilist to link to</param>
/// <param name="joinMap">The join map to use</param>
/// <returns>The join map used for linking</returns>
protected GenericLightingJoinMap LinkLightingToApi(LightingBase lightingDevice, BasicTriList trilist, GenericLightingJoinMap joinMap)
{
Debug.LogMessage(LogEventLevel.Debug, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
Debug.LogMessage(LogEventLevel.Information, "Linking to Lighting Type {0}", lightingDevice.GetType().Name.ToString());
// GenericLighitng Actions & FeedBack
trilist.SetUShortSigAction(joinMap.SelectScene.JoinNumber, u => lightingDevice.SelectScene(lightingDevice.LightingScenes[u]));
var sceneIndex = 0;
foreach (var scene in lightingDevice.LightingScenes)
{
var index = sceneIndex;
trilist.SetSigTrueAction((uint)(joinMap.SelectSceneDirect.JoinNumber + index), () => lightingDevice.SelectScene(lightingDevice.LightingScenes[index]));
scene.IsActiveFeedback.LinkInputSig(trilist.BooleanInput[(uint)(joinMap.SelectSceneDirect.JoinNumber + index)]);
trilist.StringInput[(uint)(joinMap.SelectSceneDirect.JoinNumber + index)].StringValue = scene.Name;
trilist.BooleanInput[(uint)(joinMap.ButtonVisibility.JoinNumber + index)].BoolValue = true;
sceneIndex++;
}
trilist.OnlineStatusChange += (sender, args) =>
{
if (!args.DeviceOnLine) return;
sceneIndex = 0;
foreach (var scene in lightingDevice.LightingScenes) foreach (var scene in lightingDevice.LightingScenes)
{ {
var index = sceneIndex; var index = sceneIndex;
trilist.SetSigTrueAction((uint)(joinMap.SelectSceneDirect.JoinNumber + index), () => lightingDevice.SelectScene(lightingDevice.LightingScenes[index]));
scene.IsActiveFeedback.LinkInputSig(trilist.BooleanInput[(uint)(joinMap.SelectSceneDirect.JoinNumber + index)]);
trilist.StringInput[(uint)(joinMap.SelectSceneDirect.JoinNumber + index)].StringValue = scene.Name; trilist.StringInput[(uint)(joinMap.SelectSceneDirect.JoinNumber + index)].StringValue = scene.Name;
trilist.BooleanInput[(uint)(joinMap.ButtonVisibility.JoinNumber + index)].BoolValue = true; trilist.BooleanInput[(uint)(joinMap.ButtonVisibility.JoinNumber + index)].BoolValue = true;
scene.IsActiveFeedback.FireUpdate();
sceneIndex++; sceneIndex++;
} }
};
trilist.OnlineStatusChange += (sender, args) => return joinMap;
{
if (!args.DeviceOnLine) return;
sceneIndex = 0;
foreach (var scene in lightingDevice.LightingScenes)
{
var index = sceneIndex;
trilist.StringInput[(uint) (joinMap.SelectSceneDirect.JoinNumber + index)].StringValue = scene.Name;
trilist.BooleanInput[(uint) (joinMap.ButtonVisibility.JoinNumber + index)].BoolValue = true;
scene.IsActiveFeedback.FireUpdate();
sceneIndex++;
}
};
return joinMap;
}
} }
}
} }

View File

@@ -9,10 +9,20 @@ namespace PepperDash.Essentials.Devices.Common.Room
public interface IEssentialsHuddleSpaceRoom : IEssentialsRoom, IHasCurrentSourceInfoChange, IRunRouteAction, IHasDefaultDisplay, IHasCurrentVolumeControls, IRoomOccupancy, public interface IEssentialsHuddleSpaceRoom : IEssentialsRoom, IHasCurrentSourceInfoChange, IRunRouteAction, IHasDefaultDisplay, IHasCurrentVolumeControls, IRoomOccupancy,
IEmergency, IMicrophonePrivacy IEmergency, IMicrophonePrivacy
{ {
/// <summary>
/// Gets whether to exclude this room from global functions
/// </summary>
bool ExcludeFromGlobalFunctions { get; } bool ExcludeFromGlobalFunctions { get; }
/// <summary>
/// Runs the route action for the given routeKey and sourceListKey
/// </summary>
/// <param name="routeKey">The route key</param>
void RunRouteAction(string routeKey); void RunRouteAction(string routeKey);
/// <summary>
/// Gets the PropertiesConfig
/// </summary>
EssentialsHuddleRoomPropertiesConfig PropertiesConfig { get; } EssentialsHuddleRoomPropertiesConfig PropertiesConfig { get; }
} }
} }

View File

@@ -12,18 +12,40 @@ namespace PepperDash.Essentials.Devices.Common.Room
public interface IEssentialsHuddleVtc1Room : IEssentialsRoom, IHasCurrentSourceInfoChange, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay, IHasInCallFeedback, public interface IEssentialsHuddleVtc1Room : IEssentialsRoom, IHasCurrentSourceInfoChange, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay, IHasInCallFeedback,
IRoomOccupancy, IEmergency, IMicrophonePrivacy IRoomOccupancy, IEmergency, IMicrophonePrivacy
{ {
/// <summary>
/// Gets the PropertiesConfig
/// </summary>
EssentialsHuddleVtc1PropertiesConfig PropertiesConfig { get; } EssentialsHuddleVtc1PropertiesConfig PropertiesConfig { get; }
/// <summary>
/// Gets whether to exclude this room from global functions
/// </summary>
bool ExcludeFromGlobalFunctions { get; } bool ExcludeFromGlobalFunctions { get; }
/// <summary>
/// Runs the route action for the given routeKey and sourceListKey
/// </summary>
/// <param name="routeKey">The route key</param>
void RunRouteAction(string routeKey); void RunRouteAction(string routeKey);
/// <summary>
/// Gets the ScheduleSource
/// </summary>
IHasScheduleAwareness ScheduleSource { get; } IHasScheduleAwareness ScheduleSource { get; }
/// <summary>
/// Gets the InCallFeedback
/// </summary>
new BoolFeedback InCallFeedback { get; } new BoolFeedback InCallFeedback { get; }
/// <summary>
/// Gets the PrivacyModeIsOnFeedback
/// </summary>
new BoolFeedback PrivacyModeIsOnFeedback { get; } new BoolFeedback PrivacyModeIsOnFeedback { get; }
/// <summary>
/// Gets the DefaultCodecRouteString
/// </summary>
string DefaultCodecRouteString { get; } string DefaultCodecRouteString { get; }
} }
} }

Some files were not shown because too many files have changed in this diff Show More