mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-02-13 11:44:54 +00:00
Merge branch 'main' into update-crestron-assembly
This commit is contained in:
@@ -7,16 +7,18 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
public class AuthorizationResponse
|
||||
{
|
||||
[JsonProperty("authorized")]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Authorized
|
||||
/// </summary>
|
||||
[JsonProperty("authorized")]
|
||||
public bool Authorized { get; set; }
|
||||
|
||||
[JsonProperty("reason", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Reason
|
||||
/// </summary>
|
||||
[JsonProperty("reason", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Reason { get; set; } = null;
|
||||
}
|
||||
|
||||
@@ -25,10 +27,11 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
public class AuthorizationRequest
|
||||
{
|
||||
[JsonProperty("grantCode")]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the GrantCode
|
||||
/// </summary>
|
||||
[JsonProperty("grantCode")]
|
||||
public string GrantCode { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
/// <summary>
|
||||
/// Send an update request for a specific client
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public class ClientSpecificUpdateRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Initialize an instance of the <see cref="ClientSpecificUpdateRequest"/> class.
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
public ClientSpecificUpdateRequest(Action<string> action)
|
||||
{
|
||||
ResponseMethod = action;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ResponseMethod
|
||||
/// </summary>
|
||||
public Action<string> ResponseMethod { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,9 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
public interface IDelayedConfiguration
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the configuration is ready. Used when Mobile Control is interacting with a SIMPL program.
|
||||
/// </summary>
|
||||
event EventHandler<EventArgs> ConfigurationIsReady;
|
||||
}
|
||||
}
|
||||
90
src/PepperDash.Essentials.MobileControl/MessageToClients.cs
Normal file
90
src/PepperDash.Essentials.MobileControl/MessageToClients.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Logging;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
using PepperDash.Essentials.Core.Queues;
|
||||
using PepperDash.Essentials.WebSocketServer;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a MessageToClients
|
||||
/// </summary>
|
||||
public class MessageToClients : IQueueMessage
|
||||
{
|
||||
private readonly MobileControlWebsocketServer _server;
|
||||
private readonly object msgToSend;
|
||||
|
||||
/// <summary>
|
||||
/// Message to send to Direct Server Clients
|
||||
/// </summary>
|
||||
/// <param name="msg">message object to send</param>
|
||||
/// <param name="server">WebSocket server instance</param>
|
||||
public MessageToClients(object msg, MobileControlWebsocketServer server)
|
||||
{
|
||||
_server = server;
|
||||
msgToSend = msg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Message to send to Direct Server Clients
|
||||
/// </summary>
|
||||
/// <param name="msg">message object to send</param>
|
||||
/// <param name="server">WebSocket server instance</param>
|
||||
public MessageToClients(DeviceStateMessageBase msg, MobileControlWebsocketServer server)
|
||||
{
|
||||
_server = server;
|
||||
msgToSend = msg;
|
||||
}
|
||||
|
||||
#region Implementation of IQueueMessage
|
||||
|
||||
/// <summary>
|
||||
/// Dispatch method
|
||||
/// </summary>
|
||||
public void Dispatch()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_server == null)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Warning, "Cannot send message. Server is null");
|
||||
return;
|
||||
}
|
||||
|
||||
var message = JsonConvert.SerializeObject(msgToSend, Formatting.None,
|
||||
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Converters = { new IsoDateTimeConverter() } });
|
||||
|
||||
var clientSpecificMessage = msgToSend as MobileControlMessage;
|
||||
if (clientSpecificMessage.ClientId != null)
|
||||
{
|
||||
var clientId = clientSpecificMessage.ClientId;
|
||||
|
||||
_server.LogVerbose("Message TX To client {clientId}: {message}", clientId, message);
|
||||
|
||||
_server.SendMessageToClient(clientId, message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_server.SendMessageToAllClients(message);
|
||||
|
||||
_server.LogVerbose("Message TX To all clients: {message}", message);
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
//Swallowing this exception, as it occurs on shutdown and there's no need to print out a scary stack trace
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Caught an exception in the Transmit Processor");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
@@ -10,12 +10,20 @@ namespace PepperDash.Essentials
|
||||
public class MobileControlAction : IMobileControlAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the Messenger
|
||||
/// Gets the Messenger
|
||||
/// </summary>
|
||||
public IMobileControlMessenger Messenger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Action to execute when this path is matched
|
||||
/// </summary>
|
||||
public Action<string, string, JToken> Action { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize an instance of the <see cref="MobileControlAction"/> class
|
||||
/// </summary>
|
||||
/// <param name="messenger">Messenger associated with this action</param>
|
||||
/// <param name="handler">Action to take when this path is matched</param>
|
||||
public MobileControlAction(IMobileControlMessenger messenger, Action<string, string, JToken> handler)
|
||||
{
|
||||
Messenger = messenger;
|
||||
|
||||
@@ -38,6 +38,12 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
[JsonProperty("enableApiServer")]
|
||||
public bool EnableApiServer { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Enable subscriptions for Messengers
|
||||
/// </summary>
|
||||
[JsonProperty("enableMessengerSubscriptions")]
|
||||
public bool EnableMessengerSubscriptions { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -78,6 +84,15 @@ namespace PepperDash.Essentials
|
||||
[JsonProperty("csLanUiDeviceKeys")]
|
||||
public List<string> CSLanUiDeviceKeys { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the Secure property
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Indicates whether the connection is secure (HTTPS).
|
||||
/// </remarks>
|
||||
[JsonProperty("Secure")]
|
||||
public bool Secure { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the MobileControlDirectServerPropertiesConfig class.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using Serilog.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a MobileControlDeviceFactory
|
||||
/// Factory to create a Mobile Control System Controller
|
||||
/// </summary>
|
||||
public class MobileControlDeviceFactory : EssentialsDeviceFactory<MobileControlSystemController>
|
||||
{
|
||||
/// <summary>
|
||||
/// Create the factory for a Mobile Control System Controller
|
||||
/// </summary>
|
||||
public MobileControlDeviceFactory()
|
||||
{
|
||||
TypeNames = new List<string> { "appserver", "mobilecontrol", "webserver" };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// BuildDevice method
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public override EssentialsDevice BuildDevice(DeviceConfig dc)
|
||||
{
|
||||
|
||||
@@ -1,34 +1,40 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a MobileControlEssentialsConfig
|
||||
/// Configuration class for sending data to Mobile Control Edge or a client using the Direct Server
|
||||
/// </summary>
|
||||
public class MobileControlEssentialsConfig : EssentialsConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Current versions for the system
|
||||
/// </summary>
|
||||
[JsonProperty("runtimeInfo")]
|
||||
public MobileControlRuntimeInfo RuntimeInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create Configuration for Mobile Control. Used as part of the data sent to a client
|
||||
/// </summary>
|
||||
/// <param name="config">The base configuration</param>
|
||||
public MobileControlEssentialsConfig(EssentialsConfig config)
|
||||
: base()
|
||||
{
|
||||
// TODO: Consider using Reflection to iterate properties
|
||||
this.Devices = config.Devices;
|
||||
this.Info = config.Info;
|
||||
this.JoinMaps = config.JoinMaps;
|
||||
this.Rooms = config.Rooms;
|
||||
this.SourceLists = config.SourceLists;
|
||||
this.DestinationLists = config.DestinationLists;
|
||||
this.SystemUrl = config.SystemUrl;
|
||||
this.TemplateUrl = config.TemplateUrl;
|
||||
this.TieLines = config.TieLines;
|
||||
Devices = config.Devices;
|
||||
Info = config.Info;
|
||||
JoinMaps = config.JoinMaps;
|
||||
Rooms = config.Rooms;
|
||||
SourceLists = config.SourceLists;
|
||||
DestinationLists = config.DestinationLists;
|
||||
SystemUrl = config.SystemUrl;
|
||||
TemplateUrl = config.TemplateUrl;
|
||||
TieLines = config.TieLines;
|
||||
|
||||
if (this.Info == null)
|
||||
this.Info = new InfoConfig();
|
||||
if (Info == null)
|
||||
Info = new InfoConfig();
|
||||
|
||||
RuntimeInfo = new MobileControlRuntimeInfo();
|
||||
}
|
||||
@@ -39,22 +45,30 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
public class MobileControlRuntimeInfo
|
||||
{
|
||||
[JsonProperty("pluginVersion")]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the PluginVersion
|
||||
/// </summary>
|
||||
[JsonProperty("pluginVersion")]
|
||||
public string PluginVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Essentials Version
|
||||
/// </summary>
|
||||
[JsonProperty("essentialsVersion")]
|
||||
public string EssentialsVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// PepperDash Core Version
|
||||
/// </summary>
|
||||
[JsonProperty("pepperDashCoreVersion")]
|
||||
public string PepperDashCoreVersion { get; set; }
|
||||
|
||||
[JsonProperty("essentialsPlugins")]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the EssentialsPlugins
|
||||
/// List of Plugins loaded on this system
|
||||
/// </summary>
|
||||
[JsonProperty("essentialsPlugins")]
|
||||
public List<LoadedAssembly> EssentialsPlugins { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,13 @@ using PepperDash.Essentials.Core;
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a MobileControlFactory
|
||||
/// Factory class for the Mobile Control App Controller
|
||||
/// </summary>
|
||||
public class MobileControlFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="MobileControlFactory"/> class.
|
||||
/// </summary>
|
||||
public MobileControlFactory()
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
@@ -54,7 +54,10 @@ namespace PepperDash.Essentials
|
||||
StringComparer.InvariantCultureIgnoreCase
|
||||
);
|
||||
|
||||
public Dictionary<string, List<IMobileControlAction>> ActionDictionary => _actionDictionary;
|
||||
/// <summary>
|
||||
/// Actions
|
||||
/// </summary>
|
||||
public ReadOnlyDictionary<string, List<IMobileControlAction>> ActionDictionary => new ReadOnlyDictionary<string, List<IMobileControlAction>>(_actionDictionary);
|
||||
|
||||
private readonly GenericQueue _receiveQueue;
|
||||
private readonly List<MobileControlBridgeBase> _roomBridges =
|
||||
@@ -66,6 +69,16 @@ namespace PepperDash.Essentials
|
||||
private readonly Dictionary<string, IMobileControlMessenger> _defaultMessengers =
|
||||
new Dictionary<string, IMobileControlMessenger>();
|
||||
|
||||
/// <summary>
|
||||
/// Get the custom messengers with subscriptions
|
||||
/// </summary>
|
||||
public ReadOnlyDictionary<string, IMobileControlMessengerWithSubscriptions> Messengers => new ReadOnlyDictionary<string, IMobileControlMessengerWithSubscriptions>(_messengers.Values.OfType<IMobileControlMessengerWithSubscriptions>().ToDictionary(k => k.Key, v => v));
|
||||
|
||||
/// <summary>
|
||||
/// Get the default messengers
|
||||
/// </summary>
|
||||
public ReadOnlyDictionary<string, IMobileControlMessengerWithSubscriptions> DefaultMessengers => new ReadOnlyDictionary<string, IMobileControlMessengerWithSubscriptions>(_defaultMessengers.Values.OfType<IMobileControlMessengerWithSubscriptions>().ToDictionary(k => k.Key, v => v));
|
||||
|
||||
private readonly GenericQueue _transmitToServerQueue;
|
||||
|
||||
private readonly GenericQueue _transmitToClientsQueue;
|
||||
@@ -78,10 +91,16 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
public MobileControlApiService ApiService { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get Room Bridges associated with this controller
|
||||
/// </summary>
|
||||
public List<MobileControlBridgeBase> RoomBridges => _roomBridges;
|
||||
|
||||
private readonly MobileControlWebsocketServer _directServer;
|
||||
|
||||
/// <summary>
|
||||
/// Get the Direct Server instance associated with this controller
|
||||
/// </summary>
|
||||
public MobileControlWebsocketServer DirectServer => _directServer;
|
||||
|
||||
private readonly CCriticalSection _wsCriticalSection = new CCriticalSection();
|
||||
@@ -91,10 +110,16 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
public string SystemUrl; //set only from SIMPL Bridge!
|
||||
|
||||
/// <summary>
|
||||
/// True if the Mobile Control Edge Server Websocket is connected
|
||||
/// </summary>
|
||||
public bool Connected => _wsClient2 != null && _wsClient2.IsAlive;
|
||||
|
||||
private IEssentialsRoomCombiner _roomCombiner;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SystemUuid from configuration or SIMPL Bridge
|
||||
/// </summary>
|
||||
public string SystemUuid
|
||||
{
|
||||
get
|
||||
@@ -156,6 +181,9 @@ namespace PepperDash.Essentials
|
||||
|
||||
private DateTime _lastAckMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LastAckMessage timestamp
|
||||
/// </summary>
|
||||
public DateTime LastAckMessage => _lastAckMessage;
|
||||
|
||||
private CTimer _pingTimer;
|
||||
@@ -164,11 +192,11 @@ namespace PepperDash.Essentials
|
||||
private LogLevel _wsLogLevel = LogLevel.Error;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Initializes a new instance of the <see cref="MobileControlSystemController"/> class.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="config"></param>
|
||||
/// <param name="key">The unique key for this controller.</param>
|
||||
/// <param name="name">The name of the controller.</param>
|
||||
/// <param name="config">The configuration settings for the controller.</param>
|
||||
public MobileControlSystemController(string key, string name, MobileControlConfig config)
|
||||
: base(key, name)
|
||||
{
|
||||
@@ -244,7 +272,7 @@ namespace PepperDash.Essentials
|
||||
CrestronEnvironment.ProgramStatusEventHandler +=
|
||||
CrestronEnvironment_ProgramStatusEventHandler;
|
||||
|
||||
ApiOnlineAndAuthorized = new BoolFeedback(() =>
|
||||
ApiOnlineAndAuthorized = new BoolFeedback("apiOnlineAndAuthorized", () =>
|
||||
{
|
||||
if (_wsClient2 == null)
|
||||
return false;
|
||||
@@ -392,14 +420,15 @@ namespace PepperDash.Essentials
|
||||
messengerAdded = true;
|
||||
}
|
||||
|
||||
if (device is CameraBase cameraDevice)
|
||||
// Default to IHasCameraControls if CameraBase and IHasCameraControls
|
||||
if (device is CameraBase cameraDevice && !(device is IHasCameraControls))
|
||||
{
|
||||
this.LogVerbose(
|
||||
"Adding CameraBaseMessenger for {deviceKey}",
|
||||
device.Key
|
||||
);
|
||||
|
||||
var cameraMessenger = new CameraBaseMessenger(
|
||||
var cameraMessenger = new CameraBaseMessenger<CameraBase>(
|
||||
$"{device.Key}-cameraBase-{Key}",
|
||||
cameraDevice,
|
||||
$"/device/{device.Key}"
|
||||
@@ -410,6 +439,21 @@ namespace PepperDash.Essentials
|
||||
messengerAdded = true;
|
||||
}
|
||||
|
||||
if (device is IHasCameraControls cameraControlDev)
|
||||
{
|
||||
this.LogVerbose(
|
||||
"Adding IHasCamerasWithControlMessenger for {deviceKey}",
|
||||
device.Key
|
||||
);
|
||||
var cameraControlMessenger = new CameraBaseMessenger<IHasCameraControls>(
|
||||
$"{device.Key}-hasCamerasWithControls-{Key}",
|
||||
cameraControlDev,
|
||||
$"/device/{device.Key}"
|
||||
);
|
||||
AddDefaultDeviceMessenger(cameraControlMessenger);
|
||||
messengerAdded = true;
|
||||
}
|
||||
|
||||
if (device is BlueJeansPc)
|
||||
{
|
||||
this.LogVerbose(
|
||||
@@ -486,15 +530,15 @@ namespace PepperDash.Essentials
|
||||
messengerAdded = true;
|
||||
}
|
||||
|
||||
if (device is IBasicVolumeWithFeedback)
|
||||
if (device is IBasicVolumeControls)
|
||||
{
|
||||
var deviceKey = device.Key;
|
||||
this.LogVerbose(
|
||||
"Adding IBasicVolumeControlWithFeedback for {deviceKey}",
|
||||
"Adding IBasicVolumeControls for {deviceKey}",
|
||||
deviceKey
|
||||
);
|
||||
|
||||
var volControlDevice = device as IBasicVolumeWithFeedback;
|
||||
var volControlDevice = device as IBasicVolumeControls;
|
||||
var messenger = new DeviceVolumeMessenger(
|
||||
$"{device.Key}-volume-{Key}",
|
||||
string.Format("/device/{0}", deviceKey),
|
||||
@@ -962,6 +1006,19 @@ namespace PepperDash.Essentials
|
||||
messengerAdded = true;
|
||||
}
|
||||
|
||||
if (device is IHasCamerasWithControls cameras2)
|
||||
{
|
||||
this.LogVerbose("Adding IHasCamerasWithControlsMessenger for {deviceKey}", device.Key
|
||||
);
|
||||
var messenger = new IHasCamerasWithControlMessenger(
|
||||
$"{device.Key}-cameras-{Key}",
|
||||
$"/device/{device.Key}",
|
||||
cameras2
|
||||
);
|
||||
AddDefaultDeviceMessenger(messenger);
|
||||
messengerAdded = true;
|
||||
}
|
||||
|
||||
this.LogVerbose("Trying to cast to generic device for device: {key}", device.Key);
|
||||
|
||||
if (device is EssentialsDevice)
|
||||
@@ -1150,6 +1207,9 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
public string Host { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configured Client App URL
|
||||
/// </summary>
|
||||
public string ClientAppUrl => Config.ClientAppUrl;
|
||||
|
||||
private void OnRoomCombinationScenarioChanged(
|
||||
@@ -1161,7 +1221,7 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CheckForDeviceMessenger method
|
||||
/// Checks if a device messenger exists for the given key.
|
||||
/// </summary>
|
||||
public bool CheckForDeviceMessenger(string key)
|
||||
{
|
||||
@@ -1169,13 +1229,13 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AddDeviceMessenger method
|
||||
/// Add the provided messenger to the messengers collection
|
||||
/// </summary>
|
||||
public void AddDeviceMessenger(IMobileControlMessenger messenger)
|
||||
{
|
||||
if (_messengers.ContainsKey(messenger.Key))
|
||||
{
|
||||
this.LogWarning("Messenger with key {messengerKey) already added", messenger.Key);
|
||||
this.LogWarning("Messenger with key {messengerKey} already added", messenger.Key);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1199,8 +1259,7 @@ namespace PepperDash.Essentials
|
||||
|
||||
if (_initialized)
|
||||
{
|
||||
this.LogDebug("Registering messenger {messengerKey} AFTER initialization", messenger.Key);
|
||||
messenger.RegisterWithAppServer(this);
|
||||
RegisterMessengerWithServer(messenger);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1241,15 +1300,23 @@ namespace PepperDash.Essentials
|
||||
messenger.MessagePath
|
||||
);
|
||||
|
||||
if (messenger is IMobileControlMessengerWithSubscriptions subMessenger)
|
||||
{
|
||||
subMessenger.RegisterWithAppServer(this, Config.EnableMessengerSubscriptions);
|
||||
return;
|
||||
}
|
||||
|
||||
messenger.RegisterWithAppServer(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize method
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public override void Initialize()
|
||||
{
|
||||
if (!Config.EnableMessengerSubscriptions)
|
||||
{
|
||||
this.LogWarning("Messenger subscriptions disabled. add \"enableMessengerSubscriptions\": true to config for {key} to enable.", Key);
|
||||
}
|
||||
|
||||
foreach (var messenger in _messengers)
|
||||
{
|
||||
try
|
||||
@@ -1291,7 +1358,7 @@ namespace PepperDash.Essentials
|
||||
#region IMobileControl Members
|
||||
|
||||
/// <summary>
|
||||
/// GetAppServer method
|
||||
/// Gets the App Server instance
|
||||
/// </summary>
|
||||
public static IMobileControl GetAppServer()
|
||||
{
|
||||
@@ -1309,16 +1376,10 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the url and creates the websocket client
|
||||
/// </summary>
|
||||
private bool CreateWebsocket()
|
||||
{
|
||||
if (_wsClient2 != null)
|
||||
{
|
||||
_wsClient2.Close();
|
||||
_wsClient2 = null;
|
||||
}
|
||||
_wsClient2?.Close();
|
||||
_wsClient2 = null;
|
||||
|
||||
if (string.IsNullOrEmpty(SystemUuid))
|
||||
{
|
||||
@@ -1335,14 +1396,13 @@ namespace PepperDash.Essentials
|
||||
{
|
||||
Log =
|
||||
{
|
||||
Output = (data, s) =>
|
||||
this.LogDebug(
|
||||
"Message from websocket: {message}",
|
||||
data
|
||||
)
|
||||
Output = (data, message) => Utilities.ConvertWebsocketLog(data, message, this)
|
||||
}
|
||||
};
|
||||
|
||||
// setting to trace to let level be controlled by appdebug
|
||||
_wsClient2.Log.Level = LogLevel.Trace;
|
||||
|
||||
_wsClient2.SslConfiguration.EnabledSslProtocols =
|
||||
System.Security.Authentication.SslProtocols.Tls11
|
||||
| System.Security.Authentication.SslProtocols.Tls12;
|
||||
@@ -1356,7 +1416,7 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LinkSystemMonitorToAppServer method
|
||||
/// Link the System Monitor to this App server
|
||||
/// </summary>
|
||||
public void LinkSystemMonitorToAppServer()
|
||||
{
|
||||
@@ -1383,14 +1443,6 @@ namespace PepperDash.Essentials
|
||||
|
||||
private void SetWebsocketDebugLevel(string cmdparameters)
|
||||
{
|
||||
if (CrestronEnvironment.ProgramCompatibility == eCrestronSeries.Series4)
|
||||
{
|
||||
this.LogInformation(
|
||||
"Setting websocket log level not currently allowed on 4 series."
|
||||
);
|
||||
return; // Web socket log level not currently allowed in series4
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(cmdparameters))
|
||||
{
|
||||
this.LogInformation("Current Websocket debug level: {webSocketDebugLevel}", _wsLogLevel);
|
||||
@@ -1415,6 +1467,8 @@ namespace PepperDash.Essentials
|
||||
_wsClient2.Log.Level = _wsLogLevel;
|
||||
}
|
||||
|
||||
_directServer?.SetWebsocketLogLevel(_wsLogLevel);
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse($"Websocket log level set to {debugLevel}");
|
||||
}
|
||||
catch
|
||||
@@ -1426,10 +1480,6 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends message to server to indicate the system is shutting down
|
||||
/// </summary>
|
||||
/// <param name="programEventType"></param>
|
||||
private void CrestronEnvironment_ProgramStatusEventHandler(
|
||||
eProgramStatusEventType programEventType
|
||||
)
|
||||
@@ -1462,6 +1512,9 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get action paths for the current actions
|
||||
/// </summary>
|
||||
public List<(string, string)> GetActionDictionaryPaths()
|
||||
{
|
||||
var paths = new List<(string, string)>();
|
||||
@@ -1484,7 +1537,7 @@ namespace PepperDash.Essentials
|
||||
/// <summary>
|
||||
/// Adds an action to the dictionary
|
||||
/// </summary>
|
||||
/// <param name="key">The path of the API command</param>
|
||||
/// <param name="messenger">The messenger for the API command</param>
|
||||
/// <param name="action">The action to be triggered by the commmand</param>
|
||||
public void AddAction<T>(T messenger, Action<string, string, JToken> action)
|
||||
where T : IMobileControlMessenger
|
||||
@@ -1534,24 +1587,24 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the room bridge with the provided key
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the room bridge</param>
|
||||
public MobileControlBridgeBase GetRoomBridge(string key)
|
||||
{
|
||||
return _roomBridges.FirstOrDefault((r) => r.RoomKey.Equals(key));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GetRoomMessenger method
|
||||
/// Get the room messenger with the provided key
|
||||
/// </summary>
|
||||
/// <param name="key">The Key of the rooom messenger</param>
|
||||
public IMobileControlRoomMessenger GetRoomMessenger(string key)
|
||||
{
|
||||
return _roomBridges.FirstOrDefault((r) => r.RoomKey.Equals(key));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Bridge_ConfigurationIsReady(object sender, EventArgs e)
|
||||
{
|
||||
this.LogDebug("Bridge ready. Registering");
|
||||
@@ -1572,10 +1625,6 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="o"></param>
|
||||
private void ReconnectToServerTimerCallback(object o)
|
||||
{
|
||||
this.LogDebug("Attempting to reconnect to server...");
|
||||
@@ -1583,9 +1632,6 @@ namespace PepperDash.Essentials
|
||||
ConnectWebsocketClient();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies system connection with servers
|
||||
/// </summary>
|
||||
private void AuthorizeSystem(string code)
|
||||
{
|
||||
if (
|
||||
@@ -1630,9 +1676,6 @@ namespace PepperDash.Essentials
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps info in response to console command.
|
||||
/// </summary>
|
||||
private void ShowInfo()
|
||||
{
|
||||
var url = Config != null ? Host : "No config";
|
||||
@@ -1698,38 +1741,37 @@ namespace PepperDash.Essentials
|
||||
"\r\n UI Client Info:\r\n" +
|
||||
" Tokens Defined: {0}\r\n" +
|
||||
" Clients Connected: {1}\r\n",
|
||||
_directServer.UiClients.Count,
|
||||
_directServer.UiClientContexts.Count,
|
||||
_directServer.ConnectedUiClientsCount
|
||||
);
|
||||
|
||||
var clientNo = 1;
|
||||
foreach (var clientContext in _directServer.UiClients)
|
||||
foreach (var clientContext in _directServer.UiClientContexts)
|
||||
{
|
||||
var isAlive = false;
|
||||
var duration = "Not Connected";
|
||||
|
||||
if (clientContext.Value.Client != null)
|
||||
{
|
||||
isAlive = clientContext.Value.Client.Context.WebSocket.IsAlive;
|
||||
duration = clientContext.Value.Client.ConnectedDuration.ToString();
|
||||
}
|
||||
var clients = _directServer.UiClients.Values.Where(c => c.Token == clientContext.Value.Token.Token);
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse(
|
||||
"\r\nClient {0}:\r\n" +
|
||||
"Room Key: {1}\r\n" +
|
||||
"Touchpanel Key: {6}\r\n" +
|
||||
"Token: {2}\r\n" +
|
||||
"Client URL: {3}\r\n" +
|
||||
"Connected: {4}\r\n" +
|
||||
"Duration: {5}\r\n",
|
||||
clientNo,
|
||||
clientContext.Value.Token.RoomKey,
|
||||
clientContext.Key,
|
||||
string.Format("{0}{1}", _directServer.UserAppUrlPrefix, clientContext.Key),
|
||||
isAlive,
|
||||
duration,
|
||||
clientContext.Value.Token.TouchpanelKey
|
||||
$"\r\nClient {clientNo}:\r\n" +
|
||||
$" Room Key: {clientContext.Value.Token.RoomKey}\r\n" +
|
||||
$" Touchpanel Key: {clientContext.Value.Token.TouchpanelKey}\r\n" +
|
||||
$" Token: {clientContext.Key}\r\n" +
|
||||
$" Client URL: {_directServer.UserAppUrlPrefix}{clientContext.Key}\r\n" +
|
||||
$" Clients:\r\n"
|
||||
);
|
||||
|
||||
if (!clients.Any())
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse(" No clients connected");
|
||||
}
|
||||
foreach (var client in clients)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse(
|
||||
$" ID: {client.Id}\r\n" +
|
||||
$" Connected: {client.Context.WebSocket.IsAlive}\r\n" +
|
||||
$" Duration: {(client.Context.WebSocket.IsAlive ? client.ConnectedDuration.TotalSeconds.ToString() : "Not Connected")}\r\n"
|
||||
);
|
||||
}
|
||||
|
||||
clientNo++;
|
||||
}
|
||||
}
|
||||
@@ -1743,7 +1785,7 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RegisterSystemToServer method
|
||||
/// Register this system to the Mobile Control Edge Server
|
||||
/// </summary>
|
||||
public void RegisterSystemToServer()
|
||||
{
|
||||
@@ -1767,9 +1809,6 @@ namespace PepperDash.Essentials
|
||||
ConnectWebsocketClient();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connects the Websocket Client
|
||||
/// </summary>
|
||||
private void ConnectWebsocketClient()
|
||||
{
|
||||
try
|
||||
@@ -1810,9 +1849,6 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to connect the websocket
|
||||
/// </summary>
|
||||
private void TryConnect()
|
||||
{
|
||||
try
|
||||
@@ -1842,9 +1878,6 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gracefully handles conect failures by reconstructing the ws client and starting the reconnect timer
|
||||
/// </summary>
|
||||
private void HandleConnectFailure()
|
||||
{
|
||||
_wsClient2 = null;
|
||||
@@ -1876,11 +1909,6 @@ namespace PepperDash.Essentials
|
||||
StartServerReconnectTimer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void HandleOpen(object sender, EventArgs e)
|
||||
{
|
||||
StopServerReconnectTimer();
|
||||
@@ -1889,11 +1917,6 @@ namespace PepperDash.Essentials
|
||||
SendMessageObject(new MobileControlMessage { Type = "hello" });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void HandleMessage(object sender, MessageEventArgs e)
|
||||
{
|
||||
if (e.IsPing)
|
||||
@@ -1910,11 +1933,6 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void HandleError(object sender, ErrorEventArgs e)
|
||||
{
|
||||
this.LogError("Websocket error {0}", e.Message);
|
||||
@@ -1923,11 +1941,6 @@ namespace PepperDash.Essentials
|
||||
StartServerReconnectTimer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void HandleClose(object sender, CloseEventArgs e)
|
||||
{
|
||||
this.LogDebug(
|
||||
@@ -1948,9 +1961,6 @@ namespace PepperDash.Essentials
|
||||
StartServerReconnectTimer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// After a "hello" from the server, sends config and stuff
|
||||
/// </summary>
|
||||
private void SendInitialMessage()
|
||||
{
|
||||
this.LogInformation("Sending initial join message");
|
||||
@@ -1977,7 +1987,7 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GetConfigWithPluginVersion method
|
||||
/// Get the Essentials configuration with version data
|
||||
/// </summary>
|
||||
public MobileControlEssentialsConfig GetConfigWithPluginVersion()
|
||||
{
|
||||
@@ -2012,8 +2022,13 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SetClientUrl method
|
||||
/// Set the Client URL for a given room
|
||||
/// </summary>
|
||||
/// <param name="path">new App URL</param>
|
||||
/// <param name="roomKey">room key. Default is null</param>
|
||||
/// <remarks>
|
||||
/// If roomKey is null, the URL will be set for the entire system.
|
||||
/// </remarks>
|
||||
public void SetClientUrl(string path, string roomKey = null)
|
||||
{
|
||||
var message = new MobileControlMessage
|
||||
@@ -2029,9 +2044,6 @@ namespace PepperDash.Essentials
|
||||
/// Sends any object type to server
|
||||
/// </summary>
|
||||
/// <param name="o"></param>
|
||||
/// <summary>
|
||||
/// SendMessageObject method
|
||||
/// </summary>
|
||||
public void SendMessageObject(IMobileControlMessage o)
|
||||
{
|
||||
|
||||
@@ -2055,8 +2067,9 @@ namespace PepperDash.Essentials
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// SendMessageObjectToDirectClient method
|
||||
/// Send a message to a client using the Direct Server
|
||||
/// </summary>
|
||||
/// <param name="o">object to send</param>
|
||||
public void SendMessageObjectToDirectClient(object o)
|
||||
{
|
||||
if (
|
||||
@@ -2069,10 +2082,6 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Disconnects the Websocket Client and stops the heartbeat timer
|
||||
/// </summary>
|
||||
private void CleanUpWebsocketClient()
|
||||
{
|
||||
if (_wsClient2 == null)
|
||||
@@ -2130,9 +2139,6 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
private void StartServerReconnectTimer()
|
||||
{
|
||||
StopServerReconnectTimer();
|
||||
@@ -2143,9 +2149,6 @@ namespace PepperDash.Essentials
|
||||
this.LogDebug("Reconnect Timer Started.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does what it says
|
||||
/// </summary>
|
||||
private void StopServerReconnectTimer()
|
||||
{
|
||||
if (_serverReconnectTimer == null)
|
||||
@@ -2156,10 +2159,6 @@ namespace PepperDash.Essentials
|
||||
_serverReconnectTimer = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets reconnect timer and updates usercode
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
private void HandleHeartBeat(JToken content)
|
||||
{
|
||||
SendMessageObject(new MobileControlMessage { Type = "/system/heartbeatAck" });
|
||||
@@ -2180,6 +2179,7 @@ namespace PepperDash.Essentials
|
||||
{
|
||||
var clientId = content["clientId"].Value<string>();
|
||||
var roomKey = content["roomKey"].Value<string>();
|
||||
var touchpanelKey = content.SelectToken("touchpanelKey");
|
||||
|
||||
if (_roomCombiner == null)
|
||||
{
|
||||
@@ -2191,11 +2191,33 @@ namespace PepperDash.Essentials
|
||||
};
|
||||
|
||||
SendMessageObject(message);
|
||||
|
||||
SendDeviceInterfaces(clientId);
|
||||
|
||||
SendTouchpanelKey(clientId, touchpanelKey);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_roomCombiner.CurrentScenario == null)
|
||||
{
|
||||
var message = new MobileControlMessage
|
||||
{
|
||||
Type = "/system/roomKey",
|
||||
ClientId = clientId,
|
||||
Content = roomKey
|
||||
};
|
||||
|
||||
SendMessageObject(message);
|
||||
|
||||
SendDeviceInterfaces(clientId);
|
||||
|
||||
SendTouchpanelKey(clientId, touchpanelKey);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_roomCombiner.CurrentScenario.UiMap.ContainsKey(roomKey))
|
||||
{
|
||||
|
||||
this.LogWarning(
|
||||
"Unable to find correct roomKey for {roomKey} in current scenario. Returning {roomKey} as roomKey", roomKey);
|
||||
|
||||
@@ -2207,6 +2229,10 @@ namespace PepperDash.Essentials
|
||||
};
|
||||
|
||||
SendMessageObject(message);
|
||||
|
||||
SendDeviceInterfaces(clientId);
|
||||
|
||||
SendTouchpanelKey(clientId, touchpanelKey);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2220,6 +2246,54 @@ namespace PepperDash.Essentials
|
||||
};
|
||||
|
||||
SendMessageObject(newMessage);
|
||||
|
||||
SendDeviceInterfaces(clientId);
|
||||
|
||||
SendTouchpanelKey(clientId, touchpanelKey);
|
||||
}
|
||||
|
||||
private void SendTouchpanelKey(string clientId, JToken touchpanelKeyToken)
|
||||
{
|
||||
if (touchpanelKeyToken == null)
|
||||
{
|
||||
this.LogWarning("Touchpanel key not found for client {clientId}", clientId);
|
||||
return;
|
||||
}
|
||||
|
||||
SendMessageObject(new MobileControlMessage
|
||||
{
|
||||
Type = "/system/touchpanelKey",
|
||||
ClientId = clientId,
|
||||
Content = touchpanelKeyToken.Value<string>()
|
||||
});
|
||||
}
|
||||
|
||||
private void SendDeviceInterfaces(string clientId)
|
||||
{
|
||||
this.LogDebug("Sending Device interfaces");
|
||||
var devices = DeviceManager.GetDevices();
|
||||
Dictionary<string, DeviceInterfaceInfo> deviceInterfaces = new Dictionary<string, DeviceInterfaceInfo>();
|
||||
|
||||
foreach (var device in devices)
|
||||
{
|
||||
var interfaces = device?.GetType().GetInterfaces().Select((i) => i.Name).ToList() ?? new List<string>();
|
||||
|
||||
deviceInterfaces.Add(device.Key, new DeviceInterfaceInfo
|
||||
{
|
||||
Key = device.Key,
|
||||
Name = (device as IKeyName)?.Name ?? "",
|
||||
Interfaces = interfaces
|
||||
});
|
||||
}
|
||||
|
||||
var message = new MobileControlMessage
|
||||
{
|
||||
Type = "/system/deviceInterfaces",
|
||||
ClientId = clientId,
|
||||
Content = JToken.FromObject(new { deviceInterfaces })
|
||||
};
|
||||
|
||||
SendMessageObject(message);
|
||||
}
|
||||
|
||||
private void HandleUserCode(JToken content, Action<string, string> action = null)
|
||||
@@ -2256,16 +2330,13 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HandleClientMessage method
|
||||
/// Enqueue an incoming message for processing
|
||||
/// </summary>
|
||||
public void HandleClientMessage(string message)
|
||||
{
|
||||
_receiveQueue.Enqueue(new ProcessStringMessage(message, ParseStreamRx));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
private void ParseStreamRx(string messageText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(messageText))
|
||||
@@ -2333,10 +2404,33 @@ namespace PepperDash.Essentials
|
||||
|
||||
foreach (var handler in handlers)
|
||||
{
|
||||
Task.Run(
|
||||
() =>
|
||||
handler.Action(message.Type, message.ClientId, message.Content)
|
||||
);
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
handler.Action(message.Type, message.ClientId, message.Content);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.LogError(
|
||||
"Exception in handler for message type {type}, ClientId {clientId}",
|
||||
message.Type,
|
||||
message.ClientId
|
||||
);
|
||||
this.LogDebug(ex, "Stack Trace: ");
|
||||
}
|
||||
}).ContinueWith(task =>
|
||||
{
|
||||
if (task.IsFaulted && task.Exception != null)
|
||||
{
|
||||
this.LogError(
|
||||
"Unhandled exception in Task for message type {type}, ClientId {clientId}",
|
||||
message.Type,
|
||||
message.ClientId
|
||||
);
|
||||
this.LogDebug(task.Exception.GetBaseException(), "Stack Trace: ");
|
||||
}
|
||||
}, TaskContinuationOptions.OnlyOnFaulted);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -2352,10 +2446,6 @@ namespace PepperDash.Essentials
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
private void TestHttpRequest(string s)
|
||||
{
|
||||
{
|
||||
@@ -2420,33 +2510,4 @@ namespace PepperDash.Essentials
|
||||
CrestronConsole.ConsoleCommandResponse("Usage: mobilehttprequest:N get/post url\r");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a ClientSpecificUpdateRequest
|
||||
/// </summary>
|
||||
public class ClientSpecificUpdateRequest
|
||||
{
|
||||
public ClientSpecificUpdateRequest(Action<string> action)
|
||||
{
|
||||
ResponseMethod = action;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ResponseMethod
|
||||
/// </summary>
|
||||
public Action<string> ResponseMethod { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a UserCodeChanged
|
||||
/// </summary>
|
||||
public class UserCodeChanged
|
||||
{
|
||||
public Action<string, string> UpdateUserCode { get; private set; }
|
||||
|
||||
public UserCodeChanged(Action<string, string> updateMethod)
|
||||
{
|
||||
UpdateUserCode = updateMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,35 @@
|
||||
using PepperDash.Core;
|
||||
using System;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Logging;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
|
||||
using System;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.RoomBridges
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// Base class for a Mobile Control Bridge that's used to control a room
|
||||
/// </summary>
|
||||
public abstract class MobileControlBridgeBase : MessengerBase, IMobileControlRoomMessenger
|
||||
{
|
||||
/// <summary>
|
||||
/// Triggered when the user Code changes
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs> UserCodeChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a user should be prompted for the new code
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs> UserPromptedForCode;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a client joins to control this room
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs> ClientJoined;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the App URL for this room changes
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs> AppUrlChanged;
|
||||
|
||||
/// <summary>
|
||||
@@ -49,15 +61,32 @@ namespace PepperDash.Essentials.RoomBridges
|
||||
/// </summary>
|
||||
public string McServerUrl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Room Name
|
||||
/// </summary>
|
||||
public abstract string RoomName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Room key
|
||||
/// </summary>
|
||||
public abstract string RoomKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="MobileControlBridgeBase"/> class
|
||||
/// </summary>
|
||||
/// <param name="key">The unique key for this bridge</param>
|
||||
/// <param name="messagePath">The message path for this bridge</param>
|
||||
protected MobileControlBridgeBase(string key, string messagePath)
|
||||
: base(key, messagePath)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="MobileControlBridgeBase"/> class
|
||||
/// </summary>
|
||||
/// <param name="key">The unique key for this bridge</param>
|
||||
/// <param name="messagePath">The message path for this bridge</param>
|
||||
/// <param name="device">The device associated with this bridge</param>
|
||||
protected MobileControlBridgeBase(string key, string messagePath, IKeyName device)
|
||||
: base(key, messagePath, device)
|
||||
{
|
||||
@@ -110,6 +139,10 @@ namespace PepperDash.Essentials.RoomBridges
|
||||
SetUserCode(code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the App Url with the provided URL
|
||||
/// </summary>
|
||||
/// <param name="url">The new App URL</param>
|
||||
public virtual void UpdateAppUrl(string url)
|
||||
{
|
||||
AppUrl = url;
|
||||
@@ -137,16 +170,25 @@ namespace PepperDash.Essentials.RoomBridges
|
||||
OnUserCodeChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger the UserCodeChanged event
|
||||
/// </summary>
|
||||
protected void OnUserCodeChanged()
|
||||
{
|
||||
UserCodeChanged?.Invoke(this, new EventArgs());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger the UserPromptedForCode event
|
||||
/// </summary>
|
||||
protected void OnUserPromptedForCode()
|
||||
{
|
||||
UserPromptedForCode?.Invoke(this, new EventArgs());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger the ClientJoined event
|
||||
/// </summary>
|
||||
protected void OnClientJoined()
|
||||
{
|
||||
ClientJoined?.Invoke(this, new EventArgs());
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
@@ -17,9 +20,6 @@ using PepperDash.Essentials.Devices.Common.Room;
|
||||
using PepperDash.Essentials.Devices.Common.VideoCodec;
|
||||
using PepperDash.Essentials.Room.Config;
|
||||
using PepperDash.Essentials.WebSocketServer;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using IShades = PepperDash.Essentials.Core.Shades.IShades;
|
||||
using ShadeBase = PepperDash.Essentials.Devices.Common.Shades.ShadeBase;
|
||||
|
||||
@@ -41,24 +41,37 @@ namespace PepperDash.Essentials.RoomBridges
|
||||
/// </summary>
|
||||
public string DefaultRoomKey { get; private set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets the name of the room
|
||||
/// </summary>
|
||||
public override string RoomName
|
||||
{
|
||||
get { return Room.Name; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the key of the room
|
||||
/// </summary>
|
||||
public override string RoomKey
|
||||
{
|
||||
get { return Room.Key; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MobileControlEssentialsRoomBridge"/> class with the specified room
|
||||
/// </summary>
|
||||
/// <param name="room">The essentials room to bridge</param>
|
||||
public MobileControlEssentialsRoomBridge(IEssentialsRoom room) :
|
||||
this($"mobileControlBridge-{room.Key}", room.Key, room)
|
||||
{
|
||||
Room = room;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MobileControlEssentialsRoomBridge"/> class with the specified parameters
|
||||
/// </summary>
|
||||
/// <param name="key">The unique key for this bridge</param>
|
||||
/// <param name="roomKey">The key of the room to bridge</param>
|
||||
/// <param name="room">The essentials room to bridge</param>
|
||||
public MobileControlEssentialsRoomBridge(string key, string roomKey, IEssentialsRoom room) : base(key, $"/room/{room.Key}", room as Device)
|
||||
{
|
||||
DefaultRoomKey = roomKey;
|
||||
@@ -66,7 +79,9 @@ namespace PepperDash.Essentials.RoomBridges
|
||||
AddPreActivationAction(GetRoom);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Registers all message handling actions with the AppServer for this room bridge
|
||||
/// </summary>
|
||||
protected override void RegisterActions()
|
||||
{
|
||||
// we add actions to the messaging system with a path, and a related action. Custom action
|
||||
@@ -284,6 +299,9 @@ namespace PepperDash.Essentials.RoomBridges
|
||||
Room = tempRoom;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles user code changes and generates QR code URL
|
||||
/// </summary>
|
||||
protected override void UserCodeChange()
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Server user code changed: {userCode}", this, UserCode);
|
||||
@@ -485,6 +503,7 @@ namespace PepperDash.Essentials.RoomBridges
|
||||
/// Sends the full status of the room to the server
|
||||
/// </summary>
|
||||
/// <param name="room"></param>
|
||||
/// <param name="id"></param>
|
||||
private void SendFullStatusForClientId(string id, IEssentialsRoom room)
|
||||
{
|
||||
//Parent.SendMessageObject(GetFullStatus(room));
|
||||
@@ -554,6 +573,7 @@ namespace PepperDash.Essentials.RoomBridges
|
||||
|
||||
/// <summary>
|
||||
/// Determines the configuration of the room and the details about the devices associated with the room
|
||||
/// </summary>
|
||||
/// <param name="room"></param>
|
||||
/// <returns></returns>
|
||||
private RoomConfiguration GetRoomConfiguration(IEssentialsRoom room)
|
||||
@@ -798,36 +818,64 @@ namespace PepperDash.Essentials.RoomBridges
|
||||
/// </summary>
|
||||
public class RoomStateMessage : DeviceStateMessageBase
|
||||
{
|
||||
[JsonProperty("configuration", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Configuration
|
||||
/// </summary>
|
||||
[JsonProperty("configuration", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public RoomConfiguration Configuration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the activity mode of the room
|
||||
/// </summary>
|
||||
[JsonProperty("activityMode", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public int? ActivityMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether advanced sharing is active
|
||||
/// </summary>
|
||||
[JsonProperty("advancedSharingActive", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? AdvancedSharingActive { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the room is powered on
|
||||
/// </summary>
|
||||
[JsonProperty("isOn", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? IsOn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the room is warming up
|
||||
/// </summary>
|
||||
[JsonProperty("isWarmingUp", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? IsWarmingUp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the room is cooling down
|
||||
/// </summary>
|
||||
[JsonProperty("isCoolingDown", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? IsCoolingDown { get; set; }
|
||||
[JsonProperty("selectedSourceKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the SelectedSourceKey
|
||||
/// </summary>
|
||||
[JsonProperty("selectedSourceKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string SelectedSourceKey { get; set; }
|
||||
[JsonProperty("share", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Share
|
||||
/// </summary>
|
||||
[JsonProperty("share", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public ShareState Share { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the volume controls collection
|
||||
/// </summary>
|
||||
[JsonProperty("volumes", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Dictionary<string, Volume> Volumes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the room is in a call
|
||||
/// </summary>
|
||||
[JsonProperty("isInCall", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? IsInCall { get; set; }
|
||||
}
|
||||
@@ -837,13 +885,22 @@ namespace PepperDash.Essentials.RoomBridges
|
||||
/// </summary>
|
||||
public class ShareState
|
||||
{
|
||||
[JsonProperty("currentShareText", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the CurrentShareText
|
||||
/// </summary>
|
||||
[JsonProperty("currentShareText", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string CurrentShareText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether sharing is enabled
|
||||
/// </summary>
|
||||
[JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether content is currently being shared
|
||||
/// </summary>
|
||||
[JsonProperty("isSharing", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? IsSharing { get; set; }
|
||||
}
|
||||
@@ -853,133 +910,200 @@ namespace PepperDash.Essentials.RoomBridges
|
||||
/// </summary>
|
||||
public class RoomConfiguration
|
||||
{
|
||||
//[JsonProperty("shutdownPromptSeconds", NullValueHandling = NullValueHandling.Ignore)]
|
||||
//public int? ShutdownPromptSeconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the room has video conferencing capabilities
|
||||
/// </summary>
|
||||
[JsonProperty("hasVideoConferencing", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasVideoConferencing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the video codec is a Zoom Room
|
||||
/// </summary>
|
||||
[JsonProperty("videoCodecIsZoomRoom", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? VideoCodecIsZoomRoom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the room has audio conferencing capabilities
|
||||
/// </summary>
|
||||
[JsonProperty("hasAudioConferencing", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasAudioConferencing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the room has environmental controls (lighting, shades, etc.)
|
||||
/// </summary>
|
||||
[JsonProperty("hasEnvironmentalControls", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasEnvironmentalControls { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the room has camera controls
|
||||
/// </summary>
|
||||
[JsonProperty("hasCameraControls", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasCameraControls { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the room has set-top box controls
|
||||
/// </summary>
|
||||
[JsonProperty("hasSetTopBoxControls", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasSetTopBoxControls { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the room has routing controls
|
||||
/// </summary>
|
||||
[JsonProperty("hasRoutingControls", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasRoutingControls { get; set; }
|
||||
|
||||
[JsonProperty("touchpanelKeys", NullValueHandling = NullValueHandling.Ignore)]
|
||||
/// <summary>
|
||||
/// Gets or sets the TouchpanelKeys
|
||||
/// </summary>
|
||||
[JsonProperty("touchpanelKeys", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public List<string> TouchpanelKeys { get; set; }
|
||||
|
||||
[JsonProperty("zoomRoomControllerKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ZoomRoomControllerKey
|
||||
/// </summary>
|
||||
[JsonProperty("zoomRoomControllerKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string ZoomRoomControllerKey { get; set; }
|
||||
|
||||
[JsonProperty("ciscoNavigatorKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the CiscoNavigatorKey
|
||||
/// </summary>
|
||||
[JsonProperty("ciscoNavigatorKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string CiscoNavigatorKey { get; set; }
|
||||
|
||||
|
||||
[JsonProperty("videoCodecKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the VideoCodecKey
|
||||
/// </summary>
|
||||
[JsonProperty("videoCodecKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string VideoCodecKey { get; set; }
|
||||
[JsonProperty("audioCodecKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the AudioCodecKey
|
||||
/// </summary>
|
||||
[JsonProperty("audioCodecKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string AudioCodecKey { get; set; }
|
||||
[JsonProperty("matrixRoutingKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the MatrixRoutingKey
|
||||
/// </summary>
|
||||
[JsonProperty("matrixRoutingKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string MatrixRoutingKey { get; set; }
|
||||
[JsonProperty("endpointKeys", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the EndpointKeys
|
||||
/// </summary>
|
||||
[JsonProperty("endpointKeys", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public List<string> EndpointKeys { get; set; }
|
||||
|
||||
[JsonProperty("accessoryDeviceKeys", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the AccessoryDeviceKeys
|
||||
/// </summary>
|
||||
[JsonProperty("accessoryDeviceKeys", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public List<string> AccessoryDeviceKeys { get; set; }
|
||||
|
||||
[JsonProperty("defaultDisplayKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DefaultDisplayKey
|
||||
/// </summary>
|
||||
[JsonProperty("defaultDisplayKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string DefaultDisplayKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the destinations dictionary keyed by destination type
|
||||
/// </summary>
|
||||
[JsonProperty("destinations", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Dictionary<eSourceListItemDestinationTypes, string> Destinations { get; set; }
|
||||
[JsonProperty("environmentalDevices", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the EnvironmentalDevices
|
||||
/// </summary>
|
||||
[JsonProperty("environmentalDevices", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public List<EnvironmentalDeviceConfiguration> EnvironmentalDevices { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the source list for the room
|
||||
/// </summary>
|
||||
[JsonProperty("sourceList", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Dictionary<string, SourceListItem> SourceList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the destination list for the room
|
||||
/// </summary>
|
||||
[JsonProperty("destinationList", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Dictionary<string, DestinationListItem> DestinationList { get; set; }
|
||||
|
||||
[JsonProperty("audioControlPointList", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the AudioControlPointList
|
||||
/// </summary>
|
||||
[JsonProperty("audioControlPointList", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public AudioControlPointListItem AudioControlPointList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the camera list for the room
|
||||
/// </summary>
|
||||
[JsonProperty("cameraList", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Dictionary<string, CameraListItem> CameraList { get; set; }
|
||||
|
||||
[JsonProperty("defaultPresentationSourceKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DefaultPresentationSourceKey
|
||||
/// </summary>
|
||||
[JsonProperty("defaultPresentationSourceKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string DefaultPresentationSourceKey { get; set; }
|
||||
|
||||
|
||||
[JsonProperty("helpMessage", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the HelpMessage
|
||||
/// </summary>
|
||||
[JsonProperty("helpMessage", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string HelpMessage { get; set; }
|
||||
|
||||
[JsonProperty("techPassword", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the TechPassword
|
||||
/// </summary>
|
||||
[JsonProperty("techPassword", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string TechPassword { get; set; }
|
||||
|
||||
[JsonProperty("uiBehavior", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the UiBehavior
|
||||
/// </summary>
|
||||
[JsonProperty("uiBehavior", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public EssentialsRoomUiBehaviorConfig UiBehavior { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the room supports advanced sharing features
|
||||
/// </summary>
|
||||
[JsonProperty("supportsAdvancedSharing", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? SupportsAdvancedSharing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the user can change the share mode
|
||||
/// </summary>
|
||||
[JsonProperty("userCanChangeShareMode", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? UserCanChangeShareMode { get; set; }
|
||||
|
||||
[JsonProperty("roomCombinerKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the RoomCombinerKey
|
||||
/// </summary>
|
||||
[JsonProperty("roomCombinerKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string RoomCombinerKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoomConfiguration"/> class
|
||||
/// </summary>
|
||||
public RoomConfiguration()
|
||||
{
|
||||
Destinations = new Dictionary<eSourceListItemDestinationTypes, string>();
|
||||
@@ -994,19 +1118,26 @@ namespace PepperDash.Essentials.RoomBridges
|
||||
/// </summary>
|
||||
public class EnvironmentalDeviceConfiguration
|
||||
{
|
||||
[JsonProperty("deviceKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DeviceKey
|
||||
/// </summary>
|
||||
[JsonProperty("deviceKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string DeviceKey { get; private set; }
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
[JsonProperty("deviceType", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DeviceType
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
[JsonProperty("deviceType", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public eEnvironmentalDeviceTypes DeviceType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EnvironmentalDeviceConfiguration"/> class
|
||||
/// </summary>
|
||||
/// <param name="key">The device key</param>
|
||||
/// <param name="type">The environmental device type</param>
|
||||
public EnvironmentalDeviceConfiguration(string key, eEnvironmentalDeviceTypes type)
|
||||
{
|
||||
DeviceKey = key;
|
||||
@@ -1015,14 +1146,29 @@ namespace PepperDash.Essentials.RoomBridges
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of eEnvironmentalDeviceTypes values
|
||||
/// Enumeration of environmental device types
|
||||
/// </summary>
|
||||
public enum eEnvironmentalDeviceTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// No environmental device type specified
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// Lighting device type
|
||||
/// </summary>
|
||||
Lighting,
|
||||
/// <summary>
|
||||
/// Shade device type
|
||||
/// </summary>
|
||||
Shade,
|
||||
/// <summary>
|
||||
/// Shade controller device type
|
||||
/// </summary>
|
||||
ShadeController,
|
||||
/// <summary>
|
||||
/// Relay device type
|
||||
/// </summary>
|
||||
Relay,
|
||||
}
|
||||
|
||||
@@ -1031,57 +1177,18 @@ namespace PepperDash.Essentials.RoomBridges
|
||||
/// </summary>
|
||||
public class ApiTouchPanelToken
|
||||
{
|
||||
[JsonProperty("touchPanels", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the TouchPanels
|
||||
/// </summary>
|
||||
[JsonProperty("touchPanels", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public List<JoinToken> TouchPanels { get; set; } = new List<JoinToken>();
|
||||
|
||||
[JsonProperty("userAppUrl", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the UserAppUrl
|
||||
/// </summary>
|
||||
[JsonProperty("userAppUrl", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string UserAppUrl { get; set; } = "";
|
||||
}
|
||||
|
||||
#if SERIES3
|
||||
/// <summary>
|
||||
/// Represents a SourceSelectMessageContent
|
||||
/// </summary>
|
||||
public class SourceSelectMessageContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the SourceListItem
|
||||
/// </summary>
|
||||
public string SourceListItem { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the SourceListKey
|
||||
/// </summary>
|
||||
public string SourceListKey { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a DirectRoute
|
||||
/// </summary>
|
||||
public class DirectRoute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the SourceKey
|
||||
/// </summary>
|
||||
public string SourceKey { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the DestinationKey
|
||||
/// </summary>
|
||||
public string DestinationKey { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="b"></param>
|
||||
/// <summary>
|
||||
/// Delegate for PressAndHoldAction
|
||||
/// </summary>
|
||||
public delegate void PressAndHoldAction(bool b);
|
||||
#endif
|
||||
}
|
||||
@@ -1,18 +1,22 @@
|
||||
using PepperDash.Core;
|
||||
using System;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Essentials.Services
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents a MobileControlApiService
|
||||
/// Service for interacting with a Mobile Control Edge server instance
|
||||
/// </summary>
|
||||
public class MobileControlApiService
|
||||
{
|
||||
private readonly HttpClient _client;
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="MobileControlApiService"/> class.
|
||||
/// </summary>
|
||||
/// <param name="apiUrl">Mobile Control Edge API URL</param>
|
||||
public MobileControlApiService(string apiUrl)
|
||||
{
|
||||
var handler = new HttpClientHandler
|
||||
@@ -24,6 +28,13 @@ namespace PepperDash.Essentials.Services
|
||||
_client = new HttpClient(handler);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send authorization request to Mobile Control Edge Server
|
||||
/// </summary>
|
||||
/// <param name="apiUrl">Mobile Control Edge API URL</param>
|
||||
/// <param name="grantCode">Grant code for authorization</param>
|
||||
/// <param name="systemUuid">System UUID for authorization</param>
|
||||
/// <returns>Authorization response</returns>
|
||||
public async Task<AuthorizationResponse> SendAuthorizationRequest(string apiUrl, string grantCode, string systemUuid)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -7,8 +7,15 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
/// </summary>
|
||||
public interface ITheme : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
/// Current theme
|
||||
/// </summary>
|
||||
string Theme { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Set the theme with the given value
|
||||
/// </summary>
|
||||
/// <param name="theme">The theme to set</param>
|
||||
void UpdateTheme(string theme);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,24 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
/// </summary>
|
||||
public interface ITswAppControl : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
/// Updates when the Zoom Room Control Application opens or closes
|
||||
/// </summary>
|
||||
BoolFeedback AppOpenFeedback { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Hide the Zoom App and show the User Control Application
|
||||
/// </summary>
|
||||
void HideOpenApp();
|
||||
|
||||
/// <summary>
|
||||
/// Close the Zoom App and show the User Control Application
|
||||
/// </summary>
|
||||
void CloseOpenApp();
|
||||
|
||||
/// <summary>
|
||||
/// Open the Zoom App
|
||||
/// </summary>
|
||||
void OpenApp();
|
||||
}
|
||||
|
||||
@@ -22,10 +34,19 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
/// </summary>
|
||||
public interface ITswZoomControl : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
/// Updates when Zoom has an incoming call
|
||||
/// </summary>
|
||||
BoolFeedback ZoomIncomingCallFeedback { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates when Zoom is in a call
|
||||
/// </summary>
|
||||
BoolFeedback ZoomInCallFeedback { get; }
|
||||
|
||||
/// <summary>
|
||||
/// End a Zoom Call
|
||||
/// </summary>
|
||||
void EndZoomCall();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,24 @@ using PepperDash.Essentials.AppServer.Messengers;
|
||||
namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a ITswAppControlMessenger
|
||||
/// Messenger for controlling the Zoom App on a TSW Panel that supports the Zoom Room Control Application
|
||||
/// </summary>
|
||||
public class ITswAppControlMessenger : MessengerBase
|
||||
{
|
||||
private readonly ITswAppControl _appControl;
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="ITswAppControlMessenger"/> class.
|
||||
/// </summary>
|
||||
/// <param name="key">The key for this messenger</param>
|
||||
/// <param name="messagePath">The message path for this messenger</param>
|
||||
/// <param name="device">The device for this messenger</param>
|
||||
public ITswAppControlMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)
|
||||
{
|
||||
_appControl = device as ITswAppControl;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RegisterActions()
|
||||
{
|
||||
if (_appControl == null)
|
||||
@@ -26,7 +33,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
return;
|
||||
}
|
||||
|
||||
AddAction($"/fullStatus", (id, context) => SendFullStatus());
|
||||
AddAction($"/fullStatus", (id, context) => SendFullStatus(id));
|
||||
|
||||
AddAction($"/openApp", (id, context) => _appControl.OpenApp());
|
||||
|
||||
@@ -43,14 +50,14 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
};
|
||||
}
|
||||
|
||||
private void SendFullStatus()
|
||||
private void SendFullStatus(string id = null)
|
||||
{
|
||||
var message = new TswAppStateMessage
|
||||
{
|
||||
AppOpen = _appControl.AppOpenFeedback.BoolValue,
|
||||
};
|
||||
|
||||
PostStatusMessage(message);
|
||||
PostStatusMessage(message, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +66,9 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
/// </summary>
|
||||
public class TswAppStateMessage : DeviceStateMessageBase
|
||||
{
|
||||
/// <summary>
|
||||
/// True if the Zoom app is open on a TSW panel
|
||||
/// </summary>
|
||||
[JsonProperty("appOpen", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? AppOpen { get; set; }
|
||||
}
|
||||
|
||||
@@ -8,17 +8,24 @@ using PepperDash.Essentials.AppServer.Messengers;
|
||||
namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a ITswZoomControlMessenger
|
||||
/// Messenger to handle Zoom status and control for a TSW panel that supports the Zoom Application
|
||||
/// </summary>
|
||||
public class ITswZoomControlMessenger : MessengerBase
|
||||
{
|
||||
private readonly ITswZoomControl _zoomControl;
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="ITswZoomControlMessenger"/> class for the given device
|
||||
/// </summary>
|
||||
/// <param name="key">The key for this messenger</param>
|
||||
/// <param name="messagePath">The message path for this messenger</param>
|
||||
/// <param name="device">The device for this messenger</param>
|
||||
public ITswZoomControlMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)
|
||||
{
|
||||
_zoomControl = device as ITswZoomControl;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RegisterActions()
|
||||
{
|
||||
if (_zoomControl == null)
|
||||
@@ -27,7 +34,9 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
return;
|
||||
}
|
||||
|
||||
AddAction($"/fullStatus", (id, context) => SendFullStatus());
|
||||
AddAction($"/fullStatus", (id, context) => SendFullStatus(id));
|
||||
|
||||
AddAction($"/zoomStatus", (id, content) => SendFullStatus(id));
|
||||
|
||||
|
||||
AddAction($"/endCall", (id, context) => _zoomControl.EndZoomCall());
|
||||
@@ -53,7 +62,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
};
|
||||
}
|
||||
|
||||
private void SendFullStatus()
|
||||
private void SendFullStatus(string id = null)
|
||||
{
|
||||
var message = new TswZoomStateMessage
|
||||
{
|
||||
@@ -61,7 +70,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
IncomingCall = _zoomControl?.ZoomIncomingCallFeedback.BoolValue
|
||||
};
|
||||
|
||||
PostStatusMessage(message);
|
||||
PostStatusMessage(message, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,9 +79,16 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
/// </summary>
|
||||
public class TswZoomStateMessage : DeviceStateMessageBase
|
||||
{
|
||||
/// <summary>
|
||||
/// True if the panel is in a Zoom call
|
||||
/// </summary>
|
||||
[JsonProperty("inCall", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? InCall { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if there is an incoming Zoom call
|
||||
/// </summary>
|
||||
|
||||
[JsonProperty("incomingCall", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? IncomingCall { get; set; }
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ using PepperDash.Essentials.Core.DeviceInfo;
|
||||
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
|
||||
using PepperDash.Essentials.Core.UI;
|
||||
using Feedback = PepperDash.Essentials.Core.Feedback;
|
||||
using IPAddress = System.Net.IPAddress;
|
||||
|
||||
namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
@@ -106,11 +107,14 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
/// </summary>
|
||||
public DeviceInfo DeviceInfo => new DeviceInfo();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of connected IPs for this IpId
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<ConnectedIpInformation> ConnectedIps => Panel.ConnectedIpList;
|
||||
|
||||
private System.Net.IPAddress csIpAddress;
|
||||
private readonly IPAddress csIpAddress;
|
||||
|
||||
private System.Net.IPAddress csSubnetMask;
|
||||
private readonly IPAddress csSubnetMask;
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -190,12 +194,27 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
|
||||
RegisterForExtenders();
|
||||
|
||||
var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);
|
||||
var csSubnetMask = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, csAdapterId);
|
||||
var csIpAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId);
|
||||
try
|
||||
{
|
||||
var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);
|
||||
var csSubnetMask = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, csAdapterId);
|
||||
var csIpAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId);
|
||||
|
||||
this.csSubnetMask = System.Net.IPAddress.Parse(csSubnetMask);
|
||||
this.csIpAddress = System.Net.IPAddress.Parse(csIpAddress);
|
||||
this.csSubnetMask = IPAddress.Parse(csSubnetMask);
|
||||
this.csIpAddress = IPAddress.Parse(csIpAddress);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
Debug.LogInformation("This processor does not have a CS LAN", this);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
Debug.LogInformation("This processor does not have a CS LAN", this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Unexpected exception when checking CS LAN: {ex}", this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -226,13 +245,14 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
x70Panel.ExtenderApplicationControlReservedSigs.DeviceExtenderSigChange += (e, a) =>
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X70 App Control Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}");
|
||||
this.LogVerbose("X70 App Control Device Extender args: {event}:{sig}:{type}:{boolValue}:{ushortValue}:{stringValue}", a.Event, a.Sig, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue);
|
||||
|
||||
UpdateZoomFeedbacks();
|
||||
|
||||
if (!x70Panel.ExtenderApplicationControlReservedSigs.HideOpenedApplicationFeedback.BoolValue)
|
||||
{
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.ShowButtonToolbar();
|
||||
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.Button2On();
|
||||
}
|
||||
else
|
||||
@@ -245,7 +265,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
|
||||
x70Panel.ExtenderZoomRoomAppReservedSigs.DeviceExtenderSigChange += (e, a) =>
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X70 Zoom Room Ap Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}");
|
||||
this.LogVerbose("X70 Zoom Room App Device Extender args: {event}:{sig}:{type}:{boolValue}:{ushortValue}:{stringValue}", a.Event, a.Sig, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue);
|
||||
|
||||
if (a.Sig.Number == x70Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.Number)
|
||||
{
|
||||
@@ -263,7 +283,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
DeviceInfo.MacAddress = x70Panel.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;
|
||||
DeviceInfo.IpAddress = x70Panel.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"MAC: {DeviceInfo.MacAddress} IP: {DeviceInfo.IpAddress}");
|
||||
this.LogDebug("MAC: {macAddress} IP: {ipAddress}", DeviceInfo.MacAddress, DeviceInfo.IpAddress);
|
||||
|
||||
var handler = DeviceInfoChanged;
|
||||
|
||||
@@ -275,17 +295,16 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
handler(this, new DeviceInfoEventArgs(DeviceInfo));
|
||||
};
|
||||
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.DeviceExtenderSigChange += (o, a) =>
|
||||
{
|
||||
this.LogVerbose("X70 Button Toolbar Device Extender args: {event}:{sig}:{name}:{type}:{boolValue}:{ushortValue}:{stringValue}", a.Event, a.Sig, a.Sig.Name, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue);
|
||||
};
|
||||
|
||||
x70Panel.ExtenderApplicationControlReservedSigs.Use();
|
||||
x70Panel.ExtenderZoomRoomAppReservedSigs.Use();
|
||||
x70Panel.ExtenderEthernetReservedSigs.Use();
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.Use();
|
||||
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.Button1Off();
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.Button3Off();
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.Button4Off();
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.Button5Off();
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.Button6Off();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -293,7 +312,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
x60withZoomApp.ExtenderApplicationControlReservedSigs.DeviceExtenderSigChange += (e, a) =>
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X60 App Control Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}");
|
||||
this.LogVerbose("X60 App Control Device Extender args: {event}:{sig}:{type}:{boolValue}:{ushortValue}:{stringValue}", a.Event, a.Sig, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue);
|
||||
|
||||
if (a.Sig.Number == x60withZoomApp.ExtenderApplicationControlReservedSigs.HideOpenApplicationFeedback.Number)
|
||||
{
|
||||
@@ -302,7 +321,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
};
|
||||
x60withZoomApp.ExtenderZoomRoomAppReservedSigs.DeviceExtenderSigChange += (e, a) =>
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"X60 Zoom Room App Device Extender args: {a.Event}:{a.Sig}:{a.Sig.Type}:{a.Sig.BoolValue}:{a.Sig.UShortValue}:{a.Sig.StringValue}");
|
||||
this.LogVerbose("X60 Zoom Room App Device Extender args: {event}:{sig}:{type}:{boolValue}:{ushortValue}:{stringValue}", a.Event, a.Sig, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue);
|
||||
|
||||
if (a.Sig.Number == x60withZoomApp.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.Number)
|
||||
{
|
||||
@@ -319,7 +338,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
DeviceInfo.MacAddress = x60withZoomApp.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;
|
||||
DeviceInfo.IpAddress = x60withZoomApp.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"MAC: {DeviceInfo.MacAddress} IP: {DeviceInfo.IpAddress}");
|
||||
this.LogDebug("MAC: {macAddress} IP: {ipAddress}", DeviceInfo.MacAddress, DeviceInfo.IpAddress);
|
||||
|
||||
var handler = DeviceInfoChanged;
|
||||
|
||||
@@ -381,7 +400,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
/// <param name="args">The signal event arguments containing the changed signal information.</param>
|
||||
protected override void ExtenderSystemReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"System Device Extender args: ${args.Event}:${args.Sig}");
|
||||
this.LogVerbose("System Device Extender args: {event}:{sig}", args.Event, args.Sig);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -395,34 +414,79 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
McServerUrlFeedback.LinkInputSig(Panel.StringInput[3]);
|
||||
UserCodeFeedback.LinkInputSig(Panel.StringInput[4]);
|
||||
|
||||
Panel.IpInformationChange += (sender, args) =>
|
||||
Panel.IpInformationChange -= Panel_IpInformationChange;
|
||||
Panel.IpInformationChange += Panel_IpInformationChange;
|
||||
|
||||
Panel.OnlineStatusChange -= Panel_OnlineChange;
|
||||
Panel.OnlineStatusChange += Panel_OnlineChange;
|
||||
}
|
||||
|
||||
private void Panel_OnlineChange(GenericBase sender, OnlineOfflineEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (args.Connected)
|
||||
if (!args.DeviceOnLine)
|
||||
{
|
||||
this.LogVerbose("Connection from IP: {ip}", args.DeviceIpAddress);
|
||||
this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue);
|
||||
|
||||
var appUrl = GetUrlWithCorrectIp(_appUrl);
|
||||
Panel.StringInput[1].StringValue = appUrl;
|
||||
|
||||
SetAppUrl(appUrl);
|
||||
this.LogInformation("panel is offline");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.LogVerbose("Disconnection from IP: {ip}", args.DeviceIpAddress);
|
||||
}
|
||||
};
|
||||
|
||||
Panel.OnlineStatusChange += (sender, args) =>
|
||||
{
|
||||
this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue);
|
||||
this.LogDebug("panel is online");
|
||||
|
||||
UpdateFeedbacks();
|
||||
Panel.StringInput[1].StringValue = _appUrl;
|
||||
Panel.StringInput[2].StringValue = QrCodeUrlFeedback.StringValue;
|
||||
Panel.StringInput[3].StringValue = McServerUrlFeedback.StringValue;
|
||||
Panel.StringInput[4].StringValue = UserCodeFeedback.StringValue;
|
||||
};
|
||||
|
||||
if (Panel is TswXX70Base x70Panel)
|
||||
{
|
||||
this.LogDebug("setting buttons off");
|
||||
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.Button1Off();
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.Button3Off();
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.Button4Off();
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.Button5Off();
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.Button6Off();
|
||||
}
|
||||
|
||||
SendUrlToPanel();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.LogError("Exception in panel online: {message}", ex.Message);
|
||||
this.LogDebug(ex, "Stack Trace: ");
|
||||
}
|
||||
}
|
||||
|
||||
private void SendUrlToPanel()
|
||||
{
|
||||
var appUrl = GetUrlWithCorrectIp(_appUrl);
|
||||
|
||||
this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue);
|
||||
|
||||
if (Panel.StringInput[1].StringValue == appUrl)
|
||||
{
|
||||
this.LogInformation("App URL already set to {appUrl}, no update needed", AppUrlFeedback.StringValue);
|
||||
return;
|
||||
}
|
||||
|
||||
Panel.StringInput[1].StringValue = appUrl;
|
||||
|
||||
SetAppUrl(appUrl);
|
||||
}
|
||||
|
||||
private void Panel_IpInformationChange(GenericBase sender, ConnectedIpEventArgs args)
|
||||
{
|
||||
if (args.Connected)
|
||||
{
|
||||
this.LogVerbose("Connection from IP: {ip}", args.DeviceIpAddress);
|
||||
SendUrlToPanel();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.LogVerbose("Disconnection from IP: {ip}", args.DeviceIpAddress);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -436,7 +500,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
|
||||
var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId);
|
||||
|
||||
if(csIpAddress == null || csSubnetMask == null || url == null)
|
||||
if (csIpAddress == null || csSubnetMask == null || url == null)
|
||||
{
|
||||
this.LogWarning("CS IP Address Subnet Mask or url is null, cannot determine correct IP for URL");
|
||||
return url;
|
||||
@@ -447,7 +511,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
|
||||
var ip = ConnectedIps.Any(ipInfo =>
|
||||
{
|
||||
if (System.Net.IPAddress.TryParse(ipInfo.DeviceIpAddress, out var parsedIp))
|
||||
if (IPAddress.TryParse(ipInfo.DeviceIpAddress, out var parsedIp))
|
||||
{
|
||||
return csIpAddress.IsInSameSubnet(parsedIp, csSubnetMask);
|
||||
}
|
||||
@@ -481,7 +545,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
|
||||
if (mcList.Count == 0)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"No Mobile Control controller found");
|
||||
this.LogError("No Mobile Control controller found");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -493,7 +557,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
|
||||
if (bridge == null)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"No Mobile Control bridge for {_config.DefaultRoomKey} found ");
|
||||
this.LogInformation("No Mobile Control bridge for {roomKey} found", _config.DefaultRoomKey);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -502,7 +566,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
_bridge.UserCodeChanged += UpdateFeedbacks;
|
||||
_bridge.AppUrlChanged += (s, a) =>
|
||||
{
|
||||
this.LogInformation("AppURL changed");
|
||||
this.LogInformation("AppURL changed: {appURL}", _bridge.AppUrl);
|
||||
SetAppUrl(_bridge.AppUrl);
|
||||
UpdateFeedbacks(s, a);
|
||||
};
|
||||
@@ -538,7 +602,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
foreach (var feedback in ZoomFeedbacks)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"Updating {feedback.Key}");
|
||||
this.LogDebug("Updating {feedbackKey}", feedback.Key);
|
||||
feedback.FireUpdate();
|
||||
}
|
||||
}
|
||||
@@ -574,7 +638,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
|
||||
if (Panel is TswX60WithZoomRoomAppReservedSigs)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"X60 panel does not support zoom app");
|
||||
this.LogInformation("X60 panel does not support zoom app");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -650,7 +714,16 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
handler(this, new DeviceInfoEventArgs(DeviceInfo));
|
||||
}
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"MAC: {DeviceInfo.MacAddress} IP: {DeviceInfo.IpAddress}");
|
||||
this.LogDebug("MAC: {macAddress} IP: {ipAddress}", DeviceInfo.MacAddress, DeviceInfo.IpAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force a reload of the iframe on the panel connected to this IP ID
|
||||
/// </summary>
|
||||
public void ReloadIframe()
|
||||
{
|
||||
this.LogInformation("Pulsing join 1");
|
||||
Panel.PulseBool(1, 100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -659,6 +732,8 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
/// </summary>
|
||||
public class MobileControlTouchpanelControllerFactory : EssentialsPluginDeviceFactory<MobileControlTouchpanelController>
|
||||
{
|
||||
private Dictionary<string, Func<uint, CrestronControlSystem, string, BasicTriListWithSmartObject>> factories;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the MobileControlTouchpanelControllerFactory class.
|
||||
/// Sets up supported device type names and minimum framework version requirements.
|
||||
@@ -667,6 +742,31 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
TypeNames = new List<string>() { "mccrestronapp", "mctsw550", "mctsw750", "mctsw1050", "mctsw560", "mctsw760", "mctsw1060", "mctsw570", "mctsw770", "mcts770", "mctsw1070", "mcts1070", "mcxpanel", "mcdge1000" };
|
||||
MinimumEssentialsFrameworkVersion = "2.0.0";
|
||||
|
||||
factories = new Dictionary<string, Func<uint, CrestronControlSystem, string, BasicTriListWithSmartObject>>
|
||||
{
|
||||
{"crestronapp", (id, controlSystem, projectName) => {
|
||||
var app = new CrestronApp(id, Global.ControlSystem);
|
||||
app.ParameterProjectName.Value = projectName;
|
||||
return app;
|
||||
}},
|
||||
{"xpanel", (id, controlSystem, projectName) => new XpanelForHtml5(id, controlSystem)},
|
||||
{"tsw550", (id, controlSystem, projectName) => new Tsw550(id, controlSystem)},
|
||||
{"tsw552", (id, controlSystem, projectName) => new Tsw552(id, controlSystem)},
|
||||
{"tsw560", (id, controlSystem, projectName) => new Tsw560(id, controlSystem)},
|
||||
{"tsw750", (id, controlSystem, projectName) => new Tsw750(id, controlSystem)},
|
||||
{"tsw752", (id, controlSystem, projectName) => new Tsw752(id, controlSystem)},
|
||||
{"tsw760", (id, controlSystem, projectName) => new Tsw760(id, controlSystem)},
|
||||
{"tsw1050", (id, controlSystem, projectName) => new Tsw1050(id, controlSystem)},
|
||||
{"tsw1052", (id, controlSystem, projectName) => new Tsw1052(id, controlSystem)},
|
||||
{"tsw1060", (id, controlSystem, projectName) => new Tsw1060(id, controlSystem)},
|
||||
{"tsw570", (id, controlSystem, projectName) => new Tsw570(id, controlSystem)},
|
||||
{"tsw770", (id, controlSystem, projectName) => new Tsw770(id, controlSystem)},
|
||||
{"ts770", (id, controlSystem, projectName) => new Ts770(id, controlSystem)},
|
||||
{"tsw1070", (id, controlSystem, projectName) => new Tsw1070(id, controlSystem)},
|
||||
{"ts1070", (id, controlSystem, projectName) => new Ts1070(id, controlSystem)},
|
||||
{"dge1000", (id, controlSystem, projectName) => new Dge1000(id, controlSystem)}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -686,10 +786,10 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
|
||||
if (panel == null)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "Unable to create Touchpanel for type {0}. Touchpanel Controller WILL NOT function correctly", dc.Type);
|
||||
Debug.LogError("Unable to create Touchpanel for type {type}. Touchpanel Controller WILL NOT function correctly", dc.Type);
|
||||
}
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Factory Attempting to create new MobileControlTouchpanelController");
|
||||
Debug.LogDebug("Factory Attempting to create new MobileControlTouchpanelController");
|
||||
|
||||
var panelController = new MobileControlTouchpanelController(dc.Key, dc.Name, panel, props);
|
||||
|
||||
@@ -699,56 +799,21 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
private BasicTriListWithSmartObject GetPanelForType(string type, uint id, string projectName)
|
||||
{
|
||||
type = type.ToLower().Replace("mc", "");
|
||||
|
||||
try
|
||||
{
|
||||
if (type == "crestronapp")
|
||||
if (!factories.TryGetValue(type, out var buildCrestronHardwareDevice))
|
||||
{
|
||||
var app = new CrestronApp(id, Global.ControlSystem);
|
||||
app.ParameterProjectName.Value = projectName;
|
||||
return app;
|
||||
}
|
||||
else if (type == "xpanel")
|
||||
return new XpanelForHtml5(id, Global.ControlSystem);
|
||||
else if (type == "tsw550")
|
||||
return new Tsw550(id, Global.ControlSystem);
|
||||
else if (type == "tsw552")
|
||||
return new Tsw552(id, Global.ControlSystem);
|
||||
else if (type == "tsw560")
|
||||
return new Tsw560(id, Global.ControlSystem);
|
||||
else if (type == "tsw750")
|
||||
return new Tsw750(id, Global.ControlSystem);
|
||||
else if (type == "tsw752")
|
||||
return new Tsw752(id, Global.ControlSystem);
|
||||
else if (type == "tsw760")
|
||||
return new Tsw760(id, Global.ControlSystem);
|
||||
else if (type == "tsw1050")
|
||||
return new Tsw1050(id, Global.ControlSystem);
|
||||
else if (type == "tsw1052")
|
||||
return new Tsw1052(id, Global.ControlSystem);
|
||||
else if (type == "tsw1060")
|
||||
return new Tsw1060(id, Global.ControlSystem);
|
||||
else if (type == "tsw570")
|
||||
return new Tsw570(id, Global.ControlSystem);
|
||||
else if (type == "tsw770")
|
||||
return new Tsw770(id, Global.ControlSystem);
|
||||
else if (type == "ts770")
|
||||
return new Ts770(id, Global.ControlSystem);
|
||||
else if (type == "tsw1070")
|
||||
return new Tsw1070(id, Global.ControlSystem);
|
||||
else if (type == "ts1070")
|
||||
return new Ts1070(id, Global.ControlSystem);
|
||||
else if (type == "dge1000")
|
||||
return new Dge1000(id, Global.ControlSystem);
|
||||
else
|
||||
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "WARNING: Cannot create TSW controller with type '{0}'", type);
|
||||
Debug.LogError("Cannot create TSW controller with type {type}", type);
|
||||
return null;
|
||||
}
|
||||
|
||||
return buildCrestronHardwareDevice(id, Global.ControlSystem, projectName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "WARNING: Cannot create TSW base class. Panel will not function: {0}", e.Message);
|
||||
Debug.LogError("Cannot create TSW base class. Panel will not function: {message}", e.Message);
|
||||
Debug.LogDebug(e, "Stack Trace: ");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,28 +8,32 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
/// </summary>
|
||||
public class MobileControlTouchpanelProperties : CrestronTouchpanelPropertiesConfig
|
||||
{
|
||||
[JsonProperty("useDirectServer")]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the UseDirectServer
|
||||
/// </summary>
|
||||
[JsonProperty("useDirectServer")]
|
||||
public bool UseDirectServer { get; set; } = false;
|
||||
|
||||
[JsonProperty("zoomRoomController")]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ZoomRoomController
|
||||
/// </summary>
|
||||
[JsonProperty("zoomRoomController")]
|
||||
public bool ZoomRoomController { get; set; } = false;
|
||||
|
||||
[JsonProperty("buttonToolbarTimeoutInS")]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ButtonToolbarTimoutInS
|
||||
/// </summary>
|
||||
[JsonProperty("buttonToolbarTimeoutInS")]
|
||||
public ushort ButtonToolbarTimoutInS { get; set; } = 0;
|
||||
|
||||
[JsonProperty("theme")]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Theme
|
||||
/// </summary>
|
||||
[JsonProperty("theme")]
|
||||
public string Theme { get; set; } = "light";
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,46 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Logging;
|
||||
using PepperDash.Essentials.AppServer;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
|
||||
namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a ThemeMessenger
|
||||
/// Messenger to save the current theme (light/dark) and send to a device
|
||||
/// </summary>
|
||||
public class ThemeMessenger : MessengerBase
|
||||
{
|
||||
private readonly ITheme _tpDevice;
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="ThemeMessenger"/> class
|
||||
/// </summary>
|
||||
/// <param name="key">The key for this messenger</param>
|
||||
/// <param name="path">The path for this messenger</param>
|
||||
/// <param name="device">The device for this messenger</param>
|
||||
public ThemeMessenger(string key, string path, ITheme device) : base(key, path, device as Device)
|
||||
{
|
||||
_tpDevice = device;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RegisterActions()
|
||||
{
|
||||
AddAction("/fullStatus", (id, content) =>
|
||||
{
|
||||
PostStatusMessage(new ThemeUpdateMessage { Theme = _tpDevice.Theme });
|
||||
PostStatusMessage(new ThemeUpdateMessage { Theme = _tpDevice.Theme }, id);
|
||||
});
|
||||
|
||||
AddAction("/saveTheme", (id, content) =>
|
||||
{
|
||||
var theme = content.ToObject<MobileControlSimpleContent<string>>();
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "Setting theme to {theme}", this, theme.Value);
|
||||
this.LogInformation("Setting theme to {theme}", theme.Value);
|
||||
_tpDevice.UpdateTheme(theme.Value);
|
||||
|
||||
PostStatusMessage(JToken.FromObject(new { theme = theme.Value }));
|
||||
PostStatusMessage(JToken.FromObject(new { theme = theme.Value }), clientId: id);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -42,10 +50,11 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
/// </summary>
|
||||
public class ThemeUpdateMessage : DeviceStateMessageBase
|
||||
{
|
||||
[JsonProperty("theme")]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Theme
|
||||
/// </summary>
|
||||
[JsonProperty("theme")]
|
||||
public string Theme { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Logging;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
using PepperDash.Essentials.Core.Queues;
|
||||
using PepperDash.Essentials.WebSocketServer;
|
||||
using Serilog.Events;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using WebSocketSharp;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
@@ -20,12 +16,22 @@ namespace PepperDash.Essentials
|
||||
private readonly WebSocket _ws;
|
||||
private readonly object msgToSend;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a message to send
|
||||
/// </summary>
|
||||
/// <param name="msg">message object to send</param>
|
||||
/// <param name="ws">WebSocket instance</param>
|
||||
public TransmitMessage(object msg, WebSocket ws)
|
||||
{
|
||||
_ws = ws;
|
||||
msgToSend = msg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a message to send
|
||||
/// </summary>
|
||||
/// <param name="msg">message object to send</param>
|
||||
/// <param name="ws">WebSocket instance</param>
|
||||
public TransmitMessage(DeviceStateMessageBase msg, WebSocket ws)
|
||||
{
|
||||
_ws = ws;
|
||||
@@ -43,13 +49,13 @@ namespace PepperDash.Essentials
|
||||
{
|
||||
if (_ws == null)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Warning, "Cannot send message. Websocket client is null");
|
||||
Debug.LogWarning("Cannot send message. Websocket client is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_ws.IsAlive)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Warning, "Cannot send message. Websocket client is not connected");
|
||||
Debug.LogWarning("Cannot send message. Websocket client is not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -57,83 +63,14 @@ namespace PepperDash.Essentials
|
||||
var message = JsonConvert.SerializeObject(msgToSend, Formatting.None,
|
||||
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Converters = { new IsoDateTimeConverter() } });
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Message TX: {0}", null, message);
|
||||
Debug.LogVerbose("Message TX: {0}", message);
|
||||
|
||||
_ws.Send(message);
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Caught an exception in the Transmit Processor");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a MessageToClients
|
||||
/// </summary>
|
||||
public class MessageToClients : IQueueMessage
|
||||
{
|
||||
private readonly MobileControlWebsocketServer _server;
|
||||
private readonly object msgToSend;
|
||||
|
||||
public MessageToClients(object msg, MobileControlWebsocketServer server)
|
||||
{
|
||||
_server = server;
|
||||
msgToSend = msg;
|
||||
}
|
||||
|
||||
public MessageToClients(DeviceStateMessageBase msg, MobileControlWebsocketServer server)
|
||||
{
|
||||
_server = server;
|
||||
msgToSend = msg;
|
||||
}
|
||||
|
||||
#region Implementation of IQueueMessage
|
||||
|
||||
/// <summary>
|
||||
/// Dispatch method
|
||||
/// </summary>
|
||||
public void Dispatch()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_server == null)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Warning, "Cannot send message. Server is null");
|
||||
return;
|
||||
}
|
||||
|
||||
var message = JsonConvert.SerializeObject(msgToSend, Formatting.None,
|
||||
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Converters = { new IsoDateTimeConverter() } });
|
||||
|
||||
var clientSpecificMessage = msgToSend as MobileControlMessage;
|
||||
if (clientSpecificMessage.ClientId != null)
|
||||
{
|
||||
var clientId = clientSpecificMessage.ClientId;
|
||||
|
||||
_server.LogVerbose("Message TX To client {clientId} Message: {message}", clientId, message);
|
||||
|
||||
_server.SendMessageToClient(clientId, message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_server.SendMessageToAllClients(message);
|
||||
|
||||
_server.LogVerbose("Message TX To all clients: {message}", message);
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
//Swallowing this exception, as it occurs on shutdown and there's no need to print out a scary stack trace
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Caught an exception in the Transmit Processor");
|
||||
Debug.LogError("Caught an exception in the Transmit Processor: {message}", ex.Message);
|
||||
Debug.LogDebug(ex, "Stack Trace: ");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
24
src/PepperDash.Essentials.MobileControl/UserCodeChanged.cs
Normal file
24
src/PepperDash.Essentials.MobileControl/UserCodeChanged.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the action to take when the User code changes
|
||||
/// </summary>
|
||||
public class UserCodeChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Action to take when the User Code changes
|
||||
/// </summary>
|
||||
public Action<string, string> UpdateUserCode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// create an instance of the <see cref="UserCodeChanged"/> class
|
||||
/// </summary>
|
||||
/// <param name="updateMethod">action to take when the User Code changes</param>
|
||||
public UserCodeChanged(Action<string, string> updateMethod)
|
||||
{
|
||||
UpdateUserCode = updateMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,16 +7,18 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
public class UserCodeChangedContent
|
||||
{
|
||||
[JsonProperty("userCode")]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the UserCode
|
||||
/// </summary>
|
||||
[JsonProperty("userCode")]
|
||||
public string UserCode { get; set; }
|
||||
|
||||
[JsonProperty("qrChecksum", NullValueHandling = NullValueHandling.Include)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the QrChecksum
|
||||
/// </summary>
|
||||
[JsonProperty("qrChecksum", NullValueHandling = NullValueHandling.Include)]
|
||||
public string QrChecksum { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
97
src/PepperDash.Essentials.MobileControl/Utilities.cs
Normal file
97
src/PepperDash.Essentials.MobileControl/Utilities.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Logging;
|
||||
using WebSocketSharp;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility functions for logging and other common tasks.
|
||||
/// </summary>
|
||||
public static class Utilities
|
||||
{
|
||||
private static int nextClientId = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Get the next unique client ID
|
||||
/// </summary>
|
||||
/// <returns>Client ID</returns>
|
||||
public static int GetNextClientId()
|
||||
{
|
||||
nextClientId++;
|
||||
return nextClientId;
|
||||
}
|
||||
/// <summary>
|
||||
/// Converts a WebSocketServer LogData object to Essentials logging calls.
|
||||
/// </summary>
|
||||
/// <param name="data">The LogData object to convert.</param>
|
||||
/// <param name="message">The log message.</param>
|
||||
/// <param name="device">The device associated with the log message.</param>
|
||||
public static void ConvertWebsocketLog(LogData data, string message, IKeyed device = null)
|
||||
{
|
||||
|
||||
switch (data.Level)
|
||||
{
|
||||
case LogLevel.Trace:
|
||||
if (device == null)
|
||||
{
|
||||
Debug.LogVerbose(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
device.LogVerbose(message);
|
||||
}
|
||||
break;
|
||||
case LogLevel.Debug:
|
||||
if (device == null)
|
||||
{
|
||||
Debug.LogDebug(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
device.LogDebug(message);
|
||||
}
|
||||
break;
|
||||
case LogLevel.Info:
|
||||
if (device == null)
|
||||
{
|
||||
Debug.LogInformation(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
device.LogInformation(message);
|
||||
}
|
||||
break;
|
||||
case LogLevel.Warn:
|
||||
if (device == null)
|
||||
{
|
||||
Debug.LogWarning(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
device.LogWarning(message);
|
||||
}
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
if (device == null)
|
||||
{
|
||||
Debug.LogError(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
device.LogError(message);
|
||||
}
|
||||
break;
|
||||
case LogLevel.Fatal:
|
||||
if (device == null)
|
||||
{
|
||||
Debug.LogFatal(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
device.LogFatal(message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
@@ -8,21 +8,24 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
public class Volumes
|
||||
{
|
||||
[JsonProperty("master", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Master
|
||||
/// </summary>
|
||||
[JsonProperty("master", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Volume Master { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Aux Faders as configured in the room
|
||||
/// </summary>
|
||||
[JsonProperty("auxFaders", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Dictionary<string, Volume> AuxFaders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Count of aux faders for this system
|
||||
/// </summary>
|
||||
[JsonProperty("numberOfAuxFaders", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public int? NumberOfAuxFaders { get; set; }
|
||||
|
||||
public Volumes()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -30,40 +33,66 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
public class Volume
|
||||
{
|
||||
[JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)]
|
||||
/// <summary>
|
||||
/// Gets or sets the Key
|
||||
/// </summary>
|
||||
[JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Level for this volume object
|
||||
/// </summary>
|
||||
[JsonProperty("level", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public int? Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if this volume control is muted
|
||||
/// </summary>
|
||||
[JsonProperty("muted", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? Muted { get; set; }
|
||||
|
||||
[JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Label
|
||||
/// </summary>
|
||||
[JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Label { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if this volume object has mute control
|
||||
/// </summary>
|
||||
[JsonProperty("hasMute", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasMute { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if this volume object has Privacy mute control
|
||||
/// </summary>
|
||||
[JsonProperty("hasPrivacyMute", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasPrivacyMute { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the privacy mute is muted
|
||||
/// </summary>
|
||||
[JsonProperty("privacyMuted", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? PrivacyMuted { get; set; }
|
||||
|
||||
|
||||
[JsonProperty("muteIcon", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the MuteIcon
|
||||
/// </summary>
|
||||
[JsonProperty("muteIcon", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string MuteIcon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="Volume" /> class
|
||||
/// </summary>
|
||||
/// <param name="key">The key for this volume object</param>
|
||||
/// <param name="level">The level for this volume object</param>
|
||||
/// <param name="muted">True if this volume control is muted</param>
|
||||
/// <param name="label">The label for this volume object</param>
|
||||
/// <param name="hasMute">True if this volume object has mute control</param>
|
||||
/// <param name="muteIcon">The mute icon for this volume object</param>
|
||||
public Volume(string key, int level, bool muted, string label, bool hasMute, string muteIcon)
|
||||
: this(key)
|
||||
{
|
||||
@@ -74,18 +103,32 @@ namespace PepperDash.Essentials
|
||||
MuteIcon = muteIcon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="Volume" /> class
|
||||
/// </summary>
|
||||
/// <param name="key">The key for this volume object</param>
|
||||
/// <param name="level">The level for this volume object</param>
|
||||
public Volume(string key, int level)
|
||||
: this(key)
|
||||
{
|
||||
Level = level;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="Volume" /> class
|
||||
/// </summary>
|
||||
/// <param name="key">The key for this volume object</param>
|
||||
/// <param name="muted">True if this volume control is muted</param>
|
||||
public Volume(string key, bool muted)
|
||||
: this(key)
|
||||
{
|
||||
Muted = muted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="Volume" /> class
|
||||
/// </summary>
|
||||
/// <param name="key">The key for this volume object</param>
|
||||
public Volume(string key)
|
||||
{
|
||||
Key = key;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core.Web.RequestHandlers;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PepperDash.Essentials.WebApiHandlers
|
||||
{
|
||||
@@ -12,11 +12,20 @@ namespace PepperDash.Essentials.WebApiHandlers
|
||||
public class ActionPathsHandler : WebApiBaseRequestHandler
|
||||
{
|
||||
private readonly MobileControlSystemController mcController;
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="ActionPathsHandler"/> class.
|
||||
/// </summary>
|
||||
/// <param name="controller"></param>
|
||||
public ActionPathsHandler(MobileControlSystemController controller) : base(true)
|
||||
{
|
||||
mcController = controller;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a request to get the action paths
|
||||
/// </summary>
|
||||
/// <param name="context">Request Context</param>
|
||||
protected override void HandleGet(HttpCwsContext context)
|
||||
{
|
||||
var response = JsonConvert.SerializeObject(new ActionPathsResponse(mcController));
|
||||
@@ -37,9 +46,16 @@ namespace PepperDash.Essentials.WebApiHandlers
|
||||
[JsonIgnore]
|
||||
private readonly MobileControlSystemController mcController;
|
||||
|
||||
/// <summary>
|
||||
/// Registered action paths for this system
|
||||
/// </summary>
|
||||
[JsonProperty("actionPaths")]
|
||||
public List<ActionPath> ActionPaths => mcController.GetActionDictionaryPaths().Select((path) => new ActionPath { MessengerKey = path.Item1, Path = path.Item2 }).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="ActionPathsResponse"/> class.
|
||||
/// </summary>
|
||||
/// <param name="mcController"></param>
|
||||
public ActionPathsResponse(MobileControlSystemController mcController)
|
||||
{
|
||||
this.mcController = mcController;
|
||||
@@ -51,16 +67,18 @@ namespace PepperDash.Essentials.WebApiHandlers
|
||||
/// </summary>
|
||||
public class ActionPath
|
||||
{
|
||||
[JsonProperty("messengerKey")]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the MessengerKey
|
||||
/// </summary>
|
||||
[JsonProperty("messengerKey")]
|
||||
public string MessengerKey { get; set; }
|
||||
|
||||
[JsonProperty("path")]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Path
|
||||
/// </summary>
|
||||
[JsonProperty("path")]
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Web.RequestHandlers;
|
||||
using PepperDash.Essentials.Core.Web;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Essentials.WebApiHandlers
|
||||
{
|
||||
@@ -15,11 +15,20 @@ namespace PepperDash.Essentials.WebApiHandlers
|
||||
{
|
||||
private readonly MobileControlSystemController mcController;
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="MobileAuthRequestHandler"/> class.
|
||||
/// </summary>
|
||||
/// <param name="controller"></param>
|
||||
public MobileAuthRequestHandler(MobileControlSystemController controller) : base(true)
|
||||
{
|
||||
mcController = controller;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle authorization request for this processor
|
||||
/// </summary>
|
||||
/// <param name="context">request context</param>
|
||||
/// <returns>Task</returns>
|
||||
protected override async Task HandlePost(HttpCwsContext context)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Web.RequestHandlers;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using PepperDash.Essentials.WebSocketServer;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PepperDash.Essentials.WebApiHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a MobileInfoHandler
|
||||
/// Represents a MobileInfoHandler. Used with the Essentials CWS API
|
||||
/// </summary>
|
||||
public class MobileInfoHandler : WebApiBaseRequestHandler
|
||||
{
|
||||
private readonly MobileControlSystemController mcController;
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="MobileInfoHandler"/> class.
|
||||
/// </summary>
|
||||
/// <param name="controller"></param>
|
||||
public MobileInfoHandler(MobileControlSystemController controller) : base(true)
|
||||
{
|
||||
mcController = controller;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Mobile Control Information
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
protected override void HandleGet(HttpCwsContext context)
|
||||
{
|
||||
try
|
||||
@@ -50,14 +59,22 @@ namespace PepperDash.Essentials.WebApiHandlers
|
||||
[JsonIgnore]
|
||||
private readonly MobileControlSystemController mcController;
|
||||
|
||||
/// <summary>
|
||||
/// Edge Server. Null if edge server is disabled
|
||||
/// </summary>
|
||||
[JsonProperty("edgeServer", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public MobileControlEdgeServer EdgeServer => mcController.Config.EnableApiServer ? new MobileControlEdgeServer(mcController) : null;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Direct server. Null if the direct server is disabled
|
||||
/// </summary>
|
||||
[JsonProperty("directServer", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public MobileControlDirectServer DirectServer => mcController.Config.DirectServer.EnableDirectServer ? new MobileControlDirectServer(mcController.DirectServer) : null;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="InformationResponse"/> class.
|
||||
/// </summary>
|
||||
/// <param name="controller"></param>
|
||||
public InformationResponse(MobileControlSystemController controller)
|
||||
{
|
||||
mcController = controller;
|
||||
@@ -72,24 +89,46 @@ namespace PepperDash.Essentials.WebApiHandlers
|
||||
[JsonIgnore]
|
||||
private readonly MobileControlSystemController mcController;
|
||||
|
||||
/// <summary>
|
||||
/// Mobile Control Edge Server address for this system
|
||||
/// </summary>
|
||||
[JsonProperty("serverAddress")]
|
||||
public string ServerAddress => mcController.Config == null ? "No Config" : mcController.Host;
|
||||
|
||||
/// <summary>
|
||||
/// System Name for this system
|
||||
/// </summary>
|
||||
[JsonProperty("systemName")]
|
||||
public string SystemName => mcController.RoomBridges.Count > 0 ? mcController.RoomBridges[0].RoomName : "No Config";
|
||||
|
||||
/// <summary>
|
||||
/// System URL for this system
|
||||
/// </summary>
|
||||
[JsonProperty("systemUrl")]
|
||||
public string SystemUrl => ConfigReader.ConfigObject.SystemUrl;
|
||||
|
||||
/// <summary>
|
||||
/// User code to use in MC UI for this system
|
||||
/// </summary>
|
||||
[JsonProperty("userCode")]
|
||||
public string UserCode => mcController.RoomBridges.Count > 0 ? mcController.RoomBridges[0].UserCode : "Not available";
|
||||
|
||||
/// <summary>
|
||||
/// True if connected to edge server
|
||||
/// </summary>
|
||||
[JsonProperty("connected")]
|
||||
public bool Connected => mcController.Connected;
|
||||
|
||||
/// <summary>
|
||||
/// Seconds since last comms with edge server
|
||||
/// </summary>
|
||||
[JsonProperty("secondsSinceLastAck")]
|
||||
public int SecondsSinceLastAck => (DateTime.Now - mcController.LastAckMessage).Seconds;
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="MobileControlEdgeServer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="controller">controller to use for this</param>
|
||||
public MobileControlEdgeServer(MobileControlSystemController controller)
|
||||
{
|
||||
mcController = controller;
|
||||
@@ -104,21 +143,43 @@ namespace PepperDash.Essentials.WebApiHandlers
|
||||
[JsonIgnore]
|
||||
private readonly MobileControlWebsocketServer directServer;
|
||||
|
||||
/// <summary>
|
||||
/// URL to use to interact with this server
|
||||
/// </summary>
|
||||
[JsonProperty("userAppUrl")]
|
||||
public string UserAppUrl => $"{directServer.UserAppUrlPrefix}/[insert_client_token]";
|
||||
|
||||
/// <summary>
|
||||
/// TCP/IP Port this server is configured to use
|
||||
/// </summary>
|
||||
[JsonProperty("serverPort")]
|
||||
public int ServerPort => directServer.Port;
|
||||
|
||||
/// <summary>
|
||||
/// Count of defined tokens for this server
|
||||
/// </summary>
|
||||
[JsonProperty("tokensDefined")]
|
||||
public int TokensDefined => directServer.UiClients.Count;
|
||||
public int TokensDefined => directServer.UiClientContexts.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Count of connected clients
|
||||
/// </summary>
|
||||
[JsonProperty("clientsConnected")]
|
||||
public int ClientsConnected => directServer.ConnectedUiClientsCount;
|
||||
|
||||
/// <summary>
|
||||
/// List of tokens and connected clients for this server
|
||||
/// </summary>
|
||||
[JsonProperty("clients")]
|
||||
public List<MobileControlDirectClient> Clients => directServer.UiClients.Select((c, i) => { return new MobileControlDirectClient(c, i, directServer.UserAppUrlPrefix); }).ToList();
|
||||
public List<MobileControlDirectClient> Clients => directServer.UiClientContexts
|
||||
.Select(context => (context, clients: directServer.UiClients.Where(client => client.Value.Token == context.Value.Token.Token).Select(c => c.Value).ToList()))
|
||||
.Select((clientTuple, i) => new MobileControlDirectClient(clientTuple.clients, clientTuple.context, i, directServer.UserAppUrlPrefix))
|
||||
.ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="MobileControlDirectServer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="server"></param>
|
||||
public MobileControlDirectServer(MobileControlWebsocketServer server)
|
||||
{
|
||||
directServer = server;
|
||||
@@ -142,33 +203,85 @@ namespace PepperDash.Essentials.WebApiHandlers
|
||||
[JsonIgnore]
|
||||
private readonly string urlPrefix;
|
||||
|
||||
/// <summary>
|
||||
/// Client number for this client
|
||||
/// </summary>
|
||||
[JsonProperty("clientNumber")]
|
||||
public string ClientNumber => $"{clientNumber}";
|
||||
|
||||
/// <summary>
|
||||
/// Room Key for this client
|
||||
/// </summary>
|
||||
[JsonProperty("roomKey")]
|
||||
public string RoomKey => context.Token.RoomKey;
|
||||
|
||||
/// <summary>
|
||||
/// Touchpanel Key, if defined, for this client
|
||||
/// </summary>
|
||||
[JsonProperty("touchpanelKey")]
|
||||
public string TouchpanelKey => context.Token.TouchpanelKey;
|
||||
|
||||
/// <summary>
|
||||
/// URL for this client
|
||||
/// </summary>
|
||||
[JsonProperty("url")]
|
||||
public string Url => $"{urlPrefix}{Key}";
|
||||
|
||||
/// <summary>
|
||||
/// Token for this client
|
||||
/// </summary>
|
||||
[JsonProperty("token")]
|
||||
public string Token => Key;
|
||||
|
||||
[JsonProperty("connected")]
|
||||
public bool Connected => context.Client != null && context.Client.Context.WebSocket.IsAlive;
|
||||
private readonly List<UiClient> clients;
|
||||
|
||||
[JsonProperty("duration")]
|
||||
public double Duration => context.Client == null ? 0 : context.Client.ConnectedDuration.TotalSeconds;
|
||||
/// <summary>
|
||||
/// List of status for all connected UI Clients
|
||||
/// </summary>
|
||||
[JsonProperty("clientStatus")]
|
||||
public List<ClientStatus> ClientStatus => clients.Select(c => new ClientStatus(c)).ToList();
|
||||
|
||||
public MobileControlDirectClient(KeyValuePair<string, UiClientContext> clientContext, int index, string urlPrefix)
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="MobileControlDirectClient"/> class.
|
||||
/// </summary>
|
||||
/// <param name="clients">List of Websocket Clients</param>
|
||||
/// <param name="context">Context for the client</param>
|
||||
/// <param name="index">Index of the client</param>
|
||||
/// <param name="urlPrefix">URL prefix for the client</param>
|
||||
public MobileControlDirectClient(List<UiClient> clients, KeyValuePair<string, UiClientContext> context, int index, string urlPrefix)
|
||||
{
|
||||
context = clientContext.Value;
|
||||
Key = clientContext.Key;
|
||||
this.context = context.Value;
|
||||
Key = context.Key;
|
||||
clientNumber = index;
|
||||
this.urlPrefix = urlPrefix;
|
||||
this.clients = clients;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Report the status of a UiClient
|
||||
/// </summary>
|
||||
public class ClientStatus
|
||||
{
|
||||
private readonly UiClient client;
|
||||
|
||||
/// <summary>
|
||||
/// True if client is connected
|
||||
/// </summary>
|
||||
public bool Connected => client != null && client.Context.WebSocket.IsAlive;
|
||||
|
||||
/// <summary>
|
||||
/// Get the time this client has been connected
|
||||
/// </summary>
|
||||
public double Duration => client == null ? 0 : client.ConnectedDuration.TotalSeconds;
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="ClientStatus"/> class for the specified client
|
||||
/// </summary>
|
||||
/// <param name="client">client to report on</param>
|
||||
public ClientStatus(UiClient client)
|
||||
{
|
||||
this.client = client;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,20 @@ namespace PepperDash.Essentials.WebApiHandlers
|
||||
public class UiClientHandler : WebApiBaseRequestHandler
|
||||
{
|
||||
private readonly MobileControlWebsocketServer server;
|
||||
|
||||
/// <summary>
|
||||
/// Essentials CWS API handler for the MC Direct Server
|
||||
/// </summary>
|
||||
/// <param name="directServer">Direct Server instance</param>
|
||||
public UiClientHandler(MobileControlWebsocketServer directServer) : base(true)
|
||||
{
|
||||
server = directServer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a client for the Direct Server
|
||||
/// </summary>
|
||||
/// <param name="context">HTTP Context for this request</param>
|
||||
protected override void HandlePost(HttpCwsContext context)
|
||||
{
|
||||
var req = context.Request;
|
||||
@@ -65,6 +74,10 @@ namespace PepperDash.Essentials.WebApiHandlers
|
||||
res.End();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle DELETE request for a Client
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
protected override void HandleDelete(HttpCwsContext context)
|
||||
{
|
||||
var req = context.Request;
|
||||
@@ -93,7 +106,7 @@ namespace PepperDash.Essentials.WebApiHandlers
|
||||
|
||||
|
||||
|
||||
if (!server.UiClients.TryGetValue(request.Token, out UiClientContext clientContext))
|
||||
if (!server.UiClientContexts.TryGetValue(request.Token, out UiClientContext clientContext))
|
||||
{
|
||||
var response = new ClientResponse
|
||||
{
|
||||
@@ -134,7 +147,7 @@ namespace PepperDash.Essentials.WebApiHandlers
|
||||
return;
|
||||
}
|
||||
|
||||
server.UiClients.Remove(request.Token);
|
||||
server.UiClientContexts.Remove(request.Token);
|
||||
|
||||
server.UpdateSecret();
|
||||
|
||||
@@ -148,22 +161,25 @@ namespace PepperDash.Essentials.WebApiHandlers
|
||||
/// </summary>
|
||||
public class ClientRequest
|
||||
{
|
||||
[JsonProperty("roomKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the RoomKey
|
||||
/// </summary>
|
||||
[JsonProperty("roomKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string RoomKey { get; set; }
|
||||
|
||||
[JsonProperty("grantCode", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the GrantCode
|
||||
/// </summary>
|
||||
[JsonProperty("grantCode", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string GrantCode { get; set; }
|
||||
|
||||
[JsonProperty("token", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Token
|
||||
/// </summary>
|
||||
[JsonProperty("token", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Token { get; set; }
|
||||
}
|
||||
|
||||
@@ -172,22 +188,25 @@ namespace PepperDash.Essentials.WebApiHandlers
|
||||
/// </summary>
|
||||
public class ClientResponse
|
||||
{
|
||||
[JsonProperty("error", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Error
|
||||
/// </summary>
|
||||
[JsonProperty("error", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Error { get; set; }
|
||||
|
||||
[JsonProperty("token", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Token
|
||||
/// </summary>
|
||||
[JsonProperty("token", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Token { get; set; }
|
||||
|
||||
[JsonProperty("path", NullValueHandling = NullValueHandling.Ignore)]
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Path
|
||||
/// </summary>
|
||||
[JsonProperty("path", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Event Args for <see cref="UiClient"/> ConnectionClosed event
|
||||
/// </summary>
|
||||
public class ConnectionClosedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Client ID that is being closed
|
||||
/// </summary>
|
||||
public string ClientId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize an instance of the <see cref="ConnectionClosedEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="clientId">client that's closing</param>
|
||||
public ConnectionClosedEventArgs(string clientId)
|
||||
{
|
||||
ClientId = clientId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents info about a device including supproted interfaces
|
||||
/// </summary>
|
||||
public class DeviceInterfaceInfo : IKeyName
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the Key
|
||||
/// </summary>
|
||||
[JsonProperty("key")]
|
||||
public string Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Name
|
||||
/// </summary>
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Interfaces
|
||||
/// </summary>
|
||||
[JsonProperty("interfaces")]
|
||||
public List<string> Interfaces { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a JoinResponse
|
||||
/// </summary>
|
||||
public class JoinResponse
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ClientId
|
||||
/// </summary>
|
||||
[JsonProperty("clientId")]
|
||||
public string ClientId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Room Key for this client
|
||||
/// </summary>
|
||||
[JsonProperty("roomKey")]
|
||||
public string RoomKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// System UUID for this system
|
||||
/// </summary>
|
||||
[JsonProperty("systemUUid")]
|
||||
public string SystemUuid { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the RoomUuid
|
||||
/// </summary>
|
||||
[JsonProperty("roomUUid")]
|
||||
public string RoomUuid { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Config
|
||||
/// </summary>
|
||||
[JsonProperty("config")]
|
||||
public object Config { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the CodeExpires
|
||||
/// </summary>
|
||||
[JsonProperty("codeExpires")]
|
||||
public DateTime CodeExpires { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the UserCode
|
||||
/// </summary>
|
||||
[JsonProperty("userCode")]
|
||||
public string UserCode { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the UserAppUrl
|
||||
/// </summary>
|
||||
[JsonProperty("userAppUrl")]
|
||||
public string UserAppUrl { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the EnableDebug
|
||||
/// </summary>
|
||||
[JsonProperty("enableDebug")]
|
||||
public bool EnableDebug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DeviceInterfaceSupport
|
||||
/// </summary>
|
||||
[JsonProperty("deviceInterfaceSupport")]
|
||||
public Dictionary<string, DeviceInterfaceInfo> DeviceInterfaceSupport { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a JoinToken
|
||||
/// </summary>
|
||||
public class JoinToken
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique client ID for a client that is joining
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the Code
|
||||
/// </summary>
|
||||
public string Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Room Key this token is associated with
|
||||
/// </summary>
|
||||
public string RoomKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unique ID for this token
|
||||
/// </summary>
|
||||
public string Uuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Touchpanel Key this token is associated with, if this is a touch panel token
|
||||
/// </summary>
|
||||
public string TouchpanelKey { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Token
|
||||
/// </summary>
|
||||
public string Token { get; set; } = null;
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,13 @@ using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using Newtonsoft.Json;
|
||||
using Org.BouncyCastle.Crypto.Prng;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Logging;
|
||||
using PepperDash.Essentials.Core;
|
||||
@@ -35,6 +38,10 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
private readonly string appConfigFileName = "_config.local.json";
|
||||
private readonly string appConfigCsFileName = "_config.cs.json";
|
||||
|
||||
private const string certificateName = "selfCres";
|
||||
|
||||
private const string certificatePassword = "cres12345";
|
||||
|
||||
/// <summary>
|
||||
/// Where the key is the join token and the value is the room key
|
||||
/// </summary>
|
||||
@@ -50,7 +57,14 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
/// <summary>
|
||||
/// Gets the collection of UI client contexts
|
||||
/// </summary>
|
||||
public Dictionary<string, UiClientContext> UiClients { get; private set; }
|
||||
public Dictionary<string, UiClientContext> UiClientContexts { get; private set; }
|
||||
|
||||
private readonly Dictionary<string, UiClient> uiClients = new Dictionary<string, UiClient>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of UI clients
|
||||
/// </summary>
|
||||
public ReadOnlyDictionary<string, UiClient> UiClients => new ReadOnlyDictionary<string, UiClient>(uiClients);
|
||||
|
||||
private readonly MobileControlSystemController _parent;
|
||||
|
||||
@@ -121,17 +135,7 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
get
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
foreach (var client in UiClients)
|
||||
{
|
||||
if (client.Value.Client != null && client.Value.Client.Context.WebSocket.IsAlive)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
return uiClients.Values.Where(c => c.Context.WebSocket.IsAlive).Count();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +159,7 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
try
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Automatically forwarding port {0} to CS LAN", Port);
|
||||
this.LogInformation("Automatically forwarding port {port} to CS LAN", Port);
|
||||
|
||||
var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);
|
||||
var csIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId);
|
||||
@@ -164,16 +168,17 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
|
||||
if (result != CrestronEthernetHelper.PortForwardingUserPatRetCodes.NoErr)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Error, "Error adding port forwarding: {0}", result);
|
||||
this.LogError("Error adding port forwarding: {error}", result);
|
||||
}
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "This processor does not have a CS LAN", this);
|
||||
this.LogInformation("This processor does not have a CS LAN", this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Error automatically forwarding port to CS LAN");
|
||||
this.LogError("Error automatically forwarding port to CS LAN: {message}", ex.Message);
|
||||
this.LogDebug(ex, "Stack Trace");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,12 +195,12 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
if (parent.Config.DirectServer.AutomaticallyForwardPortToCSLAN == false)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "This processor does not have a CS LAN", this);
|
||||
this.LogInformation("This processor does not have a CS LAN");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UiClients = new Dictionary<string, UiClientContext>();
|
||||
UiClientContexts = new Dictionary<string, UiClientContext>();
|
||||
|
||||
//_joinTokens = new Dictionary<string, JoinToken>();
|
||||
|
||||
@@ -259,13 +264,29 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
_server.OnPost += Server_OnPost;
|
||||
}
|
||||
|
||||
if (_parent.Config.DirectServer.Secure)
|
||||
{
|
||||
this.LogInformation("Adding SSL Configuration to server");
|
||||
_server.SslConfiguration = new ServerSslConfiguration(new X509Certificate2($"\\user\\{certificateName}.pfx", certificatePassword))
|
||||
{
|
||||
ClientCertificateRequired = false,
|
||||
CheckCertificateRevocation = false,
|
||||
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11
|
||||
};
|
||||
}
|
||||
|
||||
_server.Log.Output = (data, message) => Utilities.ConvertWebsocketLog(data, message, this);
|
||||
|
||||
// setting to trace to allow logging level to be controlled by appdebug
|
||||
_server.Log.Level = LogLevel.Trace;
|
||||
|
||||
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
|
||||
|
||||
_server.Start();
|
||||
|
||||
if (_server.IsListening)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Mobile Control WebSocket Server listening on port {port}", this, _server.Port);
|
||||
this.LogInformation("Mobile Control WebSocket Server listening on port {port}", _server.Port);
|
||||
}
|
||||
|
||||
CrestronEnvironment.ProgramStatusEventHandler += OnProgramStop;
|
||||
@@ -278,10 +299,20 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Exception intializing websocket server", this);
|
||||
this.LogError("Exception initializing direct server: {message}", ex.Message);
|
||||
this.LogDebug(ex, "Stack Trace");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the internal logging level for the Websocket Server
|
||||
/// </summary>
|
||||
public void SetWebsocketLogLevel(LogLevel level)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse($"Setting direct server debug level to {level}", level.ToString());
|
||||
_server.Log.Level = level;
|
||||
}
|
||||
|
||||
private void AddClientsForTouchpanels()
|
||||
{
|
||||
var touchpanels = DeviceManager.AllDevices
|
||||
@@ -347,22 +378,6 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
|
||||
string ip = processorIp;
|
||||
|
||||
// Moved to the MobileControlTouchpanelController class in the GetUrlWithCorrectIp method
|
||||
// triggered by the Panel.IpInformationChange event so that we know we have the necessary info
|
||||
// to make the determination of which IP to use.
|
||||
//if (touchpanel.Touchpanel is IMobileControlCrestronTouchpanelController crestronTouchpanel && csIpAddress != null)
|
||||
//{
|
||||
// ip = crestronTouchpanel.ConnectedIps.Any(ipInfo =>
|
||||
// {
|
||||
// if (System.Net.IPAddress.TryParse(ipInfo.DeviceIpAddress, out var parsedIp))
|
||||
// {
|
||||
// return csIpAddress.IsInSameSubnet(parsedIp, csSubnetMask);
|
||||
// }
|
||||
// this.LogWarning("Invalid IP address: {deviceIpAddress}", ipInfo.DeviceIpAddress);
|
||||
// return false;
|
||||
// }) ? csIpAddress.ToString() : processorIp;
|
||||
//}
|
||||
|
||||
if (_parent.Config.DirectServer.CSLanUiDeviceKeys != null && _parent.Config.DirectServer.CSLanUiDeviceKeys.Any(k => k.Equals(touchpanel.Touchpanel.Key, StringComparison.InvariantCultureIgnoreCase)) && csIpAddress != null)
|
||||
{
|
||||
ip = csIpAddress.ToString();
|
||||
@@ -370,9 +385,9 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
|
||||
var appUrl = $"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}";
|
||||
|
||||
this.LogVerbose("Sending URL {appUrl}", appUrl);
|
||||
this.LogVerbose("Sending URL {appUrl} to touchpanel {touchpanelKey}", appUrl, touchpanel.Touchpanel.Key);
|
||||
|
||||
touchpanel.Messenger.UpdateAppUrl($"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}");
|
||||
touchpanel.Touchpanel.SetAppUrl($"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,7 +492,8 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.LogError(ex, "Error getting application configuration");
|
||||
this.LogError("Error getting application configuration: {message}", ex.Message);
|
||||
this.LogDebug(ex, "Stack Trace");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -513,27 +529,26 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
if (token.Value == null)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Warning, "Token value is null", this);
|
||||
this.LogWarning("Token value is null");
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Information, "Adding token: {0} for room: {1}", this, token.Key, token.Value.RoomKey);
|
||||
this.LogInformation("Adding token: {key} for room: {roomKey}", token.Key, token.Value.RoomKey);
|
||||
|
||||
if (UiClients == null)
|
||||
if (UiClientContexts == null)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Warning, "UiClients is null", this);
|
||||
UiClients = new Dictionary<string, UiClientContext>();
|
||||
UiClientContexts = new Dictionary<string, UiClientContext>();
|
||||
}
|
||||
|
||||
UiClients.Add(token.Key, new UiClientContext(token.Value));
|
||||
UiClientContexts.Add(token.Key, new UiClientContext(token.Value));
|
||||
}
|
||||
}
|
||||
|
||||
if (UiClients.Count > 0)
|
||||
if (UiClientContexts.Count > 0)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Restored {uiClientCount} UiClients from secrets data", this, UiClients.Count);
|
||||
this.LogInformation("Restored {uiClientCount} UiClients from secrets data", UiClientContexts.Count);
|
||||
|
||||
foreach (var client in UiClients)
|
||||
foreach (var client in UiClientContexts)
|
||||
{
|
||||
var key = client.Key;
|
||||
var path = _wsPath + key;
|
||||
@@ -541,36 +556,23 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
|
||||
_server.AddWebSocketService(path, () =>
|
||||
{
|
||||
var c = new UiClient();
|
||||
Debug.LogMessage(LogEventLevel.Debug, "Constructing UiClient with id: {key}", this, key);
|
||||
|
||||
c.Controller = _parent;
|
||||
c.RoomKey = roomKey;
|
||||
UiClients[key].SetClient(c);
|
||||
return c;
|
||||
this.LogInformation("Building a UiClient with ID {id}", client.Value.Token.Id);
|
||||
return BuildUiClient(roomKey, client.Value.Token, key);
|
||||
});
|
||||
|
||||
|
||||
//_server.WebSocketServices.AddService<UiClient>(path, (c) =>
|
||||
//{
|
||||
// Debug.Console(2, this, "Constructing UiClient with id: {0}", key);
|
||||
// c.Controller = _parent;
|
||||
// c.RoomKey = roomKey;
|
||||
// UiClients[key].SetClient(c);
|
||||
//});
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Warning, "No secret found");
|
||||
this.LogWarning("No secret found");
|
||||
}
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Debug, "{uiClientCount} UiClients restored from secrets data", this, UiClients.Count);
|
||||
this.LogDebug("{uiClientCount} UiClients restored from secrets data", UiClientContexts.Count);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Exception retrieving secret", this);
|
||||
this.LogError("Exception retrieving secret: {message}", ex.Message);
|
||||
this.LogDebug(ex, "Stack Trace");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,14 +585,14 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
if (_secret == null)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Error, "Secret is null", this);
|
||||
this.LogError("Secret is null");
|
||||
|
||||
_secret = new ServerTokenSecrets(string.Empty);
|
||||
}
|
||||
|
||||
_secret.Tokens.Clear();
|
||||
|
||||
foreach (var uiClientContext in UiClients)
|
||||
foreach (var uiClientContext in UiClientContexts)
|
||||
{
|
||||
_secret.Tokens.Add(uiClientContext.Key, uiClientContext.Value.Token);
|
||||
}
|
||||
@@ -601,7 +603,8 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Exception updating secret", this);
|
||||
this.LogError("Exception updating secret: {message}", ex.Message);
|
||||
this.LogDebug(ex, "Stack Trace");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,30 +701,64 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
|
||||
var token = new JoinToken { Code = bridge.UserCode, RoomKey = bridge.RoomKey, Uuid = _parent.SystemUuid, TouchpanelKey = touchPanelKey };
|
||||
|
||||
UiClients.Add(key, new UiClientContext(token));
|
||||
UiClientContexts.Add(key, new UiClientContext(token));
|
||||
|
||||
var path = _wsPath + key;
|
||||
|
||||
_server.AddWebSocketService(path, () =>
|
||||
{
|
||||
var c = new UiClient();
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Constructing UiClient with id: {0}", this, key);
|
||||
c.Controller = _parent;
|
||||
c.RoomKey = bridge.RoomKey;
|
||||
UiClients[key].SetClient(c);
|
||||
return c;
|
||||
this.LogInformation("Building a UiClient with ID {id}", token.Id);
|
||||
return BuildUiClient(bridge.RoomKey, token, key);
|
||||
});
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Information, "Added new WebSocket UiClient service at path: {path}", this, path);
|
||||
Debug.LogMessage(LogEventLevel.Information, "Token: {@token}", this, token);
|
||||
this.LogInformation("Added new WebSocket UiClient for path: {path}", path);
|
||||
this.LogInformation("Token: {@token}", token);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "{serviceCount} websocket services present", this, _server.WebSocketServices.Count);
|
||||
this.LogVerbose("{serviceCount} websocket services present", _server.WebSocketServices.Count);
|
||||
|
||||
UpdateSecret();
|
||||
|
||||
return (key, path);
|
||||
}
|
||||
|
||||
private UiClient BuildUiClient(string roomKey, JoinToken token, string key)
|
||||
{
|
||||
var c = new UiClient($"uiclient-{key}-{roomKey}-{token.Id}", token.Id, token.Token, token.TouchpanelKey);
|
||||
this.LogInformation("Constructing UiClient with key {key} and ID {id}", key, token.Id);
|
||||
c.Controller = _parent;
|
||||
c.RoomKey = roomKey;
|
||||
|
||||
if (uiClients.ContainsKey(token.Id))
|
||||
{
|
||||
this.LogWarning("removing client with duplicate id {id}", token.Id);
|
||||
uiClients.Remove(token.Id);
|
||||
}
|
||||
uiClients.Add(token.Id, c);
|
||||
// UiClients[key].SetClient(c);
|
||||
c.ConnectionClosed += (o, a) => uiClients.Remove(a.ClientId);
|
||||
token.Id = null;
|
||||
return c;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints out the session data for each path
|
||||
/// </summary>
|
||||
public void PrintSessionData()
|
||||
{
|
||||
foreach (var path in _server.WebSocketServices.Paths)
|
||||
{
|
||||
this.LogInformation("Path: {path}", path);
|
||||
this.LogInformation(" Session Count: {sessionCount}", _server.WebSocketServices[path].Sessions.Count);
|
||||
this.LogInformation(" Active Session Count: {activeSessionCount}", _server.WebSocketServices[path].Sessions.ActiveIDs.Count());
|
||||
this.LogInformation(" Inactive Session Count: {inactiveSessionCount}", _server.WebSocketServices[path].Sessions.InactiveIDs.Count());
|
||||
this.LogInformation(" Active Clients:");
|
||||
foreach (var session in _server.WebSocketServices[path].Sessions.IDs)
|
||||
{
|
||||
this.LogInformation(" Client ID: {id}", (_server.WebSocketServices[path].Sessions[session] as UiClient)?.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all clients from the server
|
||||
/// </summary>
|
||||
@@ -729,7 +766,7 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
if (s == "?" || string.IsNullOrEmpty(s))
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse(@"Removes all clients from the server. To execute add 'confirm' to command");
|
||||
CrestronConsole.ConsoleCommandResponse(@"Remove all clients from the server. To execute add 'confirm' to command");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -739,7 +776,7 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var client in UiClients)
|
||||
foreach (var client in UiClientContexts)
|
||||
{
|
||||
if (client.Value.Client != null && client.Value.Client.Context.WebSocket.IsAlive)
|
||||
{
|
||||
@@ -757,7 +794,7 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
}
|
||||
}
|
||||
|
||||
UiClients.Clear();
|
||||
UiClientContexts.Clear();
|
||||
|
||||
UpdateSecret();
|
||||
}
|
||||
@@ -776,9 +813,9 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
|
||||
var key = s;
|
||||
|
||||
if (UiClients.ContainsKey(key))
|
||||
if (UiClientContexts.ContainsKey(key))
|
||||
{
|
||||
var uiClientContext = UiClients[key];
|
||||
var uiClientContext = UiClientContexts[key];
|
||||
|
||||
if (uiClientContext.Client != null && uiClientContext.Client.Context.WebSocket.IsAlive)
|
||||
{
|
||||
@@ -788,7 +825,7 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
var path = _wsPath + key;
|
||||
if (_server.RemoveWebSocketService(path))
|
||||
{
|
||||
UiClients.Remove(key);
|
||||
UiClientContexts.Remove(key);
|
||||
|
||||
UpdateSecret();
|
||||
|
||||
@@ -812,9 +849,9 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Mobile Control UI Client Info:\r");
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse(string.Format("{0} clients found:\r", UiClients.Count));
|
||||
CrestronConsole.ConsoleCommandResponse(string.Format("{0} clients found:\r", UiClientContexts.Count));
|
||||
|
||||
foreach (var client in UiClients)
|
||||
foreach (var client in UiClientContexts)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse(string.Format("RoomKey: {0} Token: {1}\r", client.Value.Token.RoomKey, client.Key));
|
||||
}
|
||||
@@ -826,9 +863,9 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
foreach (var client in UiClients.Values)
|
||||
{
|
||||
if (client.Client != null && client.Client.Context.WebSocket.IsAlive)
|
||||
if (client != null && client.Context.WebSocket.IsAlive)
|
||||
{
|
||||
client.Client.Context.WebSocket.Close(CloseStatusCode.Normal, "Server Shutting Down");
|
||||
client.Context.WebSocket.Close(CloseStatusCode.Normal, "Server Shutting Down");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -883,7 +920,8 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Caught an exception in the OnGet handler", this);
|
||||
this.LogError("Exception in OnGet handler: {message}", ex.Message);
|
||||
this.LogDebug(ex, "Stack Trace");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -962,62 +1000,81 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
|
||||
this.LogVerbose("Join Room Request with token: {token}", token);
|
||||
|
||||
byte[] body;
|
||||
|
||||
if (UiClients.TryGetValue(token, out UiClientContext clientContext))
|
||||
{
|
||||
var bridge = _parent.GetRoomBridge(clientContext.Token.RoomKey);
|
||||
|
||||
if (bridge != null)
|
||||
{
|
||||
res.StatusCode = 200;
|
||||
res.ContentType = "application/json";
|
||||
|
||||
// Construct the response object
|
||||
JoinResponse jRes = new JoinResponse
|
||||
{
|
||||
ClientId = token,
|
||||
RoomKey = bridge.RoomKey,
|
||||
SystemUuid = _parent.SystemUuid,
|
||||
RoomUuid = _parent.SystemUuid,
|
||||
Config = _parent.GetConfigWithPluginVersion(),
|
||||
CodeExpires = new DateTime().AddYears(1),
|
||||
UserCode = bridge.UserCode,
|
||||
UserAppUrl = string.Format("http://{0}:{1}/mc/app",
|
||||
CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0),
|
||||
Port),
|
||||
EnableDebug = false
|
||||
};
|
||||
|
||||
// Serialize to JSON and convert to Byte[]
|
||||
var json = JsonConvert.SerializeObject(jRes);
|
||||
var body = Encoding.UTF8.GetBytes(json);
|
||||
res.ContentLength64 = body.LongLength;
|
||||
|
||||
// Send the response
|
||||
res.Close(body, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
var message = string.Format("Unable to find bridge with key: {0}", clientContext.Token.RoomKey);
|
||||
res.StatusCode = 404;
|
||||
res.ContentType = "application/json";
|
||||
this.LogVerbose("{message}", message);
|
||||
var body = Encoding.UTF8.GetBytes(message);
|
||||
res.ContentLength64 = body.LongLength;
|
||||
res.Close(body, true);
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!UiClientContexts.TryGetValue(token, out UiClientContext clientContext))
|
||||
{
|
||||
var message = "Token invalid or has expired";
|
||||
res.StatusCode = 401;
|
||||
res.ContentType = "application/json";
|
||||
this.LogVerbose("{message}", message);
|
||||
var body = Encoding.UTF8.GetBytes(message);
|
||||
body = Encoding.UTF8.GetBytes(message);
|
||||
res.ContentLength64 = body.LongLength;
|
||||
res.Close(body, true);
|
||||
return;
|
||||
}
|
||||
|
||||
var bridge = _parent.GetRoomBridge(clientContext.Token.RoomKey);
|
||||
|
||||
if (bridge == null)
|
||||
{
|
||||
var message = string.Format("Unable to find bridge with key: {0}", clientContext.Token.RoomKey);
|
||||
res.StatusCode = 404;
|
||||
res.ContentType = "application/json";
|
||||
this.LogVerbose("{message}", message);
|
||||
body = Encoding.UTF8.GetBytes(message);
|
||||
res.ContentLength64 = body.LongLength;
|
||||
res.Close(body, true);
|
||||
return;
|
||||
}
|
||||
|
||||
res.StatusCode = 200;
|
||||
res.ContentType = "application/json";
|
||||
|
||||
var devices = DeviceManager.GetDevices();
|
||||
Dictionary<string, DeviceInterfaceInfo> deviceInterfaces = new Dictionary<string, DeviceInterfaceInfo>();
|
||||
|
||||
foreach (var device in devices)
|
||||
{
|
||||
var interfaces = device?.GetType().GetInterfaces().Select((i) => i.Name).ToList() ?? new List<string>();
|
||||
|
||||
deviceInterfaces.Add(device.Key, new DeviceInterfaceInfo
|
||||
{
|
||||
Key = device.Key,
|
||||
Name = (device as IKeyName)?.Name ?? "",
|
||||
Interfaces = interfaces
|
||||
});
|
||||
}
|
||||
|
||||
var clientId = $"{Utilities.GetNextClientId()}";
|
||||
clientContext.Token.Id = clientId;
|
||||
|
||||
this.LogVerbose("Assigning ClientId: {clientId}", clientId);
|
||||
|
||||
// Construct the response object
|
||||
JoinResponse jRes = new JoinResponse
|
||||
{
|
||||
ClientId = clientId,
|
||||
RoomKey = bridge.RoomKey,
|
||||
SystemUuid = _parent.SystemUuid,
|
||||
RoomUuid = _parent.SystemUuid,
|
||||
Config = _parent.GetConfigWithPluginVersion(),
|
||||
CodeExpires = new DateTime().AddYears(1),
|
||||
UserCode = bridge.UserCode,
|
||||
UserAppUrl = string.Format("http://{0}:{1}/mc/app",
|
||||
CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0),
|
||||
Port),
|
||||
EnableDebug = false,
|
||||
DeviceInterfaceSupport = deviceInterfaces
|
||||
};
|
||||
|
||||
// Serialize to JSON and convert to Byte[]
|
||||
var json = JsonConvert.SerializeObject(jRes);
|
||||
body = Encoding.UTF8.GetBytes(json);
|
||||
res.ContentLength64 = body.LongLength;
|
||||
|
||||
// Send the response
|
||||
res.Close(body, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1171,7 +1228,7 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
}
|
||||
else
|
||||
{
|
||||
this.LogVerbose("File not found: {filePath}", filePath);
|
||||
this.LogWarning("File not found: {filePath}", filePath);
|
||||
res.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
res.Close();
|
||||
return;
|
||||
@@ -1199,12 +1256,14 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
/// </summary>
|
||||
public void SendMessageToAllClients(string message)
|
||||
{
|
||||
foreach (var clientContext in UiClients.Values)
|
||||
foreach (var client in uiClients.Values)
|
||||
{
|
||||
if (clientContext.Client != null && clientContext.Client.Context.WebSocket.IsAlive)
|
||||
if (!client.Context.WebSocket.IsAlive)
|
||||
{
|
||||
clientContext.Client.Context.WebSocket.Send(message);
|
||||
continue;
|
||||
}
|
||||
|
||||
client.Context.WebSocket.Send(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1223,17 +1282,16 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
return;
|
||||
}
|
||||
|
||||
if (UiClients.TryGetValue((string)clientId, out UiClientContext clientContext))
|
||||
if (uiClients.TryGetValue((string)clientId, out var client))
|
||||
{
|
||||
if (clientContext.Client != null)
|
||||
{
|
||||
var socket = clientContext.Client.Context.WebSocket;
|
||||
var socket = client.Context.WebSocket;
|
||||
|
||||
if (socket.IsAlive)
|
||||
{
|
||||
socket.Send(message);
|
||||
}
|
||||
if (!socket.IsAlive)
|
||||
{
|
||||
this.LogError("Unable to send message to client {id}. Client is disconnected: {message}", clientId, message);
|
||||
return;
|
||||
}
|
||||
socket.Send(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1241,145 +1299,4 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Version
|
||||
/// </summary>
|
||||
public class Version
|
||||
{
|
||||
[JsonProperty("serverVersion")]
|
||||
public string ServerVersion { get; set; }
|
||||
|
||||
[JsonProperty("serverIsRunningOnProcessorHardware")]
|
||||
public bool ServerIsRunningOnProcessorHardware { get; private set; }
|
||||
|
||||
public Version()
|
||||
{
|
||||
ServerIsRunningOnProcessorHardware = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a UiClientContext
|
||||
/// </summary>
|
||||
public class UiClientContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the Client
|
||||
/// </summary>
|
||||
public UiClient Client { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the Token
|
||||
/// </summary>
|
||||
public JoinToken Token { get; private set; }
|
||||
|
||||
public UiClientContext(JoinToken token)
|
||||
{
|
||||
Token = token;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SetClient method
|
||||
/// </summary>
|
||||
public void SetClient(UiClient client)
|
||||
{
|
||||
Client = client;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a ServerTokenSecrets
|
||||
/// </summary>
|
||||
public class ServerTokenSecrets
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the GrantCode
|
||||
/// </summary>
|
||||
public string GrantCode { get; set; }
|
||||
|
||||
public Dictionary<string, JoinToken> Tokens { get; set; }
|
||||
|
||||
public ServerTokenSecrets(string grantCode)
|
||||
{
|
||||
GrantCode = grantCode;
|
||||
Tokens = new Dictionary<string, JoinToken>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a JoinToken
|
||||
/// </summary>
|
||||
public class JoinToken
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the Code
|
||||
/// </summary>
|
||||
public string Code { get; set; }
|
||||
|
||||
public string RoomKey { get; set; }
|
||||
|
||||
public string Uuid { get; set; }
|
||||
|
||||
public string TouchpanelKey { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Token
|
||||
/// </summary>
|
||||
public string Token { get; set; } = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a JoinResponse
|
||||
/// </summary>
|
||||
public class JoinResponse
|
||||
{
|
||||
[JsonProperty("clientId")]
|
||||
/// <summary>
|
||||
/// Gets or sets the ClientId
|
||||
/// </summary>
|
||||
public string ClientId { get; set; }
|
||||
|
||||
[JsonProperty("roomKey")]
|
||||
public string RoomKey { get; set; }
|
||||
|
||||
[JsonProperty("systemUUid")]
|
||||
public string SystemUuid { get; set; }
|
||||
|
||||
[JsonProperty("roomUUid")]
|
||||
/// <summary>
|
||||
/// Gets or sets the RoomUuid
|
||||
/// </summary>
|
||||
public string RoomUuid { get; set; }
|
||||
|
||||
[JsonProperty("config")]
|
||||
/// <summary>
|
||||
/// Gets or sets the Config
|
||||
/// </summary>
|
||||
public object Config { get; set; }
|
||||
|
||||
[JsonProperty("codeExpires")]
|
||||
/// <summary>
|
||||
/// Gets or sets the CodeExpires
|
||||
/// </summary>
|
||||
public DateTime CodeExpires { get; set; }
|
||||
|
||||
[JsonProperty("userCode")]
|
||||
/// <summary>
|
||||
/// Gets or sets the UserCode
|
||||
/// </summary>
|
||||
public string UserCode { get; set; }
|
||||
|
||||
[JsonProperty("userAppUrl")]
|
||||
/// <summary>
|
||||
/// Gets or sets the UserAppUrl
|
||||
/// </summary>
|
||||
public string UserAppUrl { get; set; }
|
||||
|
||||
[JsonProperty("enableDebug")]
|
||||
/// <summary>
|
||||
/// Gets or sets the EnableDebug
|
||||
/// </summary>
|
||||
public bool EnableDebug { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a ServerTokenSecrets
|
||||
/// </summary>
|
||||
public class ServerTokenSecrets
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the GrantCode
|
||||
/// </summary>
|
||||
public string GrantCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Tokens for this server
|
||||
/// </summary>
|
||||
public Dictionary<string, JoinToken> Tokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance of the <see cref="ServerTokenSecrets"/> class with the provided grant code
|
||||
/// </summary>
|
||||
/// <param name="grantCode">The grant code for this server</param>
|
||||
public ServerTokenSecrets(string grantCode)
|
||||
{
|
||||
GrantCode = grantCode;
|
||||
Tokens = new Dictionary<string, JoinToken>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,59 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Logging;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
using PepperDash.Essentials.RoomBridges;
|
||||
using Serilog.Events;
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using WebSocketSharp;
|
||||
using WebSocketSharp.Server;
|
||||
using ErrorEventArgs = WebSocketSharp.ErrorEventArgs;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the behaviour to associate with a UiClient for WebSocket communication
|
||||
/// </summary>
|
||||
public class UiClient : WebSocketBehavior
|
||||
public class UiClient : WebSocketBehavior, IKeyed
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Key { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Client ID used by client for this connection
|
||||
/// </summary>
|
||||
public string Id { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Token associated with this client
|
||||
/// </summary>
|
||||
public string Token { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Touchpanel Key associated with this client
|
||||
/// </summary>
|
||||
public string TouchpanelKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mobile control system controller that handles this client's messages
|
||||
/// </summary>
|
||||
public MobileControlSystemController Controller { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the room key that this client is associated with
|
||||
/// </summary>
|
||||
public string RoomKey { get; set; }
|
||||
|
||||
private string _clientId;
|
||||
|
||||
/// <summary>
|
||||
/// The timestamp when this client connection was established
|
||||
/// </summary>
|
||||
private DateTime _connectionTime;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration that this client has been connected. Returns zero if not currently connected.
|
||||
/// </summary>
|
||||
public TimeSpan ConnectedDuration
|
||||
{
|
||||
get
|
||||
@@ -41,28 +69,35 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
}
|
||||
}
|
||||
|
||||
public UiClient()
|
||||
{
|
||||
/// <summary>
|
||||
/// Triggered when this client closes it's connection
|
||||
/// </summary>
|
||||
public event EventHandler<ConnectionClosedEventArgs> ConnectionClosed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the UiClient class with the specified key
|
||||
/// </summary>
|
||||
/// <param name="key">The unique key to identify this client</param>
|
||||
/// <param name="id">The client ID used by the client for this connection</param>
|
||||
/// <param name="token">The token associated with this client</param>
|
||||
/// <param name="touchpanelKey">The touchpanel key associated with this client</param>
|
||||
public UiClient(string key, string id, string token, string touchpanelKey = "")
|
||||
{
|
||||
Key = key;
|
||||
Id = id;
|
||||
Token = token;
|
||||
TouchpanelKey = touchpanelKey;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnOpen()
|
||||
{
|
||||
base.OnOpen();
|
||||
|
||||
var url = Context.WebSocket.Url;
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "New WebSocket Connection from: {0}", null, url);
|
||||
_connectionTime = DateTime.Now;
|
||||
|
||||
var match = Regex.Match(url.AbsoluteUri, "(?:ws|wss):\\/\\/.*(?:\\/mc\\/api\\/ui\\/join\\/)(.*)");
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
_connectionTime = DateTime.Now;
|
||||
return;
|
||||
}
|
||||
|
||||
var clientId = match.Groups[1].Value;
|
||||
_clientId = clientId;
|
||||
Log.Output = (data, message) => Utilities.ConvertWebsocketLog(data, message, this);
|
||||
Log.Level = LogLevel.Trace;
|
||||
|
||||
if (Controller == null)
|
||||
{
|
||||
@@ -75,8 +110,9 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
Type = "/system/clientJoined",
|
||||
Content = JToken.FromObject(new
|
||||
{
|
||||
clientId,
|
||||
clientId = Id,
|
||||
roomKey = RoomKey,
|
||||
touchpanelKey = TouchpanelKey ?? string.Empty,
|
||||
})
|
||||
};
|
||||
|
||||
@@ -86,7 +122,7 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
|
||||
if (bridge == null) return;
|
||||
|
||||
SendUserCodeToClient(bridge, clientId);
|
||||
SendUserCodeToClient(bridge, Id);
|
||||
|
||||
bridge.UserCodeChanged -= Bridge_UserCodeChanged;
|
||||
bridge.UserCodeChanged += Bridge_UserCodeChanged;
|
||||
@@ -94,11 +130,21 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
// TODO: Future: Check token to see if there's already an open session using that token and reject/close the session
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the UserCodeChanged event from a room bridge and sends the updated user code to the client
|
||||
/// </summary>
|
||||
/// <param name="sender">The room bridge that raised the event</param>
|
||||
/// <param name="e">Event arguments</param>
|
||||
private void Bridge_UserCodeChanged(object sender, EventArgs e)
|
||||
{
|
||||
SendUserCodeToClient((MobileControlEssentialsRoomBridge)sender, _clientId);
|
||||
SendUserCodeToClient((MobileControlEssentialsRoomBridge)sender, Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the current user code and QR code URL to the specified client
|
||||
/// </summary>
|
||||
/// <param name="bridge">The room bridge containing the user code information</param>
|
||||
/// <param name="clientId">The ID of the client to send the information to</param>
|
||||
private void SendUserCodeToClient(MobileControlBridgeBase bridge, string clientId)
|
||||
{
|
||||
var content = new
|
||||
@@ -117,6 +163,7 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
Controller.SendMessageObjectToDirectClient(message);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnMessage(MessageEventArgs e)
|
||||
{
|
||||
base.OnMessage(e);
|
||||
@@ -128,18 +175,33 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnClose(CloseEventArgs e)
|
||||
{
|
||||
base.OnClose(e);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Closing: {0} reason: {1}", null, e.Code, e.Reason);
|
||||
this.LogInformation("WebSocket UiClient Closing: {code} reason: {reason}", e.Code, e.Reason);
|
||||
|
||||
foreach (var messenger in Controller.Messengers)
|
||||
{
|
||||
messenger.Value.UnsubscribeClient(Id);
|
||||
}
|
||||
|
||||
foreach (var messenger in Controller.DefaultMessengers)
|
||||
{
|
||||
messenger.Value.UnsubscribeClient(Id);
|
||||
}
|
||||
|
||||
ConnectionClosed?.Invoke(this, new ConnectionClosedEventArgs(Id));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnError(ErrorEventArgs e)
|
||||
{
|
||||
base.OnError(e);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Error: {exception} message: {message}", e.Exception, e.Message);
|
||||
this.LogError("WebSocket UiClient Error: {message}", e.Message);
|
||||
this.LogDebug(e.Exception, "Stack Trace");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a UiClientContext
|
||||
/// </summary>
|
||||
public class UiClientContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the Client
|
||||
/// </summary>
|
||||
public UiClient Client { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the Token
|
||||
/// </summary>
|
||||
public JoinToken Token { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize an instance of the <see cref="UiClientContext"/> class with the provided token
|
||||
/// </summary>
|
||||
/// <param name="token">token for this client</param>
|
||||
public UiClientContext(JoinToken token)
|
||||
{
|
||||
Token = token;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SetClient method
|
||||
/// </summary>
|
||||
public void SetClient(UiClient client)
|
||||
{
|
||||
Client = client;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Version
|
||||
/// </summary>
|
||||
public class Version
|
||||
{
|
||||
/// <summary>
|
||||
/// Server version this Websocket is connected to
|
||||
/// </summary>
|
||||
[JsonProperty("serverVersion")]
|
||||
public string ServerVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the server is on a processor
|
||||
/// </summary>
|
||||
|
||||
[JsonProperty("serverIsRunningOnProcessorHardware")]
|
||||
public bool ServerIsRunningOnProcessorHardware { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize an instance of the <see cref="Version"/> class
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <see cref="ServerIsRunningOnProcessorHardware"/> property is set to true by default.
|
||||
/// </remarks>
|
||||
public Version()
|
||||
{
|
||||
ServerIsRunningOnProcessorHardware = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,25 +13,28 @@ namespace PepperDash.Essentials.WebSocketServer
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a WebSocketServerSecret
|
||||
/// Stores a secret value using the provided secret store provider
|
||||
/// </summary>
|
||||
public class WebSocketServerSecret : ISecret
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the Provider
|
||||
/// Gets the Secret Provider associated with this secret
|
||||
/// </summary>
|
||||
public ISecretProvider Provider { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Key
|
||||
/// Gets the Key associated with this secret
|
||||
/// </summary>
|
||||
public string Key { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Value
|
||||
/// Gets the Value associated with this secret
|
||||
/// </summary>
|
||||
public object Value { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize and instance of the <see cref="WebSocketServerSecret"/> class
|
||||
/// </summary>
|
||||
public WebSocketServerSecret(string key, object value, ISecretProvider provider)
|
||||
{
|
||||
Key = key;
|
||||
|
||||
Reference in New Issue
Block a user