diff --git a/Pepperdash Core/Pepperdash Core/CrestronWebServer/GenericCwsBase.cs b/Pepperdash Core/Pepperdash Core/CrestronWebServer/GenericCwsBase.cs
new file mode 100644
index 0000000..9446e6a
--- /dev/null
+++ b/Pepperdash Core/Pepperdash Core/CrestronWebServer/GenericCwsBase.cs
@@ -0,0 +1,266 @@
+using System;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.WebScripting;
+
+namespace PepperDash.Core
+{
+ public class GenericCwsBase : Device
+ {
+ private const string SplusKey = "Uninitialized CWS Server";
+ private const string DefaultBasePath = "/api";
+
+ private const uint DebugTrace = 0;
+ private const uint DebugInfo = 1;
+ private const uint DebugVerbose = 2;
+
+ private HttpCwsServer _server;
+ private readonly CCriticalSection _serverLock = new CCriticalSection();
+
+ ///
+ /// CWS base path, will default to "/api" if not set via initialize method
+ ///
+ public string BasePath { get; private set; }
+
+ ///
+ /// Indicates CWS is registered with base path
+ ///
+ public bool IsRegistered { get; private set; }
+
+ ///
+ /// Constructor for S+. Make sure to set necessary properties using init method
+ ///
+ public GenericCwsBase()
+ : base(SplusKey)
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
+ CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;
+ }
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ public GenericCwsBase(string key, string basePath)
+ : base(key)
+ {
+
+ BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericCwsBase(string key, string name, string basePath)
+ : base(key, name)
+ {
+
+ BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
+ }
+
+ ///
+ /// Program status event handler
+ ///
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType != eProgramStatusEventType.Stopping)
+ return;
+
+ Debug.Console(DebugInfo, this, "Program stopping. Disabling Server");
+
+ Stop();
+ }
+
+ ///
+ /// Ethernet event handler
+ ///
+ ///
+ void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
+ {
+ // Re-enable the server if the link comes back up and the status should be connected
+ if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp
+ && IsRegistered)
+ {
+ Debug.Console(DebugInfo, this, "Ethernet link up. Starting server");
+
+ Start();
+ }
+ }
+
+ ///
+ /// Initializes CWS class
+ ///
+ public void Initialize(string key, string basePath)
+ {
+ Key = key;
+ BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
+ }
+
+ ///
+ /// Starts CWS instance
+ ///
+ public void Start()
+ {
+ try
+ {
+ _serverLock.Enter();
+
+ if (_server != null)
+ {
+ Debug.Console(DebugInfo, this, "Server has already been started");
+ return;
+ }
+
+ Debug.Console(DebugInfo, this, "Starting server");
+
+ _server = new HttpCwsServer(BasePath)
+ {
+ HttpRequestHandler = new RequestHandlerUnknown()
+ };
+
+ IsRegistered = _server.Register();
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(DebugInfo, this, "Start Exception Message: {0}", ex.Message);
+ Debug.Console(DebugVerbose, this, "Start Exception StackTrace: {0}", ex.StackTrace);
+ if (ex.InnerException != null)
+ Debug.Console(DebugVerbose, this, "Start Exception InnerException: {0}", ex.InnerException);
+ }
+ finally
+ {
+ _serverLock.Leave();
+ }
+ }
+
+ ///
+ /// Stop CWS instance
+ ///
+ public void Stop()
+ {
+ try
+ {
+ _serverLock.Enter();
+
+ if (_server == null)
+ {
+ Debug.Console(DebugInfo, this, "Servier has already been stopped");
+ return;
+ }
+
+ if (_server.Unregister())
+ {
+ IsRegistered = false;
+ }
+
+ Dispose(true);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(DebugInfo, this, "ServerStop Exception Message: {0}", ex.Message);
+ Debug.Console(DebugVerbose, this, "ServerStop Exception StackTrace: {0}", ex.StackTrace);
+ if (ex.InnerException != null)
+ Debug.Console(DebugVerbose, this, "ServerStop Exception InnerException: {0}", ex.InnerException);
+ }
+ finally
+ {
+ _serverLock.Leave();
+ }
+ }
+
+ ///
+ /// Received request handler
+ ///
+ ///
+ /// This is here for development and testing
+ ///
+ ///
+ ///
+ public void ReceivedRequestEventHandler(object sender, HttpCwsRequestEventArgs args)
+ {
+ try
+ {
+ // TODO [ ] Add logic for received requests
+ Debug.Console(DebugInfo, this, @"RecieveRequestEventHandler
+Method: {0}
+Path: {1}
+PathInfo: {2}
+PhysicalPath: {3}
+ContentType: {4}
+RawUrl: {5}
+Url: {6}
+UserAgent: {7}
+UserHostAddress: {8}
+UserHostName: {9}",
+ args.Context.Request.HttpMethod,
+ args.Context.Request.Path,
+ args.Context.Request.PathInfo,
+ args.Context.Request.PhysicalPath,
+ args.Context.Request.ContentType,
+ args.Context.Request.RawUrl,
+ args.Context.Request.Url,
+ args.Context.Request.UserAgent,
+ args.Context.Request.UserHostAddress,
+ args.Context.Request.UserHostName);
+
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(DebugInfo, this, "ReceivedRequestEventHandler Exception Message: {0}", ex.Message);
+ Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception StackTrace: {0}", ex.StackTrace);
+ if (ex.InnerException != null)
+ Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception InnerException: {0}", ex.InnerException);
+ }
+ }
+
+ ///
+ /// Tracks if CWS is disposed
+ ///
+ public bool Disposed
+ {
+ get
+ {
+ return (_server == null);
+ }
+ }
+
+ ///
+ /// Disposes CWS instance
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ CrestronEnvironment.GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Disposes CWS instance
+ ///
+ ///
+ protected void Dispose(bool disposing)
+ {
+ if (Disposed)
+ {
+ Debug.Console(DebugInfo, this, "Server has already been disposed");
+ return;
+ }
+
+ if (!disposing) return;
+
+ if (_server != null)
+ {
+ _server.Dispose();
+ _server = null;
+ }
+ }
+
+ ~GenericCwsBase()
+ {
+ Dispose(true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/CrestronWebServer/RequestHandlerUnknown.cs b/Pepperdash Core/Pepperdash Core/CrestronWebServer/RequestHandlerUnknown.cs
new file mode 100644
index 0000000..5a2adac
--- /dev/null
+++ b/Pepperdash Core/Pepperdash Core/CrestronWebServer/RequestHandlerUnknown.cs
@@ -0,0 +1,16 @@
+using Crestron.SimplSharp.WebScripting;
+
+namespace PepperDash.Core
+{
+ public class RequestHandlerUnknown : IHttpCwsHandler
+ {
+ public void ProcessRequest(HttpCwsContext context)
+ {
+ // TODO [ ] Modify unknown request handler
+ context.Response.StatusCode = 418;
+ context.Response.StatusDescription = "I'm a teapot";
+ context.Response.ContentType = "application/json";
+ context.Response.Write(string.Format("{0} {1}", context.Request.HttpMethod, context.Request.RawUrl), true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj
index 86ae71d..8e49d82 100644
--- a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj
+++ b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj
@@ -51,6 +51,10 @@
False
..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll
+
+ False
+ ..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCWSHelperInterface.dll
+
False
..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll
@@ -89,6 +93,8 @@
+
+