mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-01-29 20:34:51 +00:00
feat: move MC into Essentials
In order to solve some dependency issues that keep cropping up, MC should be moved back into the Essentials repo and loaded automatically on startup. This will allow for all plugins that use the MC Messengers library to use the same version without fear of overwriting a dll due to loading of plugin libraries.
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
public class AuthorizationResponse
|
||||
{
|
||||
[JsonProperty("authorized")]
|
||||
public bool Authorized { get; set; }
|
||||
|
||||
[JsonProperty("reason", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Reason { get; set; } = null;
|
||||
}
|
||||
|
||||
public class AuthorizationRequest
|
||||
{
|
||||
[JsonProperty("grantCode")]
|
||||
public string GrantCode { get; set; }
|
||||
}
|
||||
}
|
||||
16
src/PepperDash.Essentials.MobileControl/Interfaces.cs
Normal file
16
src/PepperDash.Essentials.MobileControl/Interfaces.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Essentials.Room.MobileControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a room whose configuration is derived from runtime data,
|
||||
/// perhaps from another program, and that the data may not be fully
|
||||
/// available at startup.
|
||||
/// </summary>
|
||||
public interface IDelayedConfiguration
|
||||
{
|
||||
|
||||
|
||||
event EventHandler<EventArgs> ConfigurationIsReady;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
|
||||
using PepperDash.Essentials.Core.Web.RequestHandlers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
public class MobileControlAction : IMobileControlAction
|
||||
{
|
||||
public IMobileControlMessenger Messenger { get; private set; }
|
||||
|
||||
public Action<string, string, JToken> Action {get; private set; }
|
||||
|
||||
public MobileControlAction(IMobileControlMessenger messenger, Action<string,string, JToken> handler) {
|
||||
Messenger = messenger;
|
||||
Action = handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
161
src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs
Normal file
161
src/PepperDash.Essentials.MobileControl/MobileControlConfig.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class MobileControlConfig
|
||||
{
|
||||
[JsonProperty("serverUrl")]
|
||||
public string ServerUrl { get; set; }
|
||||
|
||||
[JsonProperty("clientAppUrl")]
|
||||
public string ClientAppUrl { get; set; }
|
||||
|
||||
#if SERIES4
|
||||
[JsonProperty("directServer")]
|
||||
public MobileControlDirectServerPropertiesConfig DirectServer { get; set; }
|
||||
|
||||
[JsonProperty("applicationConfig")]
|
||||
public MobileControlApplicationConfig ApplicationConfig { get; set; }
|
||||
|
||||
[JsonProperty("enableApiServer")]
|
||||
public bool EnableApiServer { get; set; }
|
||||
#endif
|
||||
|
||||
[JsonProperty("roomBridges")]
|
||||
[Obsolete("No longer necessary")]
|
||||
public List<MobileControlRoomBridgePropertiesConfig> RoomBridges { get; set; }
|
||||
|
||||
public MobileControlConfig()
|
||||
{
|
||||
RoomBridges = new List<MobileControlRoomBridgePropertiesConfig>();
|
||||
|
||||
#if SERIES4
|
||||
EnableApiServer = true; // default to true
|
||||
ApplicationConfig = null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public class MobileControlDirectServerPropertiesConfig
|
||||
{
|
||||
[JsonProperty("enableDirectServer")]
|
||||
public bool EnableDirectServer { get; set; }
|
||||
|
||||
[JsonProperty("port")]
|
||||
public int Port { get; set; }
|
||||
|
||||
[JsonProperty("logging")]
|
||||
public MobileControlLoggingConfig Logging { get; set; }
|
||||
|
||||
[JsonProperty("automaticallyForwardPortToCSLAN")]
|
||||
public bool? AutomaticallyForwardPortToCSLAN { get; set; }
|
||||
|
||||
public MobileControlDirectServerPropertiesConfig()
|
||||
{
|
||||
Logging = new MobileControlLoggingConfig();
|
||||
}
|
||||
}
|
||||
|
||||
public class MobileControlLoggingConfig
|
||||
{
|
||||
[JsonProperty("enableRemoteLogging")]
|
||||
public bool EnableRemoteLogging { get; set; }
|
||||
|
||||
[JsonProperty("host")]
|
||||
public string Host { get; set; }
|
||||
|
||||
[JsonProperty("port")]
|
||||
public int Port { get; set; }
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class MobileControlRoomBridgePropertiesConfig
|
||||
{
|
||||
[JsonProperty("key")]
|
||||
public string Key { get; set; }
|
||||
|
||||
[JsonProperty("roomKey")]
|
||||
public string RoomKey { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class MobileControlSimplRoomBridgePropertiesConfig
|
||||
{
|
||||
[JsonProperty("eiscId")]
|
||||
public string EiscId { get; set; }
|
||||
}
|
||||
|
||||
public class MobileControlApplicationConfig
|
||||
{
|
||||
[JsonProperty("apiPath")]
|
||||
public string ApiPath { get; set; }
|
||||
|
||||
[JsonProperty("gatewayAppPath")]
|
||||
public string GatewayAppPath { get; set; }
|
||||
|
||||
[JsonProperty("enableDev")]
|
||||
public bool? EnableDev { get; set; }
|
||||
|
||||
[JsonProperty("logoPath")]
|
||||
/// <summary>
|
||||
/// Client logo to be used in header and/or splash screen
|
||||
/// </summary>
|
||||
public string LogoPath { get; set; }
|
||||
|
||||
[JsonProperty("iconSet")]
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public MCIconSet? IconSet { get; set; }
|
||||
|
||||
[JsonProperty("loginMode")]
|
||||
public string LoginMode { get; set; }
|
||||
|
||||
[JsonProperty("modes")]
|
||||
public Dictionary<string, McMode> Modes { get; set; }
|
||||
|
||||
[JsonProperty("enableRemoteLogging")]
|
||||
public bool Logging { get; set; }
|
||||
|
||||
[JsonProperty("partnerMetadata", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public List<MobileControlPartnerMetadata> PartnerMetadata { get; set; }
|
||||
}
|
||||
|
||||
public class MobileControlPartnerMetadata
|
||||
{
|
||||
[JsonProperty("role")]
|
||||
public string Role { get; set; }
|
||||
|
||||
[JsonProperty("description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[JsonProperty("logoPath")]
|
||||
public string LogoPath { get; set; }
|
||||
}
|
||||
|
||||
public class McMode
|
||||
{
|
||||
[JsonProperty("listPageText")]
|
||||
public string ListPageText { get; set; }
|
||||
[JsonProperty("loginHelpText")]
|
||||
public string LoginHelpText { get; set; }
|
||||
|
||||
[JsonProperty("passcodePageText")]
|
||||
public string PasscodePageText { get; set; }
|
||||
}
|
||||
|
||||
public enum MCIconSet
|
||||
{
|
||||
GOOGLE,
|
||||
HABANERO,
|
||||
NEO
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using PepperDash.Essentials.Room.MobileControl;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
public class MobileControlDeviceFactory : EssentialsDeviceFactory<MobileControlSystemController>
|
||||
{
|
||||
public MobileControlDeviceFactory()
|
||||
{
|
||||
TypeNames = new List<string> { "appserver", "mobilecontrol", "webserver" };
|
||||
}
|
||||
|
||||
public override EssentialsDevice BuildDevice(DeviceConfig dc)
|
||||
{
|
||||
try
|
||||
{
|
||||
var props = dc.Properties.ToObject<MobileControlConfig>();
|
||||
return new MobileControlSystemController(dc.Key, dc.Name, props);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogMessage(e, "Error building Mobile Control System Controller");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MobileControlSimplFactory : EssentialsDeviceFactory<MobileControlSIMPLRoomBridge>
|
||||
{
|
||||
public MobileControlSimplFactory()
|
||||
{
|
||||
TypeNames = new List<string> { "mobilecontrolbridge-ddvc01", "mobilecontrolbridge-simpl" };
|
||||
}
|
||||
|
||||
public override EssentialsDevice BuildDevice(DeviceConfig dc)
|
||||
{
|
||||
var comm = CommFactory.GetControlPropertiesConfig(dc);
|
||||
|
||||
var bridge = new MobileControlSIMPLRoomBridge(dc.Key, dc.Name, comm.IpIdInt);
|
||||
|
||||
bridge.AddPreActivationAction(() =>
|
||||
{
|
||||
var parent = GetMobileControlDevice();
|
||||
|
||||
if (parent == null)
|
||||
{
|
||||
Debug.Console(0, bridge, "ERROR: Cannot connect bridge. System controller not present");
|
||||
return;
|
||||
}
|
||||
Debug.Console(0, bridge, "Linking to parent controller");
|
||||
|
||||
/*bridge.AddParent(parent);
|
||||
parent.AddBridge(bridge);*/
|
||||
|
||||
parent.AddDeviceMessenger(bridge);
|
||||
});
|
||||
|
||||
return bridge;
|
||||
}
|
||||
|
||||
private static MobileControlSystemController GetMobileControlDevice()
|
||||
{
|
||||
var mobileControlList = DeviceManager.AllDevices.OfType<MobileControlSystemController>().ToList();
|
||||
|
||||
if (mobileControlList.Count > 1)
|
||||
{
|
||||
Debug.Console(0, Debug.ErrorLogLevel.Warning,
|
||||
"Multiple instances of Mobile Control Server found.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mobileControlList.Count > 0)
|
||||
{
|
||||
return mobileControlList[0];
|
||||
}
|
||||
|
||||
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Mobile Control not enabled for this system");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to overlay additional config data from mobile control on
|
||||
/// </summary>
|
||||
public class MobileControlEssentialsConfig : EssentialsConfig
|
||||
{
|
||||
[JsonProperty("runtimeInfo")]
|
||||
public MobileControlRuntimeInfo RuntimeInfo { get; set; }
|
||||
|
||||
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;
|
||||
|
||||
if (this.Info == null)
|
||||
this.Info = new InfoConfig();
|
||||
|
||||
RuntimeInfo = new MobileControlRuntimeInfo();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to add any additional runtime information from mobile control to be send to the API
|
||||
/// </summary>
|
||||
public class MobileControlRuntimeInfo
|
||||
{
|
||||
[JsonProperty("pluginVersion")]
|
||||
public string PluginVersion { get; set; }
|
||||
|
||||
[JsonProperty("essentialsVersion")]
|
||||
public string EssentialsVersion { get; set; }
|
||||
|
||||
[JsonProperty("pepperDashCoreVersion")]
|
||||
public string PepperDashCoreVersion { get; set; }
|
||||
|
||||
[JsonProperty("essentialsPlugins")]
|
||||
public List<LoadedAssembly> EssentialsPlugins { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Essentials.MobileControl
|
||||
{
|
||||
public class MobileControlFactory
|
||||
{
|
||||
public MobileControlFactory() {
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
PluginLoader.SetEssentialsAssembly(assembly.GetName().Name, assembly);
|
||||
|
||||
var types = assembly.GetTypes().Where(t => typeof(IDeviceFactory).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);
|
||||
|
||||
if(types == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
try
|
||||
{
|
||||
var factory = (IDeviceFactory)Activator.CreateInstance(type);
|
||||
|
||||
factory.LoadTypeFactories();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Unable to load type '{type}' DeviceFactory: {factory}", null, type.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
using Crestron.SimplSharpPro.EthernetCommunication;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Essentials.Room.MobileControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic device connection through to and EISC for SIMPL01
|
||||
/// </summary>
|
||||
public class MobileControlSimplDeviceBridge : Device, IChannel, INumericKeypad
|
||||
{
|
||||
/// <summary>
|
||||
/// EISC used to talk to Simpl
|
||||
/// </summary>
|
||||
private readonly ThreeSeriesTcpIpEthernetIntersystemCommunications _eisc;
|
||||
|
||||
public MobileControlSimplDeviceBridge(string key, string name,
|
||||
ThreeSeriesTcpIpEthernetIntersystemCommunications eisc)
|
||||
: base(key, name)
|
||||
{
|
||||
_eisc = eisc;
|
||||
}
|
||||
|
||||
#region IChannel Members
|
||||
|
||||
public void ChannelUp(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public void ChannelDown(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public void LastChannel(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public void Guide(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public void Info(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public void Exit(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INumericKeypad Members
|
||||
|
||||
public void Digit0(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public void Digit1(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public void Digit2(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public void Digit3(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public void Digit4(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public void Digit5(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public void Digit6(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public void Digit7(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public void Digit8(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public void Digit9(bool pressRelease)
|
||||
{
|
||||
_eisc.SetBool(1111, pressRelease);
|
||||
}
|
||||
|
||||
public bool HasKeypadAccessoryButton1
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public string KeypadAccessoryButton1Label
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public void KeypadAccessoryButton1(bool pressRelease)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool HasKeypadAccessoryButton2
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public string KeypadAccessoryButton2Label
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public void KeypadAccessoryButton2(bool pressRelease)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,67 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ProjectType>ProgramLibrary</ProjectType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RootNamespace>PepperDash.Essentials</RootNamespace>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
<Deterministic>false</Deterministic>
|
||||
<AssemblyTitle>epi-essentials-mobile-control</AssemblyTitle>
|
||||
<Company>PepperDash Technologies</Company>
|
||||
<Product>epi-essentials-mobile-control</Product>
|
||||
<Description>This software is a plugin designed to work as a part of PepperDash Essentials for Crestron control processors. This plugin allows for connection to a PepperDash Mobile Control server.</Description>
|
||||
<Copyright>Copyright 2020</Copyright>
|
||||
<Version>4.0.0-local</Version>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
<InformationalVersion>$(Version)</InformationalVersion>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<Authors>PepperDash Technologies</Authors>
|
||||
<PackageId>PepperDash.Essentials.4Series.Plugin.MobileControl</PackageId>
|
||||
<PackageProjectUrl>https://github.com/PepperDash/Essentials</PackageProjectUrl>
|
||||
<PackageTags>crestron 4series</PackageTags>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DefineConstants>TRACE;DEBUG;SERIES4</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<DefineConstants>TRACE;SERIES4</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="bin\**" />
|
||||
<EmbeddedResource Remove="bin\**" />
|
||||
<None Remove="bin\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.21.90" />
|
||||
<PackageReference Include="PepperDashCore" Version="2.0.1" />
|
||||
<PackageReference Include="WebSocketSharp-netstandard" Version="1.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="C:\Users\awelker\source\Essentials\Essentials\src\PepperDash.Essentials.MobileControl\bin\Debug\net472\PepperDash.Essentials.MobileControl.4.0.0-local.net472.cplz" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="C:\Users\awelker\source\Essentials\Essentials\src\PepperDash.Essentials.MobileControl\bin\Debug\net472\PepperDash.Essentials.MobileControl.4.0.0-local.net472.cplz" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj" >
|
||||
<Private>false</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\PepperDash.Essentials.Devices.Common\PepperDash.Essentials.Devices.Common.csproj" >
|
||||
<Private>false</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\PepperDash.Essentials.MobileControl.Messengers\PepperDash.Essentials.MobileControl.Messengers.csproj" >
|
||||
<Private>false</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,130 @@
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
|
||||
using System;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public abstract class MobileControlBridgeBase : MessengerBase, IMobileControlRoomMessenger
|
||||
{
|
||||
public event EventHandler<EventArgs> UserCodeChanged;
|
||||
|
||||
public event EventHandler<EventArgs> UserPromptedForCode;
|
||||
|
||||
public event EventHandler<EventArgs> ClientJoined;
|
||||
|
||||
public event EventHandler<EventArgs> AppUrlChanged;
|
||||
|
||||
public IMobileControl Parent { get; private set; }
|
||||
|
||||
public string AppUrl { get; private set; }
|
||||
public string UserCode { get; private set; }
|
||||
|
||||
public string QrCodeUrl { get; protected set; }
|
||||
|
||||
public string QrCodeChecksum { get; protected set; }
|
||||
|
||||
public string McServerUrl { get; private set; }
|
||||
|
||||
public abstract string RoomName { get; }
|
||||
|
||||
public abstract string RoomKey { get; }
|
||||
|
||||
protected MobileControlBridgeBase(string key, string messagePath)
|
||||
: base(key, messagePath)
|
||||
{
|
||||
}
|
||||
|
||||
protected MobileControlBridgeBase(string key, string messagePath, IKeyName device)
|
||||
: base(key, messagePath, device)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the parent. Does nothing else. Override to add functionality such
|
||||
/// as adding actions to parent
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
public virtual void AddParent(IMobileControl parent)
|
||||
{
|
||||
Parent = parent;
|
||||
|
||||
McServerUrl = Parent.ClientAppUrl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the UserCode on the bridge object. Called from controller. A changed code will
|
||||
/// fire method UserCodeChange. Override that to handle changes
|
||||
/// </summary>
|
||||
/// <param name="code"></param>
|
||||
public void SetUserCode(string code)
|
||||
{
|
||||
var changed = UserCode != code;
|
||||
UserCode = code;
|
||||
if (changed)
|
||||
{
|
||||
UserCodeChange();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the UserCode on the bridge object. Called from controller. A changed code will
|
||||
/// fire method UserCodeChange. Override that to handle changes
|
||||
/// </summary>
|
||||
/// <param name="code"></param>
|
||||
/// <param name="qrChecksum">Checksum of the QR code. Used for Cisco codec branding command</param>
|
||||
public void SetUserCode(string code, string qrChecksum)
|
||||
{
|
||||
QrCodeChecksum = qrChecksum;
|
||||
|
||||
SetUserCode(code);
|
||||
}
|
||||
|
||||
public virtual void UpdateAppUrl(string url)
|
||||
{
|
||||
AppUrl = url;
|
||||
|
||||
var handler = AppUrlChanged;
|
||||
|
||||
if (handler == null) return;
|
||||
|
||||
handler(this, new EventArgs());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Empty method in base class. Override this to add functionality
|
||||
/// when code changes
|
||||
/// </summary>
|
||||
protected virtual void UserCodeChange()
|
||||
{
|
||||
Debug.Console(1, this, "Server user code changed: {0}", UserCode);
|
||||
|
||||
var qrUrl = string.Format($"{Parent.Host}/api/rooms/{Parent.SystemUuid}/{RoomKey}/qr?x={new Random().Next()}");
|
||||
QrCodeUrl = qrUrl;
|
||||
|
||||
Debug.Console(1, this, "Server user code changed: {0} - {1}", UserCode, qrUrl);
|
||||
|
||||
OnUserCodeChanged();
|
||||
}
|
||||
|
||||
protected void OnUserCodeChanged()
|
||||
{
|
||||
UserCodeChanged?.Invoke(this, new EventArgs());
|
||||
}
|
||||
|
||||
protected void OnUserPromptedForCode()
|
||||
{
|
||||
UserPromptedForCode?.Invoke(this, new EventArgs());
|
||||
}
|
||||
|
||||
protected void OnClientJoined()
|
||||
{
|
||||
ClientJoined?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,977 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
using PepperDash.Essentials.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
|
||||
using PepperDash.Essentials.Room.MobileControl;
|
||||
using PepperDash.Essentials.Room.Config;
|
||||
using PepperDash.Essentials.Devices.Common.VideoCodec;
|
||||
using PepperDash.Essentials.Devices.Common.AudioCodec;
|
||||
using PepperDash.Essentials.Devices.Common.Cameras;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using PepperDash.Essentials.Devices.Common.Room;
|
||||
using IShades = PepperDash.Essentials.Core.Shades.IShades;
|
||||
using ShadeBase = PepperDash.Essentials.Devices.Common.Shades.ShadeBase;
|
||||
using PepperDash.Essentials.Devices.Common.TouchPanel;
|
||||
using Crestron.SimplSharp;
|
||||
using Volume = PepperDash.Essentials.Room.MobileControl.Volume;
|
||||
using PepperDash.Essentials.Core.CrestronIO;
|
||||
using PepperDash.Essentials.Core.Lighting;
|
||||
using PepperDash.Essentials.Core.Shades;
|
||||
using PepperDash.Core.Logging;
|
||||
|
||||
|
||||
|
||||
#if SERIES4
|
||||
using PepperDash.Essentials.AppServer;
|
||||
#endif
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
public class MobileControlEssentialsRoomBridge : MobileControlBridgeBase
|
||||
{
|
||||
private List<JoinToken> _touchPanelTokens = new List<JoinToken>();
|
||||
public IEssentialsRoom Room { get; private set; }
|
||||
|
||||
public string DefaultRoomKey { get; private set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public override string RoomName
|
||||
{
|
||||
get { return Room.Name; }
|
||||
}
|
||||
|
||||
public override string RoomKey
|
||||
{
|
||||
get { return Room.Key; }
|
||||
}
|
||||
|
||||
public MobileControlEssentialsRoomBridge(IEssentialsRoom room) :
|
||||
this($"mobileControlBridge-{room.Key}", room.Key, room)
|
||||
{
|
||||
Room = room;
|
||||
}
|
||||
|
||||
public MobileControlEssentialsRoomBridge(string key, string roomKey, IEssentialsRoom room) : base(key, $"/room/{room.Key}", room as Device)
|
||||
{
|
||||
DefaultRoomKey = roomKey;
|
||||
|
||||
AddPreActivationAction(GetRoom);
|
||||
}
|
||||
|
||||
#if SERIES4
|
||||
protected override void RegisterActions()
|
||||
#else
|
||||
protected override void CustomRegisterWithAppServer(MobileControlSystemController appServerController)
|
||||
#endif
|
||||
{
|
||||
// we add actions to the messaging system with a path, and a related action. Custom action
|
||||
// content objects can be handled in the controller's LineReceived method - and perhaps other
|
||||
// sub-controller parsing could be attached to these classes, so that the systemController
|
||||
// doesn't need to know about everything.
|
||||
|
||||
this.LogInformation("Registering Actions with AppServer");
|
||||
|
||||
AddAction("/promptForCode", (id, content) => OnUserPromptedForCode());
|
||||
AddAction("/clientJoined", (id, content) => OnClientJoined());
|
||||
|
||||
AddAction("/touchPanels", (id, content) => OnTouchPanelsUpdated(content));
|
||||
|
||||
AddAction($"/userApp", (id, content) => OnUserAppUpdated(content));
|
||||
|
||||
AddAction("/userCode", (id, content) =>
|
||||
{
|
||||
var msg = content.ToObject<UserCodeChangedContent>();
|
||||
|
||||
SetUserCode(msg.UserCode, msg.QrChecksum ?? string.Empty);
|
||||
});
|
||||
|
||||
|
||||
// Source Changes and room off
|
||||
AddAction("/status", (id, content) =>
|
||||
{
|
||||
SendFullStatusForClientId(id, Room);
|
||||
});
|
||||
|
||||
if (Room is IRunRouteAction routeRoom)
|
||||
AddAction("/source", (id, content) =>
|
||||
{
|
||||
|
||||
var msg = content.ToObject<SourceSelectMessageContent>();
|
||||
|
||||
this.LogVerbose("Received request to route to source: {sourceListKey} on list: {sourceList}", msg.SourceListItemKey, msg.SourceListKey);
|
||||
|
||||
routeRoom.RunRouteAction(msg.SourceListItemKey, msg.SourceListKey);
|
||||
});
|
||||
|
||||
if (Room is IRunDirectRouteAction directRouteRoom)
|
||||
{
|
||||
AddAction("/directRoute", (id, content) =>
|
||||
{
|
||||
var msg = content.ToObject<DirectRoute>();
|
||||
|
||||
|
||||
this.LogVerbose("Running direct route from {sourceKey} to {destinationKey} with signal type {signalType}", msg.SourceKey, msg.DestinationKey, msg.SignalType);
|
||||
|
||||
directRouteRoom.RunDirectRoute(msg.SourceKey, msg.DestinationKey, msg.SignalType);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (Room is IRunDefaultPresentRoute defaultRoom)
|
||||
AddAction("/defaultsource", (id, content) => defaultRoom.RunDefaultPresentRoute());
|
||||
|
||||
if (Room is IHasCurrentVolumeControls volumeRoom)
|
||||
{
|
||||
volumeRoom.CurrentVolumeDeviceChange += Room_CurrentVolumeDeviceChange;
|
||||
|
||||
if (volumeRoom.CurrentVolumeControls == null) return;
|
||||
|
||||
AddAction("/volumes/master/level", (id, content) =>
|
||||
{
|
||||
var msg = content.ToObject<MobileControlSimpleContent<ushort>>();
|
||||
|
||||
|
||||
if (volumeRoom.CurrentVolumeControls is IBasicVolumeWithFeedback basicVolumeWithFeedback)
|
||||
basicVolumeWithFeedback.SetVolume(msg.Value);
|
||||
});
|
||||
|
||||
AddAction("/volumes/master/muteToggle", (id, content) => volumeRoom.CurrentVolumeControls.MuteToggle());
|
||||
|
||||
AddAction("/volumes/master/muteOn", (id, content) =>
|
||||
{
|
||||
if (volumeRoom.CurrentVolumeControls is IBasicVolumeWithFeedback basicVolumeWithFeedback)
|
||||
basicVolumeWithFeedback.MuteOn();
|
||||
});
|
||||
|
||||
AddAction("/volumes/master/muteOff", (id, content) =>
|
||||
{
|
||||
if (volumeRoom.CurrentVolumeControls is IBasicVolumeWithFeedback basicVolumeWithFeedback)
|
||||
basicVolumeWithFeedback.MuteOff();
|
||||
});
|
||||
|
||||
AddAction("/volumes/master/volumeUp", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) =>
|
||||
{
|
||||
if (volumeRoom.CurrentVolumeControls is IBasicVolumeWithFeedback basicVolumeWithFeedback)
|
||||
{
|
||||
basicVolumeWithFeedback.VolumeUp(b);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
AddAction("/volumes/master/volumeDown", (id, content) => PressAndHoldHandler.HandlePressAndHold(DeviceKey, content, (b) =>
|
||||
{
|
||||
if (volumeRoom.CurrentVolumeControls is IBasicVolumeWithFeedback basicVolumeWithFeedback)
|
||||
{
|
||||
basicVolumeWithFeedback.VolumeDown(b);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
// Registers for initial volume events, if possible
|
||||
if (volumeRoom.CurrentVolumeControls is IBasicVolumeWithFeedback currentVolumeDevice)
|
||||
{
|
||||
this.LogVerbose("Registering for volume feedback events");
|
||||
|
||||
currentVolumeDevice.MuteFeedback.OutputChange += MuteFeedback_OutputChange;
|
||||
currentVolumeDevice.VolumeLevelFeedback.OutputChange += VolumeLevelFeedback_OutputChange;
|
||||
}
|
||||
}
|
||||
|
||||
if (Room is IHasCurrentSourceInfoChange sscRoom)
|
||||
sscRoom.CurrentSourceChange += Room_CurrentSingleSourceChange;
|
||||
|
||||
if (Room is IEssentialsHuddleVtc1Room vtcRoom)
|
||||
{
|
||||
if (vtcRoom.ScheduleSource != null)
|
||||
{
|
||||
var key = vtcRoom.Key + "-" + Key;
|
||||
|
||||
if (!AppServerController.CheckForDeviceMessenger(key))
|
||||
{
|
||||
var scheduleMessenger = new IHasScheduleAwarenessMessenger(key, vtcRoom.ScheduleSource,
|
||||
$"/room/{vtcRoom.Key}");
|
||||
AppServerController.AddDeviceMessenger(scheduleMessenger);
|
||||
}
|
||||
}
|
||||
|
||||
vtcRoom.InCallFeedback.OutputChange += InCallFeedback_OutputChange;
|
||||
}
|
||||
|
||||
if (Room is IPrivacy privacyRoom)
|
||||
{
|
||||
AddAction("/volumes/master/privacyMuteToggle", (id, content) => privacyRoom.PrivacyModeToggle());
|
||||
|
||||
privacyRoom.PrivacyModeIsOnFeedback.OutputChange += PrivacyModeIsOnFeedback_OutputChange;
|
||||
}
|
||||
|
||||
|
||||
if (Room is IRunDefaultCallRoute defCallRm)
|
||||
{
|
||||
AddAction("/activityVideo", (id, content) => defCallRm.RunDefaultCallRoute());
|
||||
}
|
||||
|
||||
Room.OnFeedback.OutputChange += OnFeedback_OutputChange;
|
||||
Room.IsCoolingDownFeedback.OutputChange += IsCoolingDownFeedback_OutputChange;
|
||||
Room.IsWarmingUpFeedback.OutputChange += IsWarmingUpFeedback_OutputChange;
|
||||
|
||||
AddTechRoomActions();
|
||||
}
|
||||
|
||||
private void OnTouchPanelsUpdated(JToken content)
|
||||
{
|
||||
var message = content.ToObject<ApiTouchPanelToken>();
|
||||
|
||||
_touchPanelTokens = message.TouchPanels;
|
||||
|
||||
UpdateTouchPanelAppUrls(message.UserAppUrl);
|
||||
}
|
||||
|
||||
private void UpdateTouchPanelAppUrls(string userAppUrl)
|
||||
{
|
||||
foreach (var tp in _touchPanelTokens)
|
||||
{
|
||||
var dev = DeviceManager.AllDevices.OfType<IMobileControlTouchpanelController>().FirstOrDefault((tpc) => tpc.Key.Equals(tp.TouchpanelKey, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
if (dev == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//UpdateAppUrl($"{userAppUrl}?token={tp.Token}");
|
||||
|
||||
dev.SetAppUrl($"{userAppUrl}?token={tp.Token}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUserAppUpdated(JToken content)
|
||||
{
|
||||
var message = content.ToObject<ApiTouchPanelToken>();
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "Updating User App URL to {userAppUrl}. Full Message: {@message}", this, message.UserAppUrl, content);
|
||||
|
||||
UpdateTouchPanelAppUrls(message.UserAppUrl);
|
||||
}
|
||||
|
||||
private void InCallFeedback_OutputChange(object sender, FeedbackEventArgs e)
|
||||
{
|
||||
var state = new RoomStateMessage
|
||||
{
|
||||
IsInCall = e.BoolValue
|
||||
};
|
||||
PostStatusMessage(state);
|
||||
}
|
||||
|
||||
private void GetRoom()
|
||||
{
|
||||
if (Room != null)
|
||||
{
|
||||
this.LogInformation("Room with key {key} already linked.", DefaultRoomKey);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!(DeviceManager.GetDeviceForKey(DefaultRoomKey) is IEssentialsRoom tempRoom))
|
||||
{
|
||||
this.LogInformation("Room with key {key} not found or is not an Essentials Room", DefaultRoomKey);
|
||||
return;
|
||||
}
|
||||
|
||||
Room = tempRoom;
|
||||
}
|
||||
|
||||
protected override void UserCodeChange()
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Server user code changed: {userCode}", this, UserCode);
|
||||
|
||||
var qrUrl = string.Format("{0}/rooms/{1}/{3}/qr?x={2}", Parent?.Host, Parent?.SystemUuid, new Random().Next(), DefaultRoomKey);
|
||||
|
||||
QrCodeUrl = qrUrl;
|
||||
|
||||
this.LogDebug("Server user code changed: {userCode} - {qrUrl}", UserCode, qrUrl);
|
||||
|
||||
OnUserCodeChanged();
|
||||
}
|
||||
|
||||
/* /// <summary>
|
||||
/// Override of base: calls base to add parent and then registers actions and events.
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
public override void AddParent(MobileControlSystemController parent)
|
||||
{
|
||||
base.AddParent(parent);
|
||||
|
||||
}*/
|
||||
|
||||
private void AddTechRoomActions()
|
||||
{
|
||||
if (!(Room is IEssentialsTechRoom techRoom))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddAction("/roomPowerOn", (id, content) => techRoom.RoomPowerOn());
|
||||
AddAction("/roomPowerOff", (id, content) => techRoom.RoomPowerOff());
|
||||
}
|
||||
|
||||
private void PrivacyModeIsOnFeedback_OutputChange(object sender, FeedbackEventArgs e)
|
||||
{
|
||||
var state = new RoomStateMessage();
|
||||
|
||||
var volumes = new Dictionary<string, Volume>
|
||||
{
|
||||
{ "master", new Volume("master")
|
||||
{
|
||||
PrivacyMuted = e.BoolValue
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
state.Volumes = volumes;
|
||||
|
||||
PostStatusMessage(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void IsSharingFeedback_OutputChange(object sender, FeedbackEventArgs e)
|
||||
{
|
||||
// sharing source
|
||||
string shareText;
|
||||
bool isSharing;
|
||||
|
||||
if (Room is IHasCurrentSourceInfoChange srcInfoRoom && (Room is IHasVideoCodec vcRoom && (vcRoom.VideoCodec.SharingContentIsOnFeedback.BoolValue && srcInfoRoom.CurrentSourceInfo != null)))
|
||||
{
|
||||
shareText = srcInfoRoom.CurrentSourceInfo.PreferredName;
|
||||
isSharing = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
shareText = "None";
|
||||
isSharing = false;
|
||||
}
|
||||
|
||||
var state = new RoomStateMessage
|
||||
{
|
||||
Share = new ShareState
|
||||
{
|
||||
CurrentShareText = shareText,
|
||||
IsSharing = isSharing
|
||||
}
|
||||
};
|
||||
|
||||
PostStatusMessage(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void IsWarmingUpFeedback_OutputChange(object sender, FeedbackEventArgs e)
|
||||
{
|
||||
var state = new
|
||||
{
|
||||
isWarmingUp = e.BoolValue
|
||||
};
|
||||
|
||||
PostStatusMessage(JToken.FromObject(state));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void IsCoolingDownFeedback_OutputChange(object sender, FeedbackEventArgs e)
|
||||
{
|
||||
var state = new
|
||||
{
|
||||
isCoolingDown = e.BoolValue
|
||||
};
|
||||
PostStatusMessage(JToken.FromObject(state));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void OnFeedback_OutputChange(object sender, FeedbackEventArgs e)
|
||||
{
|
||||
var state = new
|
||||
{
|
||||
isOn = e.BoolValue
|
||||
};
|
||||
PostStatusMessage(JToken.FromObject(state));
|
||||
}
|
||||
|
||||
private void Room_CurrentVolumeDeviceChange(object sender, VolumeDeviceChangeEventArgs e)
|
||||
{
|
||||
if (e.OldDev is IBasicVolumeWithFeedback)
|
||||
{
|
||||
var oldDev = e.OldDev as IBasicVolumeWithFeedback;
|
||||
oldDev.MuteFeedback.OutputChange -= MuteFeedback_OutputChange;
|
||||
oldDev.VolumeLevelFeedback.OutputChange -= VolumeLevelFeedback_OutputChange;
|
||||
}
|
||||
|
||||
if (e.NewDev is IBasicVolumeWithFeedback)
|
||||
{
|
||||
var newDev = e.NewDev as IBasicVolumeWithFeedback;
|
||||
newDev.MuteFeedback.OutputChange += MuteFeedback_OutputChange;
|
||||
newDev.VolumeLevelFeedback.OutputChange += VolumeLevelFeedback_OutputChange;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler for mute changes
|
||||
/// </summary>
|
||||
private void MuteFeedback_OutputChange(object sender, FeedbackEventArgs e)
|
||||
{
|
||||
var state = new RoomStateMessage();
|
||||
|
||||
var volumes = new Dictionary<string, Volume>
|
||||
{
|
||||
{ "master", new Volume("master", e.BoolValue) }
|
||||
};
|
||||
|
||||
state.Volumes = volumes;
|
||||
|
||||
PostStatusMessage(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles Volume changes on room
|
||||
/// </summary>
|
||||
private void VolumeLevelFeedback_OutputChange(object sender, FeedbackEventArgs e)
|
||||
{
|
||||
|
||||
var state = new
|
||||
{
|
||||
volumes = new Dictionary<string, Volume>
|
||||
{
|
||||
{ "master", new Volume("master", e.IntValue) }
|
||||
}
|
||||
};
|
||||
PostStatusMessage(JToken.FromObject(state));
|
||||
}
|
||||
|
||||
|
||||
private void Room_CurrentSingleSourceChange(SourceListItem info, ChangeType type)
|
||||
{
|
||||
/* Example message
|
||||
* {
|
||||
"type":"/room/status",
|
||||
"content": {
|
||||
"selectedSourceKey": "off",
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the full status of the room to the server
|
||||
/// </summary>
|
||||
/// <param name="room"></param>
|
||||
private void SendFullStatusForClientId(string id, IEssentialsRoom room)
|
||||
{
|
||||
//Parent.SendMessageObject(GetFullStatus(room));
|
||||
var message = GetFullStatusForClientId(room);
|
||||
|
||||
if (message == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
PostStatusMessage(message, id);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets full room status
|
||||
/// </summary>
|
||||
/// <param name="room">The room to get status of</param>
|
||||
/// <returns>The status response message</returns>
|
||||
private RoomStateMessage GetFullStatusForClientId(IEssentialsRoom room)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.LogVerbose("GetFullStatus");
|
||||
|
||||
var sourceKey = room is IHasCurrentSourceInfoChange ? (room as IHasCurrentSourceInfoChange).CurrentSourceInfoKey : null;
|
||||
|
||||
var volumes = new Dictionary<string, Volume>();
|
||||
if (room is IHasCurrentVolumeControls rmVc)
|
||||
{
|
||||
if (rmVc.CurrentVolumeControls is IBasicVolumeWithFeedback vc)
|
||||
{
|
||||
var volume = new Volume("master", vc.VolumeLevelFeedback.UShortValue, vc.MuteFeedback.BoolValue, "Volume", true, "");
|
||||
if (room is IPrivacy privacyRoom)
|
||||
{
|
||||
volume.HasPrivacyMute = true;
|
||||
volume.PrivacyMuted = privacyRoom.PrivacyModeIsOnFeedback.BoolValue;
|
||||
}
|
||||
|
||||
volumes.Add("master", volume);
|
||||
}
|
||||
}
|
||||
|
||||
var state = new RoomStateMessage
|
||||
{
|
||||
Configuration = GetRoomConfiguration(room),
|
||||
ActivityMode = 1,
|
||||
IsOn = room.OnFeedback.BoolValue,
|
||||
SelectedSourceKey = sourceKey,
|
||||
Volumes = volumes,
|
||||
IsWarmingUp = room.IsWarmingUpFeedback.BoolValue,
|
||||
IsCoolingDown = room.IsCoolingDownFeedback.BoolValue
|
||||
};
|
||||
|
||||
if (room is IEssentialsHuddleVtc1Room vtcRoom)
|
||||
{
|
||||
state.IsInCall = vtcRoom.InCallFeedback.BoolValue;
|
||||
}
|
||||
|
||||
return state;
|
||||
} catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Error getting full status", this);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the configuration of the room and the details about the devices associated with the room
|
||||
/// <param name="room"></param>
|
||||
/// <returns></returns>
|
||||
private RoomConfiguration GetRoomConfiguration(IEssentialsRoom room)
|
||||
{
|
||||
try
|
||||
{
|
||||
var configuration = new RoomConfiguration
|
||||
{
|
||||
//ShutdownPromptSeconds = room.ShutdownPromptSeconds,
|
||||
TouchpanelKeys = DeviceManager.AllDevices.
|
||||
OfType<IMobileControlTouchpanelController>()
|
||||
.Where((tp) => tp.DefaultRoomKey.Equals(room.Key, StringComparison.InvariantCultureIgnoreCase))
|
||||
.Select(tp => tp.Key).ToList()
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var zrcTp = DeviceManager.AllDevices.OfType<IMobileControlTouchpanelController>().SingleOrDefault((tp) => tp.ZoomRoomController);
|
||||
|
||||
configuration.ZoomRoomControllerKey = zrcTp != null ? zrcTp.Key : null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
configuration.ZoomRoomControllerKey = room.Key;
|
||||
}
|
||||
|
||||
if (room is IHasCiscoNavigatorTouchpanel ciscoNavRoom)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Setting CiscoNavigatorKey to: {ciscoNavRoom.CiscoNavigatorTouchpanelKey}", this);
|
||||
configuration.CiscoNavigatorKey = ciscoNavRoom.CiscoNavigatorTouchpanelKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// find the room combiner for this room by checking if the room is in the list of rooms for the room combiner
|
||||
var roomCombiner = DeviceManager.AllDevices.OfType<IEssentialsRoomCombiner>().FirstOrDefault();
|
||||
|
||||
configuration.RoomCombinerKey = roomCombiner != null ? roomCombiner.Key : null;
|
||||
|
||||
|
||||
if (room is IEssentialsRoomPropertiesConfig propertiesConfig)
|
||||
{
|
||||
configuration.HelpMessage = propertiesConfig.PropertiesConfig.HelpMessageForDisplay;
|
||||
}
|
||||
|
||||
if (room is IEssentialsHuddleSpaceRoom huddleRoom && !string.IsNullOrEmpty(huddleRoom.PropertiesConfig.HelpMessageForDisplay))
|
||||
{
|
||||
this.LogVerbose("Getting huddle room config");
|
||||
configuration.HelpMessage = huddleRoom.PropertiesConfig.HelpMessageForDisplay;
|
||||
configuration.UiBehavior = huddleRoom.PropertiesConfig.UiBehavior;
|
||||
configuration.DefaultPresentationSourceKey = huddleRoom.PropertiesConfig.DefaultSourceItem;
|
||||
|
||||
}
|
||||
|
||||
if (room is IEssentialsHuddleVtc1Room vtc1Room && !string.IsNullOrEmpty(vtc1Room.PropertiesConfig.HelpMessageForDisplay))
|
||||
{
|
||||
this.LogVerbose("Getting vtc room config");
|
||||
configuration.HelpMessage = vtc1Room.PropertiesConfig.HelpMessageForDisplay;
|
||||
configuration.UiBehavior = vtc1Room.PropertiesConfig.UiBehavior;
|
||||
configuration.DefaultPresentationSourceKey = vtc1Room.PropertiesConfig.DefaultSourceItem;
|
||||
}
|
||||
|
||||
if (room is IEssentialsTechRoom techRoom && !string.IsNullOrEmpty(techRoom.PropertiesConfig.HelpMessage))
|
||||
{
|
||||
this.LogVerbose("Getting tech room config");
|
||||
configuration.HelpMessage = techRoom.PropertiesConfig.HelpMessage;
|
||||
}
|
||||
|
||||
if (room is IHasVideoCodec vcRoom)
|
||||
{
|
||||
if (vcRoom.VideoCodec != null)
|
||||
{
|
||||
this.LogVerbose("Getting codec config");
|
||||
var type = vcRoom.VideoCodec.GetType();
|
||||
|
||||
configuration.HasVideoConferencing = true;
|
||||
configuration.VideoCodecKey = vcRoom.VideoCodec.Key;
|
||||
configuration.VideoCodecIsZoomRoom = type.Name.Equals("ZoomRoom", StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
};
|
||||
|
||||
if (room is IHasAudioCodec acRoom)
|
||||
{
|
||||
if (acRoom.AudioCodec != null)
|
||||
{
|
||||
this.LogVerbose("Getting audio codec config");
|
||||
configuration.HasAudioConferencing = true;
|
||||
configuration.AudioCodecKey = acRoom.AudioCodec.Key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (room is IHasMatrixRouting matrixRoutingRoom)
|
||||
{
|
||||
this.LogVerbose("Getting matrix routing config");
|
||||
configuration.MatrixRoutingKey = matrixRoutingRoom.MatrixRoutingDeviceKey;
|
||||
configuration.EndpointKeys = matrixRoutingRoom.EndpointKeys;
|
||||
}
|
||||
|
||||
if (room is IEnvironmentalControls envRoom)
|
||||
{
|
||||
this.LogVerbose("Getting environmental controls config. RoomHasEnvironmentalControls: {hasEnvironmentalControls}", envRoom.HasEnvironmentalControlDevices);
|
||||
configuration.HasEnvironmentalControls = envRoom.HasEnvironmentalControlDevices;
|
||||
|
||||
if (envRoom.HasEnvironmentalControlDevices)
|
||||
{
|
||||
this.LogVerbose("Room Has {count} Environmental Control Devices.", envRoom.EnvironmentalControlDevices.Count);
|
||||
|
||||
foreach (var dev in envRoom.EnvironmentalControlDevices)
|
||||
{
|
||||
this.LogVerbose("Adding environmental device: {key}", dev.Key);
|
||||
|
||||
eEnvironmentalDeviceTypes type = eEnvironmentalDeviceTypes.None;
|
||||
|
||||
if (dev is ILightingScenes || dev is Devices.Common.Lighting.LightingBase)
|
||||
{
|
||||
type = eEnvironmentalDeviceTypes.Lighting;
|
||||
}
|
||||
else if (dev is ShadeBase || dev is IShadesOpenCloseStop || dev is IShadesOpenClosePreset)
|
||||
{
|
||||
type = eEnvironmentalDeviceTypes.Shade;
|
||||
}
|
||||
else if (dev is IShades)
|
||||
{
|
||||
type = eEnvironmentalDeviceTypes.ShadeController;
|
||||
}
|
||||
else if (dev is ISwitchedOutput)
|
||||
{
|
||||
type = eEnvironmentalDeviceTypes.Relay;
|
||||
}
|
||||
|
||||
this.LogVerbose("Environmental Device Type: {type}", type);
|
||||
|
||||
var envDevice = new EnvironmentalDeviceConfiguration(dev.Key, type);
|
||||
|
||||
configuration.EnvironmentalDevices.Add(envDevice);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.LogVerbose("Room Has No Environmental Control Devices");
|
||||
}
|
||||
}
|
||||
|
||||
if (room is IHasDefaultDisplay defDisplayRoom)
|
||||
{
|
||||
this.LogVerbose("Getting default display config");
|
||||
configuration.DefaultDisplayKey = defDisplayRoom.DefaultDisplay.Key;
|
||||
configuration.Destinations.Add(eSourceListItemDestinationTypes.defaultDisplay, defDisplayRoom.DefaultDisplay.Key);
|
||||
}
|
||||
|
||||
if (room is IHasMultipleDisplays multiDisplayRoom)
|
||||
{
|
||||
this.LogVerbose("Getting multiple display config");
|
||||
|
||||
if (multiDisplayRoom.Displays == null)
|
||||
{
|
||||
this.LogVerbose("Displays collection is null");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.LogVerbose("Displays collection exists");
|
||||
|
||||
configuration.Destinations = multiDisplayRoom.Displays.ToDictionary(kv => kv.Key, kv => kv.Value.Key);
|
||||
}
|
||||
}
|
||||
|
||||
if (room is IHasAccessoryDevices accRoom)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "Getting accessory devices config", this);
|
||||
|
||||
if (accRoom.AccessoryDeviceKeys == null)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "Accessory devices collection is null", this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "Accessory devices collection exists", this);
|
||||
|
||||
configuration.AccessoryDeviceKeys = accRoom.AccessoryDeviceKeys;
|
||||
}
|
||||
}
|
||||
|
||||
var sourceList = ConfigReader.ConfigObject.GetSourceListForKey(room.SourceListKey);
|
||||
if (sourceList != null)
|
||||
{
|
||||
this.LogVerbose("Getting source list config");
|
||||
configuration.SourceList = sourceList;
|
||||
configuration.HasRoutingControls = true;
|
||||
|
||||
foreach (var source in sourceList)
|
||||
{
|
||||
if (source.Value.SourceDevice is Devices.Common.IRSetTopBoxBase)
|
||||
{
|
||||
configuration.HasSetTopBoxControls = true;
|
||||
continue;
|
||||
}
|
||||
else if (source.Value.SourceDevice is CameraBase)
|
||||
{
|
||||
configuration.HasCameraControls = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var destinationList = ConfigReader.ConfigObject.GetDestinationListForKey(room.DestinationListKey);
|
||||
|
||||
if (destinationList != null)
|
||||
{
|
||||
configuration.DestinationList = destinationList;
|
||||
}
|
||||
|
||||
var audioControlPointList = ConfigReader.ConfigObject.GetAudioControlPointListForKey(room.AudioControlPointListKey);
|
||||
|
||||
if (audioControlPointList != null)
|
||||
{
|
||||
configuration.AudioControlPointList = audioControlPointList;
|
||||
}
|
||||
|
||||
var cameraList = ConfigReader.ConfigObject.GetCameraListForKey(room.CameraListKey);
|
||||
|
||||
if (cameraList != null)
|
||||
{
|
||||
configuration.CameraList = cameraList;
|
||||
}
|
||||
|
||||
return configuration;
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Exception getting room configuration");
|
||||
return new RoomConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class RoomStateMessage : DeviceStateMessageBase
|
||||
{
|
||||
[JsonProperty("configuration", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public RoomConfiguration Configuration { get; set; }
|
||||
|
||||
[JsonProperty("activityMode", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public int? ActivityMode { get; set; }
|
||||
[JsonProperty("advancedSharingActive", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? AdvancedSharingActive { get; set; }
|
||||
[JsonProperty("isOn", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? IsOn { get; set; }
|
||||
[JsonProperty("isWarmingUp", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? IsWarmingUp { get; set; }
|
||||
[JsonProperty("isCoolingDown", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? IsCoolingDown { get; set; }
|
||||
[JsonProperty("selectedSourceKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string SelectedSourceKey { get; set; }
|
||||
[JsonProperty("share", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public ShareState Share { get; set; }
|
||||
|
||||
[JsonProperty("volumes", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Dictionary<string, Volume> Volumes { get; set; }
|
||||
|
||||
[JsonProperty("isInCall", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? IsInCall { get; set; }
|
||||
}
|
||||
|
||||
public class ShareState
|
||||
{
|
||||
[JsonProperty("currentShareText", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string CurrentShareText { get; set; }
|
||||
[JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? Enabled { get; set; }
|
||||
[JsonProperty("isSharing", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? IsSharing { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the capabilities of the room and the associated device info
|
||||
/// </summary>
|
||||
public class RoomConfiguration
|
||||
{
|
||||
//[JsonProperty("shutdownPromptSeconds", NullValueHandling = NullValueHandling.Ignore)]
|
||||
//public int? ShutdownPromptSeconds { get; set; }
|
||||
|
||||
[JsonProperty("hasVideoConferencing", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasVideoConferencing { get; set; }
|
||||
[JsonProperty("videoCodecIsZoomRoom", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? VideoCodecIsZoomRoom { get; set; }
|
||||
[JsonProperty("hasAudioConferencing", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasAudioConferencing { get; set; }
|
||||
[JsonProperty("hasEnvironmentalControls", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasEnvironmentalControls { get; set; }
|
||||
[JsonProperty("hasCameraControls", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasCameraControls { get; set; }
|
||||
[JsonProperty("hasSetTopBoxControls", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasSetTopBoxControls { get; set; }
|
||||
[JsonProperty("hasRoutingControls", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasRoutingControls { get; set; }
|
||||
|
||||
[JsonProperty("touchpanelKeys", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public List<string> TouchpanelKeys { get; set; }
|
||||
|
||||
[JsonProperty("zoomRoomControllerKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string ZoomRoomControllerKey { get; set; }
|
||||
|
||||
[JsonProperty("ciscoNavigatorKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string CiscoNavigatorKey { get; set; }
|
||||
|
||||
|
||||
[JsonProperty("videoCodecKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string VideoCodecKey { get; set; }
|
||||
[JsonProperty("audioCodecKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string AudioCodecKey { get; set; }
|
||||
[JsonProperty("matrixRoutingKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string MatrixRoutingKey { get; set; }
|
||||
[JsonProperty("endpointKeys", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public List<string> EndpointKeys { get; set; }
|
||||
|
||||
[JsonProperty("accessoryDeviceKeys", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public List<string> AccessoryDeviceKeys { get; set; }
|
||||
|
||||
[JsonProperty("defaultDisplayKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string DefaultDisplayKey { get; set; }
|
||||
[JsonProperty("destinations", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Dictionary<eSourceListItemDestinationTypes, string> Destinations { get; set; }
|
||||
[JsonProperty("environmentalDevices", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public List<EnvironmentalDeviceConfiguration> EnvironmentalDevices { get; set; }
|
||||
[JsonProperty("sourceList", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Dictionary<string, SourceListItem> SourceList { get; set; }
|
||||
|
||||
[JsonProperty("destinationList", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Dictionary<string, DestinationListItem> DestinationList { get; set;}
|
||||
|
||||
[JsonProperty("audioControlPointList", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public AudioControlPointListItem AudioControlPointList { get; set; }
|
||||
|
||||
[JsonProperty("cameraList", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Dictionary<string, CameraListItem> CameraList { get; set; }
|
||||
|
||||
[JsonProperty("defaultPresentationSourceKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string DefaultPresentationSourceKey { get; set; }
|
||||
|
||||
|
||||
[JsonProperty("helpMessage", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string HelpMessage { get; set; }
|
||||
|
||||
[JsonProperty("techPassword", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string TechPassword { get; set; }
|
||||
|
||||
[JsonProperty("uiBehavior", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public EssentialsRoomUiBehaviorConfig UiBehavior { get; set; }
|
||||
|
||||
[JsonProperty("supportsAdvancedSharing", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? SupportsAdvancedSharing { get; set; }
|
||||
[JsonProperty("userCanChangeShareMode", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? UserCanChangeShareMode { get; set; }
|
||||
|
||||
[JsonProperty("roomCombinerKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string RoomCombinerKey { get; set; }
|
||||
|
||||
public RoomConfiguration()
|
||||
{
|
||||
Destinations = new Dictionary<eSourceListItemDestinationTypes, string>();
|
||||
EnvironmentalDevices = new List<EnvironmentalDeviceConfiguration>();
|
||||
SourceList = new Dictionary<string, SourceListItem>();
|
||||
TouchpanelKeys = new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
public class EnvironmentalDeviceConfiguration
|
||||
{
|
||||
[JsonProperty("deviceKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string DeviceKey { get; private set; }
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
[JsonProperty("deviceType", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public eEnvironmentalDeviceTypes DeviceType { get; private set; }
|
||||
|
||||
public EnvironmentalDeviceConfiguration(string key, eEnvironmentalDeviceTypes type)
|
||||
{
|
||||
DeviceKey = key;
|
||||
DeviceType = type;
|
||||
}
|
||||
}
|
||||
|
||||
public enum eEnvironmentalDeviceTypes
|
||||
{
|
||||
None,
|
||||
Lighting,
|
||||
Shade,
|
||||
ShadeController,
|
||||
Relay,
|
||||
}
|
||||
|
||||
public class ApiTouchPanelToken
|
||||
{
|
||||
[JsonProperty("touchPanels", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public List<JoinToken> TouchPanels { get; set; } = new List<JoinToken>();
|
||||
|
||||
[JsonProperty("userAppUrl", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string UserAppUrl { get; set; } = "";
|
||||
}
|
||||
|
||||
#if SERIES3
|
||||
public class SourceSelectMessageContent
|
||||
{
|
||||
public string SourceListItem { get; set; }
|
||||
public string SourceListKey { get; set; }
|
||||
}
|
||||
|
||||
public class DirectRoute
|
||||
{
|
||||
public string SourceKey { get; set; }
|
||||
public string DestinationKey { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="b"></param>
|
||||
public delegate void PressAndHoldAction(bool b);
|
||||
#endif
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,96 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PepperDash.Essentials.Room.MobileControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains all of the default joins that map to API funtions
|
||||
/// </summary>
|
||||
public class SourceDeviceMapDictionary : Dictionary<string, uint>
|
||||
{
|
||||
public SourceDeviceMapDictionary()
|
||||
{
|
||||
var dictionary = new Dictionary<string, uint>
|
||||
{
|
||||
{"preset01", 101},
|
||||
{"preset02", 102},
|
||||
{"preset03", 103},
|
||||
{"preset04", 104},
|
||||
{"preset05", 105},
|
||||
{"preset06", 106},
|
||||
{"preset07", 107},
|
||||
{"preset08", 108},
|
||||
{"preset09", 109},
|
||||
{"preset10", 110},
|
||||
{"preset11", 111},
|
||||
{"preset12", 112},
|
||||
{"preset13", 113},
|
||||
{"preset14", 114},
|
||||
{"preset15", 115},
|
||||
{"preset16", 116},
|
||||
{"preset17", 117},
|
||||
{"preset18", 118},
|
||||
{"preset19", 119},
|
||||
{"preset20", 120},
|
||||
{"preset21", 121},
|
||||
{"preset22", 122},
|
||||
{"preset23", 123},
|
||||
{"preset24", 124},
|
||||
{"num0", 130},
|
||||
{"num1", 131},
|
||||
{"num2", 132},
|
||||
{"num3", 133},
|
||||
{"num4", 134},
|
||||
{"num5", 135},
|
||||
{"num6", 136},
|
||||
{"num7", 137},
|
||||
{"num8", 138},
|
||||
{"num9", 139},
|
||||
{"numDash", 140},
|
||||
{"numEnter", 141},
|
||||
{"chanUp", 142},
|
||||
{"chanDown", 143},
|
||||
{"lastChan", 144},
|
||||
{"exit", 145},
|
||||
{"powerToggle", 146},
|
||||
{"red", 147},
|
||||
{"green", 148},
|
||||
{"yellow", 149},
|
||||
{"blue", 150},
|
||||
{"video", 151},
|
||||
{"previous", 152},
|
||||
{"next", 153},
|
||||
{"rewind", 154},
|
||||
{"ffwd", 155},
|
||||
{"closedCaption", 156},
|
||||
{"stop", 157},
|
||||
{"pause", 158},
|
||||
{"up", 159},
|
||||
{"down", 160},
|
||||
{"left", 161},
|
||||
{"right", 162},
|
||||
{"settings", 163},
|
||||
{"info", 164},
|
||||
{"return", 165},
|
||||
{"guide", 166},
|
||||
{"reboot", 167},
|
||||
{"dvrList", 168},
|
||||
{"replay", 169},
|
||||
{"play", 170},
|
||||
{"select", 171},
|
||||
{"record", 172},
|
||||
{"menu", 173},
|
||||
{"topMenu", 174},
|
||||
{"prevTrack", 175},
|
||||
{"nextTrack", 176},
|
||||
{"powerOn", 177},
|
||||
{"powerOff", 178},
|
||||
{"dot", 179}
|
||||
};
|
||||
|
||||
foreach (var item in dictionary)
|
||||
{
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using PepperDash.Core;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Essentials.Services
|
||||
{
|
||||
|
||||
public class MobileControlApiService
|
||||
{
|
||||
private readonly HttpClient _client;
|
||||
|
||||
public MobileControlApiService(string apiUrl)
|
||||
{
|
||||
var handler = new HttpClientHandler
|
||||
{
|
||||
AllowAutoRedirect = false,
|
||||
ServerCertificateCustomValidationCallback = (req, cert, certChain, errors) => true
|
||||
};
|
||||
|
||||
_client = new HttpClient(handler);
|
||||
}
|
||||
|
||||
public async Task<AuthorizationResponse> SendAuthorizationRequest(string apiUrl, string grantCode, string systemUuid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, $"{apiUrl}/system/{systemUuid}/authorize?grantCode={grantCode}");
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Sending authorization request to {host}", null, request.RequestUri);
|
||||
|
||||
var response = await _client.SendAsync(request);
|
||||
|
||||
var authResponse = new AuthorizationResponse
|
||||
{
|
||||
Authorized = response.StatusCode == System.Net.HttpStatusCode.OK
|
||||
};
|
||||
|
||||
if (authResponse.Authorized)
|
||||
{
|
||||
return authResponse;
|
||||
}
|
||||
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.Moved)
|
||||
{
|
||||
var location = response.Headers.Location;
|
||||
|
||||
authResponse.Reason = $"ERROR: Mobile Control API has moved. Please adjust configuration to \"{location}\"";
|
||||
|
||||
return authResponse;
|
||||
}
|
||||
|
||||
var responseString = await response.Content.ReadAsStringAsync();
|
||||
|
||||
switch (responseString)
|
||||
{
|
||||
case "codeNotFound":
|
||||
authResponse.Reason = $"Authorization failed. Code not found for system UUID {systemUuid}";
|
||||
break;
|
||||
case "uuidNotFound":
|
||||
authResponse.Reason = $"Authorization failed. System UUID {systemUuid} not found. Check Essentials configuration.";
|
||||
break;
|
||||
default:
|
||||
authResponse.Reason = $"Authorization failed. Response {response.StatusCode}: {responseString}";
|
||||
break;
|
||||
}
|
||||
|
||||
return authResponse;
|
||||
} catch(Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Error authorizing with Mobile Control");
|
||||
return new AuthorizationResponse { Authorized = false, Reason = ex.Message };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/PepperDash.Essentials.MobileControl/Touchpanel/ITheme.cs
Normal file
17
src/PepperDash.Essentials.MobileControl/Touchpanel/ITheme.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
public interface ITheme:IKeyed
|
||||
{
|
||||
string Theme { get; }
|
||||
|
||||
void UpdateTheme(string theme);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
|
||||
namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
public interface ITswAppControl : IKeyed
|
||||
{
|
||||
BoolFeedback AppOpenFeedback { get; }
|
||||
|
||||
void HideOpenApp();
|
||||
|
||||
void CloseOpenApp();
|
||||
|
||||
void OpenApp();
|
||||
}
|
||||
|
||||
public interface ITswZoomControl : IKeyed
|
||||
{
|
||||
BoolFeedback ZoomIncomingCallFeedback { get; }
|
||||
|
||||
BoolFeedback ZoomInCallFeedback { get; }
|
||||
|
||||
void EndZoomCall();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
|
||||
|
||||
namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
public class ITswAppControlMessenger : MessengerBase
|
||||
{
|
||||
private readonly ITswAppControl _appControl;
|
||||
|
||||
public ITswAppControlMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)
|
||||
{
|
||||
_appControl = device as ITswAppControl;
|
||||
}
|
||||
|
||||
protected override void RegisterActions()
|
||||
{
|
||||
if (_appControl == null)
|
||||
{
|
||||
Debug.Console(0, this, $"{_device.Key} does not implement ITswAppControl");
|
||||
return;
|
||||
}
|
||||
|
||||
AddAction($"/fullStatus", (id, context) => SendFullStatus());
|
||||
|
||||
AddAction($"/openApp", (id, context) => _appControl.OpenApp());
|
||||
|
||||
AddAction($"/closeApp", (id, context) => _appControl.CloseOpenApp());
|
||||
|
||||
AddAction($"/hideApp", (id, context) => _appControl.HideOpenApp());
|
||||
|
||||
_appControl.AppOpenFeedback.OutputChange += (s, a) =>
|
||||
{
|
||||
PostStatusMessage(JToken.FromObject(new
|
||||
{
|
||||
appOpen = a.BoolValue
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
private void SendFullStatus()
|
||||
{
|
||||
var message = new TswAppStateMessage
|
||||
{
|
||||
AppOpen = _appControl.AppOpenFeedback.BoolValue,
|
||||
};
|
||||
|
||||
PostStatusMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public class TswAppStateMessage : DeviceStateMessageBase
|
||||
{
|
||||
[JsonProperty("appOpen", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? AppOpen { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
public class ITswZoomControlMessenger : MessengerBase
|
||||
{
|
||||
private readonly ITswZoomControl _zoomControl;
|
||||
|
||||
public ITswZoomControlMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)
|
||||
{
|
||||
_zoomControl = device as ITswZoomControl;
|
||||
}
|
||||
|
||||
protected override void RegisterActions()
|
||||
{
|
||||
if (_zoomControl == null)
|
||||
{
|
||||
Debug.Console(0, this, $"{_device.Key} does not implement ITswZoomControl");
|
||||
return;
|
||||
}
|
||||
|
||||
AddAction($"/fullStatus", (id, context) => SendFullStatus());
|
||||
|
||||
|
||||
AddAction($"/endCall", (id, context) => _zoomControl.EndZoomCall());
|
||||
|
||||
_zoomControl.ZoomIncomingCallFeedback.OutputChange += (s, a) =>
|
||||
{
|
||||
PostStatusMessage(JToken.FromObject(new
|
||||
{
|
||||
incomingCall = a.BoolValue,
|
||||
inCall = _zoomControl.ZoomInCallFeedback.BoolValue
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
_zoomControl.ZoomInCallFeedback.OutputChange += (s, a) =>
|
||||
{
|
||||
PostStatusMessage(JToken.FromObject(
|
||||
new
|
||||
{
|
||||
inCall = a.BoolValue,
|
||||
incomingCall = _zoomControl.ZoomIncomingCallFeedback.BoolValue
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
private void SendFullStatus()
|
||||
{
|
||||
var message = new TswZoomStateMessage
|
||||
{
|
||||
InCall = _zoomControl?.ZoomInCallFeedback.BoolValue,
|
||||
IncomingCall = _zoomControl?.ZoomIncomingCallFeedback.BoolValue
|
||||
};
|
||||
|
||||
PostStatusMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public class TswZoomStateMessage : DeviceStateMessageBase
|
||||
{
|
||||
[JsonProperty("inCall", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? InCall { get; set; }
|
||||
|
||||
[JsonProperty("incomingCall", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? IncomingCall { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,571 @@
|
||||
using Crestron.SimplSharpPro;
|
||||
using Crestron.SimplSharpPro.DeviceSupport;
|
||||
using Crestron.SimplSharpPro.UI;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Logging;
|
||||
using PepperDash.Essentials.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using PepperDash.Essentials.Core.DeviceInfo;
|
||||
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
|
||||
using PepperDash.Essentials.Core.UI;
|
||||
using PepperDash.Essentials.Touchpanel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Feedback = PepperDash.Essentials.Core.Feedback;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.TouchPanel
|
||||
{
|
||||
//public interface IMobileControlTouchpanelController
|
||||
//{
|
||||
// StringFeedback AppUrlFeedback { get; }
|
||||
// string DefaultRoomKey { get; }
|
||||
// string DeviceKey { get; }
|
||||
//}
|
||||
|
||||
|
||||
public class MobileControlTouchpanelController : TouchpanelBase, IHasFeedback, ITswAppControl, ITswZoomControl, IDeviceInfoProvider, IMobileControlTouchpanelController, ITheme
|
||||
{
|
||||
private readonly MobileControlTouchpanelProperties localConfig;
|
||||
private IMobileControlRoomMessenger _bridge;
|
||||
|
||||
private string _appUrl;
|
||||
|
||||
public StringFeedback AppUrlFeedback { get; private set; }
|
||||
private readonly StringFeedback QrCodeUrlFeedback;
|
||||
private readonly StringFeedback McServerUrlFeedback;
|
||||
private readonly StringFeedback UserCodeFeedback;
|
||||
|
||||
private readonly BoolFeedback _appOpenFeedback;
|
||||
|
||||
public BoolFeedback AppOpenFeedback => _appOpenFeedback;
|
||||
|
||||
private readonly BoolFeedback _zoomIncomingCallFeedback;
|
||||
|
||||
public BoolFeedback ZoomIncomingCallFeedback => _zoomIncomingCallFeedback;
|
||||
|
||||
private readonly BoolFeedback _zoomInCallFeedback;
|
||||
|
||||
public event DeviceInfoChangeHandler DeviceInfoChanged;
|
||||
|
||||
public BoolFeedback ZoomInCallFeedback => _zoomInCallFeedback;
|
||||
|
||||
|
||||
public FeedbackCollection<Feedback> Feedbacks { get; private set; }
|
||||
|
||||
public FeedbackCollection<Feedback> ZoomFeedbacks { get; private set; }
|
||||
|
||||
public string DefaultRoomKey => _config.DefaultRoomKey;
|
||||
|
||||
public bool UseDirectServer => localConfig.UseDirectServer;
|
||||
|
||||
public bool ZoomRoomController => localConfig.ZoomRoomController;
|
||||
|
||||
public string Theme => localConfig.Theme;
|
||||
|
||||
public StringFeedback ThemeFeedback { get; private set; }
|
||||
|
||||
public DeviceInfo DeviceInfo => new DeviceInfo();
|
||||
|
||||
public MobileControlTouchpanelController(string key, string name, BasicTriListWithSmartObject panel, MobileControlTouchpanelProperties config) : base(key, name, panel, config)
|
||||
{
|
||||
localConfig = config;
|
||||
|
||||
AddPostActivationAction(SubscribeForMobileControlUpdates);
|
||||
|
||||
ThemeFeedback = new StringFeedback($"{Key}-theme",() => Theme);
|
||||
AppUrlFeedback = new StringFeedback($"{Key}-appUrl", () => _appUrl);
|
||||
QrCodeUrlFeedback = new StringFeedback($"{Key}-qrCodeUrl", () => _bridge?.QrCodeUrl);
|
||||
McServerUrlFeedback = new StringFeedback($"{Key}-mcServerUrl", () => _bridge?.McServerUrl);
|
||||
UserCodeFeedback = new StringFeedback($"{Key}-userCode", () => _bridge?.UserCode);
|
||||
|
||||
_appOpenFeedback = new BoolFeedback($"{Key}-appOpen", () =>
|
||||
{
|
||||
if (Panel is TswX60BaseClass tsX60)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"x60 sending {tsX60.ExtenderApplicationControlReservedSigs.HideOpenApplicationFeedback.BoolValue}");
|
||||
return !tsX60.ExtenderApplicationControlReservedSigs.HideOpenApplicationFeedback.BoolValue;
|
||||
}
|
||||
|
||||
if (Panel is TswX70Base tsX70)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"x70 sending {tsX70.ExtenderApplicationControlReservedSigs.HideOpenedApplicationFeedback.BoolValue}");
|
||||
return !tsX70.ExtenderApplicationControlReservedSigs.HideOpenedApplicationFeedback.BoolValue;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
_zoomIncomingCallFeedback = new BoolFeedback($"{Key}-zoomIncomingCall", () =>
|
||||
{
|
||||
if (Panel is TswX60WithZoomRoomAppReservedSigs tsX60)
|
||||
{
|
||||
return tsX60.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.BoolValue;
|
||||
}
|
||||
|
||||
if (Panel is TswX70Base tsX70)
|
||||
{
|
||||
return tsX70.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.BoolValue;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
_zoomInCallFeedback = new BoolFeedback($"{Key}-zoomInCall", () =>
|
||||
{
|
||||
if (Panel is TswX60WithZoomRoomAppReservedSigs tsX60)
|
||||
{
|
||||
return tsX60.ExtenderZoomRoomAppReservedSigs.ZoomRoomActiveFeedback.BoolValue;
|
||||
}
|
||||
|
||||
if (Panel is TswX70Base tsX70)
|
||||
{
|
||||
return tsX70.ExtenderZoomRoomAppReservedSigs.ZoomRoomActiveFeedback.BoolValue;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
Feedbacks = new FeedbackCollection<Feedback>
|
||||
{
|
||||
AppUrlFeedback, QrCodeUrlFeedback, McServerUrlFeedback, UserCodeFeedback
|
||||
};
|
||||
|
||||
ZoomFeedbacks = new FeedbackCollection<Feedback> {
|
||||
AppOpenFeedback, _zoomInCallFeedback, _zoomIncomingCallFeedback
|
||||
};
|
||||
|
||||
RegisterForExtenders();
|
||||
}
|
||||
|
||||
public void UpdateTheme(string theme)
|
||||
{
|
||||
localConfig.Theme = theme;
|
||||
|
||||
var props = JToken.FromObject(localConfig);
|
||||
|
||||
var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault((d) => d.Key == Key);
|
||||
|
||||
if (deviceConfig == null) { return; }
|
||||
|
||||
deviceConfig.Properties = props;
|
||||
|
||||
ConfigWriter.UpdateDeviceConfig(deviceConfig);
|
||||
}
|
||||
|
||||
private void RegisterForExtenders()
|
||||
{
|
||||
if (Panel is TswXX70Base x70Panel)
|
||||
{
|
||||
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}");
|
||||
|
||||
UpdateZoomFeedbacks();
|
||||
|
||||
if (!x70Panel.ExtenderApplicationControlReservedSigs.HideOpenedApplicationFeedback.BoolValue)
|
||||
{
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.ShowButtonToolbar();
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.Button2On();
|
||||
}
|
||||
else
|
||||
{
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.HideButtonToolbar();
|
||||
x70Panel.ExtenderButtonToolbarReservedSigs.Button2Off();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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}");
|
||||
|
||||
if (a.Sig.Number == x70Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.Number)
|
||||
{
|
||||
ZoomIncomingCallFeedback.FireUpdate();
|
||||
}
|
||||
else if (a.Sig.Number == x70Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomActiveFeedback.Number)
|
||||
{
|
||||
ZoomInCallFeedback.FireUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
x70Panel.ExtenderEthernetReservedSigs.DeviceExtenderSigChange += (e, a) =>
|
||||
{
|
||||
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}");
|
||||
|
||||
var handler = DeviceInfoChanged;
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
handler(this, new DeviceInfoEventArgs(DeviceInfo));
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (Panel is TswX60WithZoomRoomAppReservedSigs x60withZoomApp)
|
||||
{
|
||||
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}");
|
||||
|
||||
if (a.Sig.Number == x60withZoomApp.ExtenderApplicationControlReservedSigs.HideOpenApplicationFeedback.Number)
|
||||
{
|
||||
AppOpenFeedback.FireUpdate();
|
||||
}
|
||||
};
|
||||
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}");
|
||||
|
||||
if (a.Sig.Number == x60withZoomApp.ExtenderZoomRoomAppReservedSigs.ZoomRoomIncomingCallFeedback.Number)
|
||||
{
|
||||
ZoomIncomingCallFeedback.FireUpdate();
|
||||
}
|
||||
else if (a.Sig.Number == x60withZoomApp.ExtenderZoomRoomAppReservedSigs.ZoomRoomActiveFeedback.Number)
|
||||
{
|
||||
ZoomInCallFeedback.FireUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
x60withZoomApp.ExtenderEthernetReservedSigs.DeviceExtenderSigChange += (e, a) =>
|
||||
{
|
||||
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}");
|
||||
|
||||
var handler = DeviceInfoChanged;
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
handler(this, new DeviceInfoEventArgs(DeviceInfo));
|
||||
};
|
||||
|
||||
x60withZoomApp.ExtenderZoomRoomAppReservedSigs.Use();
|
||||
x60withZoomApp.ExtenderApplicationControlReservedSigs.Use();
|
||||
x60withZoomApp.ExtenderEthernetReservedSigs.Use();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CustomActivate()
|
||||
{
|
||||
var appMessenger = new ITswAppControlMessenger($"appControlMessenger-{Key}", $"/device/{Key}", this);
|
||||
|
||||
var zoomMessenger = new ITswZoomControlMessenger($"zoomControlMessenger-{Key}", $"/device/{Key}", this);
|
||||
|
||||
var themeMessenger = new ThemeMessenger($"themeMessenger-{Key}", $"/device/{Key}", this);
|
||||
|
||||
var mc = DeviceManager.AllDevices.OfType<IMobileControl>().FirstOrDefault();
|
||||
|
||||
if (mc == null)
|
||||
{
|
||||
return base.CustomActivate();
|
||||
}
|
||||
|
||||
if (!(Panel is TswXX70Base) && !(Panel is TswX60WithZoomRoomAppReservedSigs))
|
||||
{
|
||||
mc.AddDeviceMessenger(themeMessenger);
|
||||
|
||||
return base.CustomActivate();
|
||||
}
|
||||
|
||||
mc.AddDeviceMessenger(appMessenger);
|
||||
mc.AddDeviceMessenger(zoomMessenger);
|
||||
mc.AddDeviceMessenger(themeMessenger);
|
||||
|
||||
return base.CustomActivate();
|
||||
}
|
||||
|
||||
|
||||
protected override void ExtenderSystemReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, this, $"System Device Extender args: ${args.Event}:${args.Sig}");
|
||||
}
|
||||
|
||||
protected override void SetupPanelDrivers(string roomKey)
|
||||
{
|
||||
AppUrlFeedback.LinkInputSig(Panel.StringInput[1]);
|
||||
QrCodeUrlFeedback.LinkInputSig(Panel.StringInput[2]);
|
||||
McServerUrlFeedback.LinkInputSig(Panel.StringInput[3]);
|
||||
UserCodeFeedback.LinkInputSig(Panel.StringInput[4]);
|
||||
|
||||
Panel.OnlineStatusChange += (sender, args) =>
|
||||
{
|
||||
UpdateFeedbacks();
|
||||
|
||||
this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue);
|
||||
|
||||
Panel.StringInput[1].StringValue = AppUrlFeedback.StringValue;
|
||||
Panel.StringInput[2].StringValue = QrCodeUrlFeedback.StringValue;
|
||||
Panel.StringInput[3].StringValue = McServerUrlFeedback.StringValue;
|
||||
Panel.StringInput[4].StringValue = UserCodeFeedback.StringValue;
|
||||
};
|
||||
}
|
||||
|
||||
private void SubscribeForMobileControlUpdates()
|
||||
{
|
||||
foreach (var dev in DeviceManager.AllDevices)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"{dev.Key}:{dev.GetType().Name}");
|
||||
}
|
||||
|
||||
var mcList = DeviceManager.AllDevices.OfType<MobileControlSystemController>().ToList();
|
||||
|
||||
if (mcList.Count == 0)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"No Mobile Control controller found");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// use first in list, since there should only be one.
|
||||
var mc = mcList[0];
|
||||
|
||||
var bridge = mc.GetRoomBridge(_config.DefaultRoomKey);
|
||||
|
||||
if (bridge == null)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"No Mobile Control bridge for {_config.DefaultRoomKey} found ");
|
||||
return;
|
||||
}
|
||||
|
||||
_bridge = bridge;
|
||||
|
||||
_bridge.UserCodeChanged += UpdateFeedbacks;
|
||||
_bridge.AppUrlChanged += (s, a) => {
|
||||
this.LogInformation("AppURL changed");
|
||||
SetAppUrl(_bridge.AppUrl);
|
||||
UpdateFeedbacks(s, a);
|
||||
};
|
||||
|
||||
SetAppUrl(_bridge.AppUrl);
|
||||
}
|
||||
|
||||
public void SetAppUrl(string url)
|
||||
{
|
||||
_appUrl = url;
|
||||
AppUrlFeedback.FireUpdate();
|
||||
}
|
||||
|
||||
private void UpdateFeedbacks(object sender, EventArgs args)
|
||||
{
|
||||
UpdateFeedbacks();
|
||||
}
|
||||
|
||||
private void UpdateFeedbacks()
|
||||
{
|
||||
foreach (var feedback in Feedbacks) { this.LogDebug("Updating {feedbackKey}", feedback.Key); feedback.FireUpdate(); }
|
||||
}
|
||||
|
||||
private void UpdateZoomFeedbacks()
|
||||
{
|
||||
foreach (var feedback in ZoomFeedbacks)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"Updating {feedback.Key}");
|
||||
feedback.FireUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void HideOpenApp()
|
||||
{
|
||||
if (Panel is TswX70Base x70Panel)
|
||||
{
|
||||
x70Panel.ExtenderApplicationControlReservedSigs.HideOpenedApplication();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Panel is TswX60BaseClass x60Panel)
|
||||
{
|
||||
x60Panel.ExtenderApplicationControlReservedSigs.HideOpenApplication();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenApp()
|
||||
{
|
||||
if (Panel is TswX70Base x70Panel)
|
||||
{
|
||||
x70Panel.ExtenderApplicationControlReservedSigs.OpenApplication();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Panel is TswX60WithZoomRoomAppReservedSigs)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, this, $"X60 panel does not support zoom app");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseOpenApp()
|
||||
{
|
||||
if (Panel is TswX70Base x70Panel)
|
||||
{
|
||||
x70Panel.ExtenderApplicationControlReservedSigs.CloseOpenedApplication();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Panel is TswX60WithZoomRoomAppReservedSigs x60Panel)
|
||||
{
|
||||
x60Panel.ExtenderApplicationControlReservedSigs.CloseOpenedApplication();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void EndZoomCall()
|
||||
{
|
||||
if (Panel is TswX70Base x70Panel)
|
||||
{
|
||||
x70Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomEndCall();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Panel is TswX60WithZoomRoomAppReservedSigs x60Panel)
|
||||
{
|
||||
x60Panel.ExtenderZoomRoomAppReservedSigs.ZoomRoomEndCall();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateDeviceInfo()
|
||||
{
|
||||
if (Panel is TswXX70Base x70Panel)
|
||||
{
|
||||
DeviceInfo.MacAddress = x70Panel.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;
|
||||
DeviceInfo.IpAddress = x70Panel.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;
|
||||
|
||||
var handler = DeviceInfoChanged;
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
handler(this, new DeviceInfoEventArgs(DeviceInfo));
|
||||
}
|
||||
|
||||
if (Panel is TswX60WithZoomRoomAppReservedSigs x60Panel)
|
||||
{
|
||||
DeviceInfo.MacAddress = x60Panel.ExtenderEthernetReservedSigs.MacAddressFeedback.StringValue;
|
||||
DeviceInfo.IpAddress = x60Panel.ExtenderEthernetReservedSigs.IpAddressFeedback.StringValue;
|
||||
|
||||
var handler = DeviceInfoChanged;
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
handler(this, new DeviceInfoEventArgs(DeviceInfo));
|
||||
}
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, this, $"MAC: {DeviceInfo.MacAddress} IP: {DeviceInfo.IpAddress}");
|
||||
}
|
||||
}
|
||||
|
||||
public class MobileControlTouchpanelControllerFactory : EssentialsPluginDeviceFactory<MobileControlTouchpanelController>
|
||||
{
|
||||
public MobileControlTouchpanelControllerFactory()
|
||||
{
|
||||
TypeNames = new List<string>() { "mccrestronapp", "mctsw550", "mctsw750", "mctsw1050", "mctsw560", "mctsw760", "mctsw1060", "mctsw570", "mctsw770", "mcts770", "mctsw1070", "mcts1070", "mcxpanel" };
|
||||
MinimumEssentialsFrameworkVersion = "2.0.0";
|
||||
}
|
||||
|
||||
public override EssentialsDevice BuildDevice(DeviceConfig dc)
|
||||
{
|
||||
var comm = CommFactory.GetControlPropertiesConfig(dc);
|
||||
var props = JsonConvert.DeserializeObject<MobileControlTouchpanelProperties>(dc.Properties.ToString());
|
||||
|
||||
var panel = GetPanelForType(dc.Type, comm.IpIdInt, props.ProjectName);
|
||||
|
||||
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.LogMessage(Serilog.Events.LogEventLevel.Debug, "Factory Attempting to create new MobileControlTouchpanelController");
|
||||
|
||||
var panelController = new MobileControlTouchpanelController(dc.Key, dc.Name, panel, props);
|
||||
|
||||
return panelController;
|
||||
}
|
||||
|
||||
private BasicTriListWithSmartObject GetPanelForType(string type, uint id, string projectName)
|
||||
{
|
||||
type = type.ToLower().Replace("mc", "");
|
||||
try
|
||||
{
|
||||
if (type == "crestronapp")
|
||||
{
|
||||
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
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "WARNING: Cannot create TSW controller with type '{0}'", type);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "WARNING: Cannot create TSW base class. Panel will not function: {0}", e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Essentials.Core;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.TouchPanel
|
||||
{
|
||||
public class MobileControlTouchpanelProperties : CrestronTouchpanelPropertiesConfig
|
||||
{
|
||||
[JsonProperty("useDirectServer")]
|
||||
public bool UseDirectServer { get; set; } = false;
|
||||
|
||||
[JsonProperty("zoomRoomController")]
|
||||
public bool ZoomRoomController { get; set; } = false;
|
||||
|
||||
[JsonProperty("buttonToolbarTimeoutInS")]
|
||||
public ushort ButtonToolbarTimoutInS { get; set; } = 0;
|
||||
|
||||
[JsonProperty("theme")]
|
||||
public string Theme { get; set; } = "light";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.AppServer;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
|
||||
namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
public class ThemeMessenger : MessengerBase
|
||||
{
|
||||
private readonly ITheme _tpDevice;
|
||||
|
||||
public ThemeMessenger(string key, string path, ITheme device) : base(key, path, device as Device)
|
||||
{
|
||||
_tpDevice = device;
|
||||
}
|
||||
|
||||
protected override void RegisterActions()
|
||||
{
|
||||
AddAction("/fullStatus", (id, content) =>
|
||||
{
|
||||
PostStatusMessage(new ThemeUpdateMessage { Theme = _tpDevice.Theme });
|
||||
});
|
||||
|
||||
AddAction("/saveTheme", (id, content) =>
|
||||
{
|
||||
var theme = content.ToObject<MobileControlSimpleContent<string>>();
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "Setting theme to {theme}", this, theme.Value);
|
||||
_tpDevice.UpdateTheme(theme.Value);
|
||||
|
||||
PostStatusMessage(JToken.FromObject(new {theme = theme.Value}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class ThemeUpdateMessage:DeviceStateMessageBase
|
||||
{
|
||||
[JsonProperty("theme")]
|
||||
public string Theme { get; set; }
|
||||
}
|
||||
}
|
||||
150
src/PepperDash.Essentials.MobileControl/TransmitMessage.cs
Normal file
150
src/PepperDash.Essentials.MobileControl/TransmitMessage.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
using PepperDash.Essentials.Core.Queues;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using WebSocketSharp;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
public class TransmitMessage : IQueueMessage
|
||||
{
|
||||
private readonly WebSocket _ws;
|
||||
private readonly object msgToSend;
|
||||
|
||||
public TransmitMessage(object msg, WebSocket ws)
|
||||
{
|
||||
_ws = ws;
|
||||
msgToSend = msg;
|
||||
}
|
||||
|
||||
public TransmitMessage(DeviceStateMessageBase msg, WebSocket ws)
|
||||
{
|
||||
_ws = ws;
|
||||
msgToSend = msg;
|
||||
}
|
||||
|
||||
#region Implementation of IQueueMessage
|
||||
|
||||
public void Dispatch()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
//Debug.Console(2, "Dispatching message type: {0}", msgToSend.GetType());
|
||||
|
||||
//Debug.Console(2, "Message: {0}", msgToSend.ToString());
|
||||
|
||||
//var messageToSend = JObject.FromObject(msgToSend);
|
||||
|
||||
if (_ws != null && _ws.IsAlive)
|
||||
{
|
||||
var message = JsonConvert.SerializeObject(msgToSend, Formatting.None,
|
||||
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Converters = { new IsoDateTimeConverter() } });
|
||||
|
||||
Debug.Console(2, "Message TX: {0}", message);
|
||||
|
||||
_ws.Send(message);
|
||||
}
|
||||
else if (_ws == null)
|
||||
{
|
||||
Debug.Console(1, "Cannot send. No client.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(0, Debug.ErrorLogLevel.Error, "Caught an exception in the Transmit Processor {0}\r{1}\r{2}", ex.Message, ex.InnerException, ex.StackTrace);
|
||||
Debug.Console(2, Debug.ErrorLogLevel.Error, "Stack Trace: {0}", ex.StackTrace);
|
||||
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
Debug.Console(0, Debug.ErrorLogLevel.Error, "Inner Exception: {0}", ex.InnerException.Message);
|
||||
Debug.Console(2, Debug.ErrorLogLevel.Error, "Stack Trace: {0}", ex.InnerException.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
#if SERIES4
|
||||
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
|
||||
|
||||
public void Dispatch()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Debug.Console(2, "Message: {0}", msgToSend.ToString());
|
||||
|
||||
if (_server != null)
|
||||
{
|
||||
Debug.Console(2, _server, Debug.ErrorLogLevel.Notice, "Dispatching message type: {0}", msgToSend.GetType());
|
||||
|
||||
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;
|
||||
|
||||
Debug.Console(2, _server, "Message TX To Client ID: {0} Message: {1}", clientId, message);
|
||||
|
||||
_server.SendMessageToClient(clientId, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
_server.SendMessageToAllClients(message);
|
||||
|
||||
Debug.Console(2, "Message TX To Clients: {0}", message);
|
||||
}
|
||||
}
|
||||
else if (_server == null)
|
||||
{
|
||||
Debug.Console(1, "Cannot send. No server.");
|
||||
}
|
||||
}
|
||||
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.Console(0, Debug.ErrorLogLevel.Error, "Caught an exception in the Transmit Processor {0}", ex.Message);
|
||||
Debug.Console(2, Debug.ErrorLogLevel.Error, "Stack Trace: {0}", ex.StackTrace);
|
||||
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
Debug.Console(0, Debug.ErrorLogLevel.Error, "----\r\n{0}", ex.InnerException.Message);
|
||||
Debug.Console(2, Debug.ErrorLogLevel.Error, "Stack Trace: {0}", ex.InnerException.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Essentials.AppServer
|
||||
{
|
||||
public class UserCodeChangedContent
|
||||
{
|
||||
[JsonProperty("userCode")]
|
||||
public string UserCode { get; set; }
|
||||
|
||||
[JsonProperty("qrChecksum", NullValueHandling = NullValueHandling.Include)]
|
||||
public string QrChecksum { get; set; }
|
||||
}
|
||||
}
|
||||
76
src/PepperDash.Essentials.MobileControl/Volumes.cs
Normal file
76
src/PepperDash.Essentials.MobileControl/Volumes.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PepperDash.Essentials.Room.MobileControl
|
||||
{
|
||||
public class Volumes
|
||||
{
|
||||
[JsonProperty("master", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Volume Master { get; set; }
|
||||
|
||||
[JsonProperty("auxFaders", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Dictionary<string, Volume> AuxFaders { get; set; }
|
||||
|
||||
[JsonProperty("numberOfAuxFaders", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public int? NumberOfAuxFaders { get; set; }
|
||||
|
||||
public Volumes()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class Volume
|
||||
{
|
||||
[JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Key { get; set; }
|
||||
|
||||
[JsonProperty("level", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public int? Level { get; set; }
|
||||
|
||||
[JsonProperty("muted", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? Muted { get; set; }
|
||||
|
||||
[JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Label { get; set; }
|
||||
|
||||
[JsonProperty("hasMute", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasMute { get; set; }
|
||||
|
||||
[JsonProperty("hasPrivacyMute", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? HasPrivacyMute { get; set; }
|
||||
|
||||
[JsonProperty("privacyMuted", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? PrivacyMuted { get; set; }
|
||||
|
||||
|
||||
[JsonProperty("muteIcon", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string MuteIcon { get; set; }
|
||||
|
||||
public Volume(string key, int level, bool muted, string label, bool hasMute, string muteIcon)
|
||||
: this(key)
|
||||
{
|
||||
Level = level;
|
||||
Muted = muted;
|
||||
Label = label;
|
||||
HasMute = hasMute;
|
||||
MuteIcon = muteIcon;
|
||||
}
|
||||
|
||||
public Volume(string key, int level)
|
||||
: this(key)
|
||||
{
|
||||
Level = level;
|
||||
}
|
||||
|
||||
public Volume(string key, bool muted)
|
||||
: this(key)
|
||||
{
|
||||
Muted = muted;
|
||||
}
|
||||
|
||||
public Volume(string key)
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core.Web.RequestHandlers;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PepperDash.Essentials.WebApiHandlers
|
||||
{
|
||||
public class ActionPathsHandler : WebApiBaseRequestHandler
|
||||
{
|
||||
private readonly MobileControlSystemController mcController;
|
||||
public ActionPathsHandler(MobileControlSystemController controller) : base(true)
|
||||
{
|
||||
mcController = controller;
|
||||
}
|
||||
|
||||
protected override void HandleGet(HttpCwsContext context)
|
||||
{
|
||||
var response = JsonConvert.SerializeObject(new ActionPathsResponse(mcController));
|
||||
|
||||
context.Response.StatusCode = 200;
|
||||
context.Response.ContentType = "application/json";
|
||||
context.Response.Headers.Add("Content-Type", "application/json");
|
||||
context.Response.Write(response, false);
|
||||
context.Response.End();
|
||||
}
|
||||
}
|
||||
|
||||
public class ActionPathsResponse
|
||||
{
|
||||
[JsonIgnore]
|
||||
private readonly MobileControlSystemController mcController;
|
||||
|
||||
[JsonProperty("actionPaths")]
|
||||
public List<ActionPath> ActionPaths => mcController.GetActionDictionaryPaths().Select((path) => new ActionPath { MessengerKey = path.Item1, Path = path.Item2}).ToList();
|
||||
|
||||
public ActionPathsResponse(MobileControlSystemController mcController)
|
||||
{
|
||||
this.mcController = mcController;
|
||||
}
|
||||
}
|
||||
|
||||
public class ActionPath
|
||||
{
|
||||
[JsonProperty("messengerKey")]
|
||||
public string MessengerKey { get; set; }
|
||||
|
||||
[JsonProperty("path")]
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
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
|
||||
{
|
||||
public class MobileAuthRequestHandler : WebApiBaseRequestAsyncHandler
|
||||
{
|
||||
private readonly MobileControlSystemController mcController;
|
||||
|
||||
public MobileAuthRequestHandler(MobileControlSystemController controller) : base(true)
|
||||
{
|
||||
mcController = controller;
|
||||
}
|
||||
|
||||
protected override async Task HandlePost(HttpCwsContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var requestBody = EssentialsWebApiHelpers.GetRequestBody(context.Request);
|
||||
|
||||
var grantCode = JsonConvert.DeserializeObject<AuthorizationRequest>(requestBody);
|
||||
|
||||
if (string.IsNullOrEmpty(grantCode?.GrantCode))
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "Missing grant code");
|
||||
context.Response.StatusCode = 400;
|
||||
context.Response.StatusDescription = "Missing grant code";
|
||||
context.Response.End();
|
||||
return;
|
||||
}
|
||||
|
||||
var response = await mcController.ApiService.SendAuthorizationRequest(mcController.Host, grantCode.GrantCode, mcController.SystemUuid);
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, $"response received");
|
||||
if (response.Authorized)
|
||||
{
|
||||
mcController.RegisterSystemToServer();
|
||||
}
|
||||
|
||||
|
||||
context.Response.StatusCode = 200;
|
||||
var responseBody = JsonConvert.SerializeObject(response, Formatting.None);
|
||||
context.Response.ContentType = "application/json";
|
||||
context.Response.Headers.Add("Content-Type", "application/json");
|
||||
context.Response.Write(responseBody, false);
|
||||
context.Response.End();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Exception recieved authorizing system");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Web.RequestHandlers;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PepperDash.Essentials.WebApiHandlers
|
||||
{
|
||||
public class MobileInfoHandler : WebApiBaseRequestHandler
|
||||
{
|
||||
private readonly MobileControlSystemController mcController;
|
||||
public MobileInfoHandler(MobileControlSystemController controller) : base(true)
|
||||
{
|
||||
mcController = controller;
|
||||
}
|
||||
|
||||
protected override void HandleGet(HttpCwsContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = new InformationResponse(mcController);
|
||||
|
||||
context.Response.StatusCode = 200;
|
||||
context.Response.ContentType = "application/json";
|
||||
context.Response.Write(JsonConvert.SerializeObject(response), false);
|
||||
context.Response.End();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(1, $"exception showing mobile info: {ex.Message}");
|
||||
Debug.Console(2, $"stack trace: {ex.StackTrace}");
|
||||
|
||||
context.Response.StatusCode = 500;
|
||||
context.Response.End();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class InformationResponse
|
||||
{
|
||||
[JsonIgnore]
|
||||
private readonly MobileControlSystemController mcController;
|
||||
|
||||
[JsonProperty("edgeServer", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public MobileControlEdgeServer EdgeServer => mcController.Config.EnableApiServer ? new MobileControlEdgeServer(mcController) : null;
|
||||
|
||||
|
||||
[JsonProperty("directServer", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public MobileControlDirectServer DirectServer => mcController.Config.DirectServer.EnableDirectServer ? new MobileControlDirectServer(mcController.DirectServer) : null;
|
||||
|
||||
|
||||
public InformationResponse(MobileControlSystemController controller)
|
||||
{
|
||||
mcController = controller;
|
||||
}
|
||||
}
|
||||
|
||||
public class MobileControlEdgeServer
|
||||
{
|
||||
[JsonIgnore]
|
||||
private readonly MobileControlSystemController mcController;
|
||||
|
||||
[JsonProperty("serverAddress")]
|
||||
public string ServerAddress => mcController.Config == null ? "No Config" : mcController.Host;
|
||||
|
||||
[JsonProperty("systemName")]
|
||||
public string SystemName => mcController.RoomBridges.Count > 0 ? mcController.RoomBridges[0].RoomName : "No Config";
|
||||
|
||||
[JsonProperty("systemUrl")]
|
||||
public string SystemUrl => ConfigReader.ConfigObject.SystemUrl;
|
||||
|
||||
[JsonProperty("userCode")]
|
||||
public string UserCode => mcController.RoomBridges.Count > 0 ? mcController.RoomBridges[0].UserCode : "Not available";
|
||||
|
||||
[JsonProperty("connected")]
|
||||
public bool Connected => mcController.Connected;
|
||||
|
||||
[JsonProperty("secondsSinceLastAck")]
|
||||
public int SecondsSinceLastAck => (DateTime.Now - mcController.LastAckMessage).Seconds;
|
||||
|
||||
public MobileControlEdgeServer(MobileControlSystemController controller)
|
||||
{
|
||||
mcController = controller;
|
||||
}
|
||||
}
|
||||
|
||||
public class MobileControlDirectServer
|
||||
{
|
||||
[JsonIgnore]
|
||||
private readonly MobileControlWebsocketServer directServer;
|
||||
|
||||
[JsonProperty("userAppUrl")]
|
||||
public string UserAppUrl => $"{directServer.UserAppUrlPrefix}/[insert_client_token]";
|
||||
|
||||
[JsonProperty("serverPort")]
|
||||
public int ServerPort => directServer.Port;
|
||||
|
||||
[JsonProperty("tokensDefined")]
|
||||
public int TokensDefined => directServer.UiClients.Count;
|
||||
|
||||
[JsonProperty("clientsConnected")]
|
||||
public int ClientsConnected => directServer.ConnectedUiClientsCount;
|
||||
|
||||
[JsonProperty("clients")]
|
||||
public List<MobileControlDirectClient> Clients => directServer.UiClients.Select((c, i) => { return new MobileControlDirectClient(c, i, directServer.UserAppUrlPrefix); }).ToList();
|
||||
|
||||
public MobileControlDirectServer(MobileControlWebsocketServer server)
|
||||
{
|
||||
directServer = server;
|
||||
}
|
||||
}
|
||||
|
||||
public class MobileControlDirectClient
|
||||
{
|
||||
[JsonIgnore]
|
||||
private readonly UiClientContext context;
|
||||
|
||||
[JsonIgnore]
|
||||
private readonly string Key;
|
||||
|
||||
[JsonIgnore]
|
||||
private readonly int clientNumber;
|
||||
|
||||
[JsonIgnore]
|
||||
private readonly string urlPrefix;
|
||||
|
||||
[JsonProperty("clientNumber")]
|
||||
public string ClientNumber => $"{clientNumber}";
|
||||
|
||||
[JsonProperty("roomKey")]
|
||||
public string RoomKey => context.Token.RoomKey;
|
||||
|
||||
[JsonProperty("touchpanelKey")]
|
||||
public string TouchpanelKey => context.Token.TouchpanelKey;
|
||||
|
||||
[JsonProperty("url")]
|
||||
public string Url => $"{urlPrefix}{Key}";
|
||||
|
||||
[JsonProperty("token")]
|
||||
public string Token => Key;
|
||||
|
||||
[JsonProperty("connected")]
|
||||
public bool Connected => context.Client == null ? false : context.Client.Context.WebSocket.IsAlive;
|
||||
|
||||
[JsonProperty("duration")]
|
||||
public double Duration => context.Client == null ? 0 : context.Client.ConnectedDuration.TotalSeconds;
|
||||
|
||||
public MobileControlDirectClient(KeyValuePair<string, UiClientContext> clientContext, int index, string urlPrefix)
|
||||
{
|
||||
context = clientContext.Value;
|
||||
Key = clientContext.Key;
|
||||
clientNumber = index;
|
||||
this.urlPrefix = urlPrefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Web.RequestHandlers;
|
||||
using PepperDash.Essentials.Core.Web;
|
||||
|
||||
namespace PepperDash.Essentials.WebApiHandlers
|
||||
{
|
||||
public class UiClientHandler : WebApiBaseRequestHandler
|
||||
{
|
||||
private readonly MobileControlWebsocketServer server;
|
||||
public UiClientHandler(MobileControlWebsocketServer directServer) : base(true)
|
||||
{
|
||||
server = directServer;
|
||||
}
|
||||
|
||||
protected override void HandlePost(HttpCwsContext context)
|
||||
{
|
||||
var req = context.Request;
|
||||
var res = context.Response;
|
||||
var body = EssentialsWebApiHelpers.GetRequestBody(req);
|
||||
|
||||
var request = JsonConvert.DeserializeObject<ClientRequest>(body);
|
||||
|
||||
var response = new ClientResponse();
|
||||
|
||||
if (string.IsNullOrEmpty(request?.RoomKey))
|
||||
{
|
||||
response.Error = "roomKey is required";
|
||||
|
||||
res.StatusCode = 400;
|
||||
res.ContentType = "application/json";
|
||||
res.Headers.Add("Content-Type", "application/json");
|
||||
res.Write(JsonConvert.SerializeObject(response), false);
|
||||
res.End();
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(request.GrantCode))
|
||||
{
|
||||
response.Error = "grantCode is required";
|
||||
|
||||
res.StatusCode = 400;
|
||||
res.ContentType = "application/json";
|
||||
res.Headers.Add("Content-Type", "application/json");
|
||||
res.Write(JsonConvert.SerializeObject(response), false);
|
||||
res.End();
|
||||
return;
|
||||
}
|
||||
|
||||
var (token, path) = server.ValidateGrantCode(request.GrantCode, request.RoomKey);
|
||||
|
||||
response.Token = token;
|
||||
response.Path = path;
|
||||
|
||||
res.StatusCode = 200;
|
||||
res.ContentType = "application/json";
|
||||
res.Headers.Add("Content-Type", "application/json");
|
||||
res.Write(JsonConvert.SerializeObject(response), false);
|
||||
res.End();
|
||||
}
|
||||
|
||||
protected override void HandleDelete(HttpCwsContext context)
|
||||
{
|
||||
var req = context.Request;
|
||||
var res = context.Response;
|
||||
var body = EssentialsWebApiHelpers.GetRequestBody(req);
|
||||
|
||||
var request = JsonConvert.DeserializeObject<ClientRequest>(body);
|
||||
|
||||
|
||||
|
||||
if (string.IsNullOrEmpty(request?.Token))
|
||||
{
|
||||
var response = new ClientResponse
|
||||
{
|
||||
Error = "token is required"
|
||||
};
|
||||
|
||||
res.StatusCode = 400;
|
||||
res.ContentType = "application/json";
|
||||
res.Headers.Add("Content-Type", "application/json");
|
||||
res.Write(JsonConvert.SerializeObject(response), false);
|
||||
res.End();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!server.UiClients.TryGetValue(request.Token, out UiClientContext clientContext))
|
||||
{
|
||||
var response = new ClientResponse
|
||||
{
|
||||
Error = $"Unable to find client with token: {request.Token}"
|
||||
};
|
||||
|
||||
res.StatusCode = 200;
|
||||
res.ContentType = "application/json";
|
||||
res.Headers.Add("Content-Type", "application/json");
|
||||
res.Write(JsonConvert.SerializeObject(response), false);
|
||||
res.End();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (clientContext.Client != null && clientContext.Client.Context.WebSocket.IsAlive)
|
||||
{
|
||||
clientContext.Client.Context.WebSocket.Close(WebSocketSharp.CloseStatusCode.Normal, "Token removed from server");
|
||||
}
|
||||
|
||||
var path = server.WsPath + request.Token;
|
||||
|
||||
if (!server.Server.RemoveWebSocketService(path))
|
||||
{
|
||||
Debug.Console(0, $"Unable to remove client with token {request.Token}");
|
||||
|
||||
var response = new ClientResponse
|
||||
{
|
||||
Error = $"Unable to remove client with token {request.Token}"
|
||||
};
|
||||
|
||||
res.StatusCode = 500;
|
||||
res.ContentType = "application/json";
|
||||
res.Headers.Add("Content-Type", "application/json");
|
||||
res.Write(JsonConvert.SerializeObject(response), false);
|
||||
res.End();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
server.UiClients.Remove(request.Token);
|
||||
|
||||
server.UpdateSecret();
|
||||
|
||||
res.StatusCode = 200;
|
||||
res.End();
|
||||
}
|
||||
}
|
||||
|
||||
public class ClientRequest
|
||||
{
|
||||
[JsonProperty("roomKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string RoomKey { get; set; }
|
||||
|
||||
[JsonProperty("grantCode", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string GrantCode { get; set; }
|
||||
|
||||
[JsonProperty("token", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Token { get; set; }
|
||||
}
|
||||
|
||||
public class ClientResponse
|
||||
{
|
||||
[JsonProperty("error", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Error { get; set; }
|
||||
|
||||
[JsonProperty("token", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Token { get; set; }
|
||||
|
||||
[JsonProperty("path", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Essentials.Core;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
internal class WebSocketServerSecretProvider : CrestronLocalSecretsProvider
|
||||
{
|
||||
public WebSocketServerSecretProvider(string key)
|
||||
: base(key)
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
}
|
||||
|
||||
public class WebSocketServerSecret : ISecret
|
||||
{
|
||||
public ISecretProvider Provider { get; private set; }
|
||||
|
||||
public string Key { get; private set; }
|
||||
|
||||
public object Value { get; private set; }
|
||||
|
||||
public WebSocketServerSecret(string key, object value, ISecretProvider provider)
|
||||
{
|
||||
Key = key;
|
||||
Value = JsonConvert.SerializeObject(value);
|
||||
Provider = provider;
|
||||
}
|
||||
|
||||
public ServerTokenSecrets DeserializeSecret()
|
||||
{
|
||||
return JsonConvert.DeserializeObject<ServerTokenSecrets>(Value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user