Compare commits

...

134 Commits

Author SHA1 Message Date
Heath Volmer
ca9da1ef79 Merge pull request #7 in PEC/essentials from feature/ecs-747 to development
* commit 'e0bfb8f0915de0597a05bc658bf9b9a235566b38':
  Fixed issue preventing lighting scene feedback from updating correctly.
  Bug fix for LutronQuantumArea class that caused crash when debug level was set to 2 and data was received from device.  Updates EssentialsEnvironmentDriver to only include environment devices in UI container columns if a matching UI driver can be constructed (ignores DIN8SW8)
2018-06-26 14:51:18 -04:00
Neil Dorin
e0bfb8f091 Fixed issue preventing lighting scene feedback from updating correctly. 2018-06-26 11:53:18 -06:00
Neil Dorin
97db1f35a1 Bug fix for LutronQuantumArea class that caused crash when debug level was set to 2 and data was received from device. Updates EssentialsEnvironmentDriver to only include environment devices in UI container columns if a matching UI driver can be constructed (ignores DIN8SW8) 2018-06-26 10:41:43 -06:00
Heath Volmer
2b6a13271f Renabled GoWithLoad, lost in merge 2018-06-15 15:34:52 -06:00
Heath Volmer
4686799371 Compiled, for review 2018-06-15 14:30:34 -06:00
Heath Volmer
cb750a2fd0 Merge branch 'development' of http://code.pepperdash.net/scm/pec/essentials into development 2018-06-15 14:11:17 -06:00
Heath Volmer
6d81e8d21e Merge branch 'feature/ecs-684' into development 2018-06-15 14:05:00 -06:00
Heath Volmer
600b9f11ff Merge pull request #6 in PEC/essentials from feature/ecs-711 to development
* commit 'c20036cfe95f0940d378dfa6db402cd61a48b54e':
  Updates to add IncludeInFusionRoomHealth config property for UI devices to allow them to be excluded from the Fusion error rollup.  Implement ICommunicationMonitor on EssentialsTouchpanelControler class. Updated Fusion class to better handle mapping digital online joins to ICommunicationMonitor classes.
2018-06-15 15:42:47 -04:00
Neil Dorin
c20036cfe9 Updates to add IncludeInFusionRoomHealth config property for UI devices to allow them to be excluded from the Fusion error rollup. Implement ICommunicationMonitor on EssentialsTouchpanelControler class. Updated Fusion class to better handle mapping digital online joins to ICommunicationMonitor classes. 2018-06-07 16:00:53 -06:00
Heath Volmer
005b3f0843 v 1.1.8; mob-303, fixed swapped template and system uuid fields in server delivered config 2018-06-06 11:09:58 -06:00
Neil Dorin
fbe5df84be Updated to allow for middle shade button to have label driven from config. 2018-06-05 16:47:07 -06:00
Heath Volmer
8e4c84dd02 Ignore tie lines if none present in config 2018-06-01 11:48:27 -06:00
Heath Volmer
dace2a57c2 1.1.7. Changed app server device types to match config tool: appServer, mobileControlBridge-ddvc01 2018-06-01 09:29:06 -06:00
Neil Dorin
3cf188f820 Completed Environment UI effort. Tested with EssentialsHuddleRoom type 2018-05-24 09:44:13 -06:00
Neil Dorin
80377a41d0 Environment Driver now showing and correct background subpage is appearing 2018-05-22 23:15:08 -06:00
Neil Dorin
a0ebc08838 Corrected issue in ShowSetupButtons method. Environment icon is displaying correctly when config defined. 2018-05-21 22:13:07 -06:00
Neil Dorin
0de4fbb69c Strange issue in HeaderDriver.SetupHeaderButtons with avDriver being null. Need to investigate further 2018-05-17 14:42:57 -06:00
Neil Dorin
c88b259c71 Significant refactoring of DeviceFactory for touchpanel device building. Moved SetupHeaderButtons() out of AV driver classes and into new EssentialsHeaderDriver class. 2018-05-17 12:33:20 -06:00
Heath Volmer
20660c561a v 1.1.6 2018-05-14 12:36:56 -06:00
Neil Dorin
b44613b91f Refactored method names for better consistency. Built out Din8sw8 and Din8sw8Output classes. Added logic for RelayControlledShade class to operate relays based on open/close/stop method calls 2018-05-03 10:23:30 -06:00
Heath Volmer
58de8ce8ad Will now identify in console when auth fails due to UUID or code 2018-05-01 21:06:59 -06:00
Neil Dorin
33f0a1fe32 More progress on relay based shade control 2018-05-01 16:45:18 -06:00
Heath Volmer
4b0f8abd9f 1.1.6 Added TryEnter to VTC room routing to prevent multiple source routes from accidentally running simultaneously (NYU bug); Restructuring MOBILEHTTPREQUEST to do post and get 2018-05-01 10:27:16 -06:00
Heath Volmer
4defea55f0 1.1.4, added clientAppUrl property to config, passed through to DDVC01 EISC 2018-04-27 14:44:54 -06:00
Neil Dorin
9c8f85555e Adds initial Shade interfaces and Shade Base Class 2018-04-27 10:28:41 -06:00
Heath Volmer
62e2c3d19f 1.1.1 Added MOBILEHTTPDEBUG command to help track failed http requests 2018-04-26 12:29:15 -06:00
Heath Volmer
e1c8b54434 1.1.0 Added verbose logging to http failure on MC initial connect 2018-04-26 09:46:15 -06:00
Neil Dorin
6d913e8a72 Updates to LutronQuantum.cs 2018-04-05 17:08:42 -06:00
Neil Dorin
afa1cff0e0 v1.0.48 - Updates all code that uses file paths to work on windows or linux platforms. Updates to add compatability for running on XiO Edge platform. 2018-03-29 10:36:22 -06:00
Neil Dorin
ce6cecbb79 Refactored how configuration, IR and SGD file path prefixes are handled, in order to be able to handle a different file structure if running on XiO Edge 2018-03-27 16:39:23 -06:00
Heath Volmer
69314bb1f1 Changed websocket sendAysnc to send in order to allow rapid messages to go out. Not sure if this is a valid fix, but it works 2018-03-22 16:45:14 -06:00
Neil Dorin
fb19b5894b Added lighting interfaces and base class 2018-03-22 16:07:18 -06:00
Neil Dorin
82fad55c1e Removed all HTTP Post logic from CotijaSystemController and switched to websockets for sending data to server 2018-03-21 12:59:41 -06:00
Heath Volmer
8d03e81431 Not sure of modification 2018-03-21 10:18:05 -06:00
Heath Volmer
fa8ea4cef6 Added mobileinfo command to help with mobile control debugging 2018-03-16 14:46:52 -06:00
Heath Volmer
97a44ffa4f 1.0.46 2018-03-13 18:34:05 -06:00
Heath Volmer
ee55be86e0 Fixed startup crash when device has out-of-range com port 2018-03-13 18:33:16 -06:00
Heath Volmer
ced1efdb47 Remove 'v' from assemblyVersion info object
, cotija
2018-03-12 16:21:41 -06:00
Neil Dorin
5129b19748 Adds RuntimeInfo class to InfoConfig and populates values when CotijaController class calls RegisterSystemToServer() 2018-03-12 15:19:31 -06:00
Heath Volmer
525881ebe1 Bugfixes: HDMD4k4x1 code around toggle-switching; add source list evaluation when pressing share 2018-03-09 15:40:02 -07:00
Heath Volmer
856a81ded4 Hotfix for TD - Null ref in incoming call. Thought this was fixed. 2018-03-09 09:10:25 -07:00
Heath Volmer
ef8298ccd6 Added Event locking to system registration to help prevent multiple registrations when network lost or dns changed. 2018-03-08 13:55:21 -07:00
Heath Volmer
cdbab152f6 System - server reconnecting ! 2018-03-07 14:09:37 -07:00
Heath Volmer
359bd508e5 Attached Parse method to websocket receive 2018-03-07 12:44:58 -07:00
Heath Volmer
62e5e6f176 Getting websocket -> cotija going 2018-03-07 12:33:10 -07:00
Heath Volmer
2d375ed2d7 Fixed null ref when in call changed and system is off 2018-03-05 09:55:27 -07:00
Heath Volmer
2e78191e33 Messages in SSE receive to help debug missing data - no progress yet 2018-03-02 13:32:48 -07:00
Heath Volmer
45ac17622a cotija fixes 2018-02-21 13:56:06 -07:00
Heath Volmer
e7e6ae98cf Added server URL and mobile user code 2018-02-16 14:20:02 -07:00
Heath Volmer
2fbd645bf0 Re-enabled cotija for huddle room: 2018-02-14 08:52:47 -07:00
Heath Volmer
2a4ed67bee DDVC bridge 2018-02-13 17:42:28 -07:00
Heath Volmer
17c448e309 Pulling in 670 branch changes 2018-02-08 12:35:08 -07:00
Heath Volmer
5afa626fb3 Removed thread/timer from touchpanel creation. Hopefully will fix disappearing panels 2018-02-07 16:29:06 -07:00
Heath Volmer
ba1e8646e5 HDCP disables from config on HDMD4x1. Appears to be working 2018-02-07 16:17:41 -07:00
Heath Volmer
88e82fb398 HDCP Disable in from config 2018-02-07 13:55:56 -07:00
Heath Volmer
53ee87c9be Initial successful tests of non-sharable source derouting and redraw of source list when in/out of call 2018-02-07 09:59:12 -07:00
Heath Volmer
032bb2b8e5 Added in source list filtering based on in-call and current mode of UI driver (call or present) 2018-02-06 17:03:11 -07:00
Heath Volmer
6ee56dd66c Loaded new PD.Core with proper cresnet and ipidTcp comm enums; Loaded spark room with HDMI4x1. Testing forthcoming: 2018-02-06 13:23:40 -07:00
Heath Volmer
d887b448c9 Added Crestron HDMD4x14kE switcher. Awaiting testing 2018-02-06 09:48:29 -07:00
Heath Volmer
f099c88352 Added framework classes for 4x1 2018-02-05 14:40:25 -07:00
Heath Volmer
f98a429b55 Building config from DDVC but no values are present... 2018-02-02 15:53:26 -07:00
Heath Volmer
645816c75f Trying to force config values to load in lieu of proper signalling from Simpl code 2018-02-02 15:34:37 -07:00
Heath Volmer
491fff2793 Rearranged how cotija bridge are brought up in relation to parent; adding ddvd01 bridge 2018-02-02 13:37:48 -07:00
Heath Volmer
952e7f4083 Trying to make bridge base happy with DDVC01 2018-02-01 15:41:36 -07:00
Heath Volmer
56c418580c Have multi-volume with recall ready for test. Pausing dependent on conversation with NYU 2018-01-31 12:36:57 -07:00
Heath Volmer
4134622b28 Added volume control switching to VTC1 class. Added volume zero/restore code to VTC1 and base classes. Related config 2018-01-31 10:58:43 -07:00
Heath Volmer
09b3f7c5e7 Preparing DDVCO1 cotija bridge to send configuration - incomplete 2018-01-30 09:19:51 -07:00
Heath Volmer
9fa2b22448 Adding ddvc01 properties from EISC into config objects 2018-01-29 19:22:08 -07:00
Heath Volmer
62d8190eef Restored missing release package cpz 2018-01-26 09:55:05 -07:00
Heath Volmer
19284b171a Post-merge 344, compile and warning cleanup 2018-01-26 09:46:53 -07:00
Neil Dorin
29c60548cc v1.0.34.* Updated Device Factory to consume portal generated config format 2018-01-23 16:12:38 -07:00
Heath Volmer
17f147b5e0 Added cresnet to comm config 2018-01-23 13:21:50 -07:00
Heath Volmer
7aa3d50cd6 Merge of ecs-342-neil, v.33 2018-01-23 10:14:35 -07:00
Neil Dorin
b85abe1b79 v1.0.33.* -Fixed issues with versiport configuration for pull up resistor based on feedback from Alex onsite. Corrected issue with RoomIsOccupiedFeedback_OutputChange method in EssentialsRoomBase. Updated Fusion Remote Occ sensor logic. 2018-01-22 16:37:27 -07:00
Heath Volmer
b7bab6cfd1 Added Activate cycle try/catch for individual devices; added test abilities to feedbacks 2018-01-22 14:53:09 -07:00
Neil Dorin
b83b3737e9 Committing missed file 2018-01-22 14:25:58 -07:00
Neil Dorin
42c4c705c9 Added ability to disable pull up resistor on versiports from config. 2018-01-22 14:25:37 -07:00
Heath Volmer
cb41b31018 Wrap up Friday - null ref in RoomBase on Occ sensor event 2018-01-22 11:31:23 -07:00
Heath Volmer
6f028c06ae Debugging on privacy buttons 2018-01-19 15:44:14 -07:00
Heath Volmer
bc25c31860 Added versiport creation to factory; rearrange success/failure cases to be more descriptive and easier to follow in code 2018-01-18 15:52:51 -07:00
Heath Volmer
32b548eba0 Room off timing on spark room; debug on load failures; testing on occ sensor and mics 2018-01-18 14:35:39 -07:00
Heath Volmer
f83e64ee7e Merge ecs-665 2018-01-18 11:09:40 -07:00
Neil Dorin
2ee365447c Added occupacy debug console messages and linked crestron sensor event to generic interface event 2018-01-18 06:51:27 -07:00
Heath Volmer
e5ac725b85 Compile for release v -> 1.0.18 2018-01-17 15:24:10 -07:00
Heath Volmer
d0cbb8d096 Join changes on DDVC 2018-01-17 15:21:42 -07:00
Heath Volmer
91f6235441 Merge pull request #4 in PEC/essentials from feature/ecs-665 to development
* commit '3c256564c6b333dc232c1fb67040d2bbc39471ac':
  Added code to Device Factory to build Cresnet Occupancy Sensors from config.
2018-01-17 17:19:19 -05:00
Neil Dorin
3c256564c6 Added code to Device Factory to build Cresnet Occupancy Sensors from config. 2018-01-17 14:54:35 -07:00
Heath Volmer
53de4f32eb Merge of mob-136 2018-01-17 11:57:34 -07:00
Heath Volmer
9effa78fdf Tested basic messaging with Simpl code; worked out join ranges for most messages; adding config data 2018-01-15 15:53:32 -07:00
Heath Volmer
ba5036e400 Fixed SetStringSigAction stack overflow ;-); Added DDVC room bridge class with basic actions attached and messages prepared 2018-01-15 11:35:04 -07:00
Heath Volmer
cd747a01ea v1.0.17 - initial setup of file system 2018-01-09 16:00:49 -07:00
Heath Volmer
ac4ef972fe Added initial filesystem set up that puts all folders in place on initial run 2018-01-09 15:58:38 -07:00
Heath Volmer
857d10f0b2 Added shutdown-related api to CotijaRoomController 2018-01-04 17:28:38 -07:00
Heath Volmer
b45fb57cb2 1.0.15; Huddle space end meeting display power fixes 2018-01-04 11:12:34 -07:00
Heath Volmer
e1e2627f56 added further delay to end meeting steps to allow for default source restoration and power off 2018-01-04 11:00:09 -07:00
Heath Volmer
d1646f8216 1.0.14 Removed extra volume set from cooldown; removed room-on/source-on check from RunDefaultRoute in HuddleRoom 2018-01-04 10:47:50 -07:00
Heath Volmer
02cfc46f90 Added Async fire to Feedbacks; fixed mock display to use async feedback 2018-01-03 16:19:06 -07:00
Heath Volmer
0fb946c7d5 Added CEvent semaphore to PostToServer to sequence posts 2018-01-02 19:58:39 -07:00
Heath Volmer
026ab438ad Fixed unknown_error http post problems with retry on new client; working on mutex around post to handle simultaneous posts 2018-01-02 18:16:41 -07:00
Heath Volmer
51afb966d5 Re-added api for volume and mute 2018-01-01 05:00:33 -07:00
Heath Volmer
68fb82f801 Manual add IHasCodecLayouts 2017-12-29 10:35:02 -07:00
Heath Volmer
0830fb8943 Finishing troublesome merge from development 2017-12-29 10:25:51 -07:00
Heath Volmer
2ed63e7c84 Manual copy of cisco codec from developmet 2017-12-29 10:23:44 -07:00
Heath Volmer
08a4497f2c Moved VCDriver manually from development 2017-12-29 10:16:30 -07:00
Heath Volmer
85d79c5265 Merge, compile for release 2017-12-22 12:45:37 -07:00
Heath Volmer
fe76b257b2 v -> v1.0.10; fixes on remote view button 2017-12-22 12:13:32 -07:00
Heath Volmer
800de511ce Merge remote-tracking branch 'origin/feature/ecs-663' into feature/mob-108 2017-12-22 10:07:11 -07:00
Neil Dorin
82784f00a4 Coded changes as per email 2017-12-21 15:35:17 -07:00
Heath Volmer
289a33451f Relay warming, cooling and shutdown messages 2017-12-13 14:36:13 -07:00
Heath Volmer
ae496f556b Changed incoming messages for a few features. Added default source select 2017-12-12 11:11:33 -07:00
Heath Volmer
762f13c708 Build after merge 2017-12-07 15:47:44 -07:00
Heath Volmer
a61bfc4173 Cisco codec layout and min/max buttons for single display. v1.0.7 2017-12-07 15:45:54 -07:00
Heath Volmer
c3d455a7c2 Heartbeat problem resolved; much logging 2017-12-05 16:53:20 -07:00
Heath Volmer
864e65fe92 SO join; closing for weekend 2017-11-22 14:41:02 -07:00
Heath Volmer
385dd8a482 Meeting popup fixes; calendar list event and update fixes 2017-11-22 14:01:01 -07:00
Heath Volmer
19860be486 ecs-649, exceptions on boot with call connected 2017-11-20 16:07:45 -07:00
Neil Dorin
4d6a680f23 ecs-646 Daily Fusion server time query. 2017-11-15 15:59:35 -07:00
Neil Dorin
f9cb4e8a34 Added default source and volume levels recall to EndShutdown() methods in huddle and huddleVtc1 room types. Added mute control and feedback to Avocor display driver (using volume level discrete commands, no mute available in API). 2017-11-15 15:20:25 -07:00
Neil Dorin
07e77f2ce4 EOD Commit. Updates to Avocor display class. Fixes to Tech Page Driver to add feedback for display power status 2017-11-10 15:10:23 -07:00
Neil Dorin
6dbc152810 Adds Avocor Display driver class 2017-11-09 16:13:03 -07:00
Neil Dorin
a3580ca15c Fixes ecs-638 (Default Volume recall in huddle room) 2017-11-08 20:02:55 -07:00
Neil Dorin
7a1ead7b56 Fixed bug with InputStateFeedback event handler in MicrophonePrivacyController. Tested OK with Digital Inputs on RMC3 triggering privacy toggle 2017-11-08 10:33:30 -07:00
Neil Dorin
f9d1a737c4 Added behaviour options for MicrophonePrivacyController to track either room power state or room call state from config 2017-11-06 15:20:50 -07:00
Neil Dorin
d9013157ad MIcrophone Privacy feature tested and working as far as relay switching. Needs someone to short the digital inputs on the office RMC and load to PRO3 or other versiport compatible processor to test mic button contact closure input. 2017-11-03 17:13:44 -06:00
Neil Dorin
43f0ae0533 Merge branch 'bugfix/ecs-629' into feature/ecs-497
Conflicts:
	Essentials Devices Common/Essentials Devices Common/Display/SamsungMDCDisplay.cs
	Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj
	Release Package/PepperDashEssentials.cpz
	Release Package/PepperDashEssentials.dll
2017-11-03 13:04:11 -06:00
Neil Dorin
6d66c7d896 Updated SamsungMDC driver to default to 0x21/0x23 for Hdmi1 and Hdmi2 commands and added Hdmi1PC and Hdmi2PC (0x22/0x24) as new input options.
Remapped DisplayPort command on tech driver display control SRL to match UI buttons
2017-11-03 11:45:50 -06:00
Neil Dorin
f327ab1590 Temp commit to capture progress on MicrophonePrivacy before switching branches to help debug Samsung MDC driver 2017-11-03 09:50:44 -06:00
Neil Dorin
47fc2f3371 Updates to how the EssentialsVideoCodecUIDriver retrieves the codec phone number/SIP URI from the CodecInfo object. It now attempts to get the values from the SIP registration, if present. If not, it pulls them from the H323 configuration (E.164 alias and H323 ID). 2017-11-02 11:20:14 -06:00
Neil Dorin
d1197329f3 Temp commit to save progress before switching branches to debug NYU items. 2017-11-02 10:54:15 -06:00
Neil Dorin
a0817307b1 Moved MicrophonePrivacyController to Devices Common. Fixed references 2017-11-01 14:20:13 -06:00
Neil Dorin
43c3b83637 Updated warmup timer for SamsungMDC back to 10s 2017-11-01 10:30:42 -06:00
Neil Dorin
2a485e36c8 updatest to deal with ecs.629 by adding delay in SamsungMDC to commands sent after volume 2017-11-01 09:50:05 -06:00
Neil Dorin
bc47c65e48 MicrophonePrivacyController progress 2017-10-31 13:17:00 -06:00
Heath Volmer
1ad5bc68e5 Merge pull request #2 in PEC/essentials from feature/ecs-497 to development
* commit '8a9a8ac6a77201784c96996a5de1149ac589cb5f':
  Updated AssemblyVersion to 1.0.2.*
  Prep for merge into development for NYU deployment
  Updated PhoneNumber property name to SipPhoneNumber
  General code cleanup and updates to Huddle UI drivers to comply with Vtc1 changes
2017-10-31 14:02:36 -04:00
120 changed files with 111973 additions and 13491 deletions

View File

@@ -26,16 +26,24 @@ namespace PepperDash.Essentials.Core
public ComPortController(string key, ComPort port, ComPort.ComPortSpec spec)
: base(key)
{
if (port == null)
{
Debug.Console(0, this, "ERROR: Invalid com port, continuing but comms will not function");
return;
}
Port = port;
Spec = spec;
//IsConnected = new BoolFeedback(CommonBoolCue.IsConnected, () => true);
if (Port.Parent is CrestronControlSystem)
{
var result = Port.Register();
if (result != eDeviceRegistrationUnRegistrationResponse.Success)
{
Debug.Console(0, this, "WARNING: Cannot register Com port: {0}", result);
Debug.Console(0, this, "ERROR: Cannot register Com port: {0}", result);
return; // false
}
}
@@ -80,11 +88,15 @@ namespace PepperDash.Essentials.Core
public void SendText(string text)
{
if (Port == null)
return;
Port.Send(text);
}
public void SendBytes(byte[] bytes)
{
if (Port == null)
return;
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
Port.Send(text);
}

View File

@@ -1,178 +1,198 @@
using System;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials.Core
{
/// <summary>
///
/// </summary>
public class CommFactory
{
public static EssentialsControlPropertiesConfig GetControlPropertiesConfig(DeviceConfig deviceConfig)
{
try
{
return JsonConvert.DeserializeObject<EssentialsControlPropertiesConfig>
(deviceConfig.Properties["control"].ToString());
//Debug.Console(2, "Control TEST: {0}", JsonConvert.SerializeObject(controlConfig));
}
catch (Exception e)
{
Debug.Console(0, "ERROR: [{0}] Control properties deserialize failed:\r{1}", deviceConfig.Key, e);
return null;
}
}
/// <summary>
/// Returns a comm method of either com port, TCP, SSH
/// </summary>
/// <param name="deviceConfig">The Device config object</param>
public static IBasicCommunication CreateCommForDevice(DeviceConfig deviceConfig)
{
EssentialsControlPropertiesConfig controlConfig = GetControlPropertiesConfig(deviceConfig);
if (controlConfig == null)
return null;
IBasicCommunication comm = null;
try
{
var c = controlConfig.TcpSshProperties;
switch (controlConfig.Method)
{
case eControlMethod.Com:
comm = new ComPortController(deviceConfig.Key + "-com", GetComPort(controlConfig), controlConfig.ComParams);
break;
case eControlMethod.IR:
break;
case eControlMethod.Ssh:
{
var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password);
ssh.AutoReconnect = c.AutoReconnect;
if(ssh.AutoReconnect)
ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = ssh;
break;
}
case eControlMethod.Tcpip:
{
var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize);
tcp.AutoReconnect = c.AutoReconnect;
if (tcp.AutoReconnect)
tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = tcp;
break;
}
case eControlMethod.Telnet:
break;
default:
break;
}
}
catch (Exception e)
{
Debug.Console(0, "Cannot create communication from JSON:\r{0}\r\rException:\r{1}",
deviceConfig.Properties.ToString(), e);
}
// put it in the device manager if it's the right flavor
var comDev = comm as Device;
if (comDev != null)
DeviceManager.AddDevice(comDev);
return comm;
}
public static ComPort GetComPort(EssentialsControlPropertiesConfig config)
{
var comPar = config.ComParams;
var dev = GetIComPortsDeviceFromManagedDevice(config.ControlPortDevKey);
if (dev != null && config.ControlPortNumber <= dev.NumberOfComPorts)
return dev.ComPorts[config.ControlPortNumber];
Debug.Console(0, "GetComPort: Device '{0}' does not have com port {1}", config.ControlPortDevKey, config.ControlPortNumber);
return null;
}
/// <summary>
/// Helper to grab the IComPorts device for this PortDeviceKey. Key "controlSystem" will
/// return the ControlSystem object from the Global class.
/// </summary>
/// <returns>IComPorts device or null if the device is not found or does not implement IComPorts</returns>
public static IComPorts GetIComPortsDeviceFromManagedDevice(string ComPortDevKey)
{
if ((ComPortDevKey.Equals("controlSystem", System.StringComparison.OrdinalIgnoreCase)
|| ComPortDevKey.Equals("processor", System.StringComparison.OrdinalIgnoreCase))
&& Global.ControlSystem is IComPorts)
return Global.ControlSystem;
else
{
var dev = DeviceManager.GetDeviceForKey(ComPortDevKey) as IComPorts;
if (dev == null)
Debug.Console(0, "ComPortConfig: Cannot find com port device '{0}'", ComPortDevKey);
return dev;
}
}
}
/// <summary>
///
/// </summary>
public class EssentialsControlPropertiesConfig :
PepperDash.Core.ControlPropertiesConfig
{
// ****** All of these things, except for #Pro-specific com stuff, were
// moved into PepperDash.Core to help non-pro PortalSync.
//public eControlMethod Method { get; set; }
//public string ControlPortDevKey { get; set; }
//[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] // In case "null" is present in config on this value
//public uint ControlPortNumber { get; set; }
//public TcpSshPropertiesConfig TcpSshProperties { get; set; }
//public string IrFile { get; set; }
//public ComPortConfig ComParams { get; set; }
[JsonConverter(typeof(ComSpecJsonConverter))]
public ComPort.ComPortSpec ComParams { get; set; }
//public string IpId { get; set; }
//[JsonIgnore]
//public uint IpIdInt { get { return Convert.ToUInt32(IpId, 16); } }
//public char EndOfLineChar { get; set; }
///// <summary>
///// Defaults to Environment.NewLine;
///// </summary>
//public string EndOfLineString { get; set; }
//public string DeviceReadyResponsePattern { get; set; }
//public EssentialsControlPropertiesConfig()
//{
// EndOfLineString = CrestronEnvironment.NewLine;
//}
}
public class IrControlSpec
{
public string PortDeviceKey { get; set; }
public uint PortNumber { get; set; }
public string File { get; set; }
}
//public enum eControlMethod
//{
// None = 0, Com, IpId, IR, Ssh, Tcpip, Telnet
//}
using System;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials.Core
{
/// <summary>
///
/// </summary>
public class CommFactory
{
public static EssentialsControlPropertiesConfig GetControlPropertiesConfig(DeviceConfig deviceConfig)
{
try
{
return JsonConvert.DeserializeObject<EssentialsControlPropertiesConfig>
(deviceConfig.Properties["control"].ToString());
//Debug.Console(2, "Control TEST: {0}", JsonConvert.SerializeObject(controlConfig));
}
catch (Exception e)
{
Debug.Console(0, "ERROR: [{0}] Control properties deserialize failed:\r{1}", deviceConfig.Key, e);
return null;
}
}
/// <summary>
/// Returns a comm method of either com port, TCP, SSH
/// </summary>
/// <param name="deviceConfig">The Device config object</param>
public static IBasicCommunication CreateCommForDevice(DeviceConfig deviceConfig)
{
EssentialsControlPropertiesConfig controlConfig = GetControlPropertiesConfig(deviceConfig);
if (controlConfig == null)
return null;
IBasicCommunication comm = null;
try
{
var c = controlConfig.TcpSshProperties;
switch (controlConfig.Method)
{
case eControlMethod.Com:
comm = new ComPortController(deviceConfig.Key + "-com", GetComPort(controlConfig), controlConfig.ComParams);
break;
case eControlMethod.IR:
break;
case eControlMethod.Ssh:
{
var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password);
ssh.AutoReconnect = c.AutoReconnect;
if(ssh.AutoReconnect)
ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = ssh;
break;
}
case eControlMethod.Tcpip:
{
var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize);
tcp.AutoReconnect = c.AutoReconnect;
if (tcp.AutoReconnect)
tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = tcp;
break;
}
case eControlMethod.Telnet:
break;
default:
break;
}
}
catch (Exception e)
{
Debug.Console(0, "Cannot create communication from JSON:\r{0}\r\rException:\r{1}",
deviceConfig.Properties.ToString(), e);
}
// put it in the device manager if it's the right flavor
var comDev = comm as Device;
if (comDev != null)
DeviceManager.AddDevice(comDev);
return comm;
}
public static ComPort GetComPort(EssentialsControlPropertiesConfig config)
{
var comPar = config.ComParams;
var dev = GetIComPortsDeviceFromManagedDevice(config.ControlPortDevKey);
if (dev != null && config.ControlPortNumber <= dev.NumberOfComPorts)
return dev.ComPorts[config.ControlPortNumber];
Debug.Console(0, "GetComPort: Device '{0}' does not have com port {1}", config.ControlPortDevKey, config.ControlPortNumber);
return null;
}
/// <summary>
/// Helper to grab the IComPorts device for this PortDeviceKey. Key "controlSystem" will
/// return the ControlSystem object from the Global class.
/// </summary>
/// <returns>IComPorts device or null if the device is not found or does not implement IComPorts</returns>
public static IComPorts GetIComPortsDeviceFromManagedDevice(string ComPortDevKey)
{
if ((ComPortDevKey.Equals("controlSystem", System.StringComparison.OrdinalIgnoreCase)
|| ComPortDevKey.Equals("processor", System.StringComparison.OrdinalIgnoreCase))
&& Global.ControlSystem is IComPorts)
return Global.ControlSystem;
else
{
var dev = DeviceManager.GetDeviceForKey(ComPortDevKey) as IComPorts;
if (dev == null)
Debug.Console(0, "ComPortConfig: Cannot find com port device '{0}'", ComPortDevKey);
return dev;
}
}
}
/// <summary>
///
/// </summary>
public class EssentialsControlPropertiesConfig :
PepperDash.Core.ControlPropertiesConfig
{
// ****** All of these things, except for #Pro-specific com stuff, were
// moved into PepperDash.Core to help non-pro PortalSync.
//public eControlMethod Method { get; set; }
//public string ControlPortDevKey { get; set; }
//[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] // In case "null" is present in config on this value
//public uint ControlPortNumber { get; set; }
//public TcpSshPropertiesConfig TcpSshProperties { get; set; }
//public string IrFile { get; set; }
//public ComPortConfig ComParams { get; set; }
[JsonConverter(typeof(ComSpecJsonConverter))]
public ComPort.ComPortSpec ComParams { get; set; }
public string CresnetId { get; set; }
/// <summary>
/// Attempts to provide uint conversion of string CresnetId
/// </summary>
public uint CresnetIdInt
{
get
{
try
{
return Convert.ToUInt32(CresnetId, 16);
}
catch (Exception)
{
throw new FormatException(string.Format("ERROR:Unable to convert Cresnet ID: {0} to hex. Error:\n{1}", CresnetId));
}
}
}
//public string IpId { get; set; }
//[JsonIgnore]
//public uint IpIdInt { get { return Convert.ToUInt32(IpId, 16); } }
//public char EndOfLineChar { get; set; }
///// <summary>
///// Defaults to Environment.NewLine;
///// </summary>
//public string EndOfLineString { get; set; }
//public string DeviceReadyResponsePattern { get; set; }
//public EssentialsControlPropertiesConfig()
//{
// EndOfLineString = CrestronEnvironment.NewLine;
//}
}
public class IrControlSpec
{
public string PortDeviceKey { get; set; }
public uint PortNumber { get; set; }
public string File { get; set; }
}
//public enum eControlMethod
//{
// None = 0, Com, IpId, IR, Ssh, Tcpip, Telnet
//}
}

View File

@@ -20,7 +20,7 @@ namespace PepperDash.Essentials.Core
{
get
{
return string.Format(@"\NVRAM\Program{0}\IR\", InitialParametersClass.ApplicationNumber);
return Global.FilePathPrefix + "IR" + Global.DirectorySeparator;
}
}

View File

@@ -17,9 +17,6 @@ namespace PepperDash.Essentials.Core.Config
[JsonProperty("info")]
public InfoConfig Info { get; set; }
//[JsonProperty("roomLists")]
//public Dictionary<string, List<string>> RoomLists { get; set; }
[JsonProperty("devices")]
public List<DeviceConfig> Devices { get; set; }

View File

@@ -1,4 +1,6 @@
using System;
using System;
using Crestron.SimplSharp.Reflection;
using Newtonsoft.Json;
@@ -19,7 +21,10 @@ namespace PepperDash.Essentials.Core.Config
public string Type { get; set; }
[JsonProperty("version")]
public string Version { get; set; }
public string Version { get; set; }
[JsonProperty("runtimeInfo")]
public RuntimeInfo RuntimeInfo { get; set; }
[JsonProperty("comment")]
public string Comment { get; set; }
@@ -30,7 +35,34 @@ namespace PepperDash.Essentials.Core.Config
Date = DateTime.Now;
Type = "";
Version = "";
Comment = "";
Comment = "";
RuntimeInfo = new RuntimeInfo();
}
}
}
/// <summary>
/// Represents runtime information about the program/processor
/// </summary>
public class RuntimeInfo
{
/// <summary>
/// The name of the running application
/// </summary>
[JsonProperty("appName")]
public string AppName { get; set; }
/// <summary>
/// The Assembly version of the running application
/// </summary>
[JsonProperty("assemblyVersion")]
public string AssemblyVersion { get; set; }
/// <summary>
/// The OS Version of the processor (Firmware Version)
/// </summary>
[JsonProperty("osVersion")]
public string OsVersion { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Core.Config
{
public class SourceDevicePropertiesConfigBase
{
public bool DisableSharing { get; set; }
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Core.CrestronIO
{
public class IOPortConfig
{
public string PortDeviceKey { get; set; }
public uint PortNumber { get; set; }
public bool DisablePullUpResistor { get; set; }
}
}

View File

@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using PepperDash.Core;
namespace PepperDash.Essentials.Core.CrestronIO
{
public class GenericDigitalInputDevice : Device, IDigitalInput
{
public DigitalInput InputPort { get; private set; }
public BoolFeedback InputStateFeedback { get; private set; }
Func<bool> InputStateFeedbackFunc
{
get
{
return () => InputPort.State;
}
}
public GenericDigitalInputDevice(string key, DigitalInput inputPort):
base(key)
{
InputStateFeedback = new BoolFeedback(InputStateFeedbackFunc);
InputPort = inputPort;
InputPort.StateChange += new DigitalInputEventHandler(InputPort_StateChange);
}
void InputPort_StateChange(DigitalInput digitalInput, DigitalInputEventArgs args)
{
InputStateFeedback.FireUpdate();
}
}
}

View File

@@ -5,39 +5,46 @@ using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using PepperDash.Core;
namespace PepperDash.Essentials.Core.Crestron_IO
namespace PepperDash.Essentials.Core.CrestronIO
{
/// <summary>
/// Represents a generic digital input deviced tied to a versiport
/// </summary>
public class GenericVersiportInputDevice
public class GenericVersiportDigitalInputDevice : Device, IDigitalInput
{
//Versiport InputPort {get; private set;}
public Versiport InputPort { get; private set; }
//BoolFeedback InputStateFeedback {get; private set;}
public BoolFeedback InputStateFeedback { get; private set; }
//Func<bool> InputStateFeedbackFunc
//{
// get
// {
// return () => InputPort.DigitalIn;
// }
//}
Func<bool> InputStateFeedbackFunc
{
get
{
return () => InputPort.DigitalIn;
}
}
//public GenericVersiportInputDevice(Versiport inputPort)
//{
// InputStateFeedback = new BoolFeedback(InputStateFeedbackFunc);
public GenericVersiportDigitalInputDevice(string key, Versiport inputPort, IOPortConfig props):
base(key)
{
InputStateFeedback = new BoolFeedback(InputStateFeedbackFunc);
InputPort = inputPort;
InputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalInput);
if (props.DisablePullUpResistor)
InputPort.DisablePullUpResistor = true;
InputPort.VersiportChange += new VersiportEventHandler(InputPort_VersiportChange);
// InputPort = inputPort;
Debug.Console(1, this, "Created GenericVersiportDigitalInputDevice on port '{0}'. DisablePullUpResistor: '{1}'", props.PortNumber, InputPort.DisablePullUpResistor);
}
// InputPort.VersiportChange += new VersiportEventHandler(InputPort_VersiportChange);
void InputPort_VersiportChange(Versiport port, VersiportEventArgs args)
{
Debug.Console(1, this, "Versiport change: {0}", args.Event);
//}
//void InputPort_VersiportChange(Versiport port, VersiportEventArgs args)
//{
// InputStateFeedback.FireUpdate();
//}
if(args.Event == eVersiportEvent.DigitalInChange)
InputStateFeedback.FireUpdate();
}
}
}

View File

@@ -4,10 +4,13 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Core.Crestron_IO
namespace PepperDash.Essentials.Core.CrestronIO
{
/// <summary>
/// Represents a device that provides digital input
/// </summary>
public interface IDigitalInput
{
BoolFeedback InputStateFeedback { get; }
}
}

View File

@@ -3,41 +3,67 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
namespace PepperDash.Essentials.Core.Crestron_IO
using PepperDash.Core;
namespace PepperDash.Essentials.Core.CrestronIO
{
/// <summary>
/// Represents a generic device controlled by relays
/// </summary>
public class GenericRelayDevice
public class GenericRelayDevice : Device, ISwitchedOutput
{
//Relay RelayOutput { get; private set; }
public Relay RelayOutput { get; private set; }
//public boolfeedback relaystatefeedback { get; private set; }
public BoolFeedback OutputIsOnFeedback { get; private set; }
//func<bool> relaystatefeedbackfunc
//{
// get
// {
// return () => relayoutput.state;
// }
//}
public GenericRelayDevice(string key, Relay relay):
base(key)
{
OutputIsOnFeedback = new BoolFeedback(new Func<bool>(() => RelayOutput.State));
//public genericrelaydevice(relay relay)
//{
// relaystatefeedback = new boolfeedback(relaystatefeedbackfunc);
if (relay.AvailableForUse)
RelayOutput = relay;
// if(relay.availableforuse)
// relayoutput = relay;
RelayOutput.StateChange += new RelayEventHandler(RelayOutput_StateChange);
}
// relayoutput.statechange += new relayeventhandler(relayoutput_statechange);
//}
void RelayOutput_StateChange(Relay relay, RelayEventArgs args)
{
OutputIsOnFeedback.FireUpdate();
}
//void relayoutput_statechange(relay relay, relayeventargs args)
//{
// relaystatefeedback.fireupdate();
//}
public void OpenRelay()
{
RelayOutput.State = false;
}
public void CloseRelay()
{
RelayOutput.State = true;
}
public void ToggleRelayState()
{
if (RelayOutput.State == true)
OpenRelay();
else
CloseRelay();
}
#region ISwitchedOutput Members
void ISwitchedOutput.On()
{
CloseRelay();
}
void ISwitchedOutput.Off()
{
OpenRelay();
}
#endregion
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Core.CrestronIO
{
/// <summary>
/// Describes an output capable of switching on and off
/// </summary>
public interface ISwitchedOutput
{
BoolFeedback OutputIsOnFeedback {get;}
void On();
void Off();
}
public interface ISwitchedOutputCollection
{
Dictionary<uint, ISwitchedOutput> SwitchedOutputs { get; }
}
}

View File

@@ -38,16 +38,16 @@ namespace PepperDash.Essentials.Core
/// </summary>
public override bool CustomActivate()
{
new CTimer(o =>
Debug.Console(0, this, "Activating");
var response = Hardware.RegisterWithLogging(Key);
if (response != eDeviceRegistrationUnRegistrationResponse.Success)
{
Debug.Console(1, this, "Activating");
var response = Hardware.RegisterWithLogging(Key);
if (response == eDeviceRegistrationUnRegistrationResponse.Success)
{
Hardware.OnlineStatusChange += new OnlineStatusChangeEventHandler(Hardware_OnlineStatusChange);
CommunicationMonitor.Start();
}
}, 0);
Debug.Console(0, this, "ERROR: Cannot register Crestron device: {0}", response);
return false;
}
Hardware.OnlineStatusChange += new OnlineStatusChangeEventHandler(Hardware_OnlineStatusChange);
CommunicationMonitor.Start();
return true;
}

View File

@@ -0,0 +1,128 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// A bridge class to cover the basic features of GenericBase hardware
/// </summary>
public class CrestronGenericBaseDevice : Device, IOnline, IHasFeedback, ICommunicationMonitor, IUsageTracking
{
public virtual GenericBase Hardware { get; protected set; }
public BoolFeedback IsOnline { get; private set; }
public BoolFeedback IsRegistered { get; private set; }
public StringFeedback IpConnectionsText { get; private set; }
public CrestronGenericBaseDevice(string key, string name, GenericBase hardware)
: base(key, name)
{
Hardware = hardware;
IsOnline = new BoolFeedback(CommonBoolCue.IsOnlineFeedback, () => Hardware.IsOnline);
IsRegistered = new BoolFeedback(new Cue("IsRegistered", 0, eCueType.Bool), () => Hardware.Registered);
IpConnectionsText = new StringFeedback(CommonStringCue.IpConnectionsText, () =>
string.Join(",", Hardware.ConnectedIpList.Select(cip => cip.DeviceIpAddress).ToArray()));
CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, hardware, 120000, 300000);
}
/// <summary>
/// Make sure that overriding classes call this!
/// Registers the Crestron device, connects up to the base events, starts communication monitor
/// </summary>
public override bool CustomActivate()
{
Debug.Console(0, this, "Activating");
var response = Hardware.RegisterWithLogging(Key);
if (response != eDeviceRegistrationUnRegistrationResponse.Success)
{
<<<<<<< HEAD
Debug.Console(0, this, "ERROR: Cannot register Crestron device: {0}", response);
return false;
}
=======
Debug.Console(0, this, "ERROR: Cannot register Crestron device: {0}", response);
return false;
}
>>>>>>> origin/feature/ecs-342-neil
Hardware.OnlineStatusChange += new OnlineStatusChangeEventHandler(Hardware_OnlineStatusChange);
CommunicationMonitor.Start();
return true;
}
/// <summary>
/// This disconnects events and unregisters the base hardware device.
/// </summary>
/// <returns></returns>
public override bool Deactivate()
{
CommunicationMonitor.Stop();
Hardware.OnlineStatusChange -= Hardware_OnlineStatusChange;
return Hardware.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success;
}
/// <summary>
/// Returns a list containing the Outputs that we want to expose.
/// </summary>
public virtual List<Feedback> Feedbacks
{
get
{
return new List<Feedback>
{
IsOnline,
IsRegistered,
IpConnectionsText
};
}
}
void Hardware_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)
{
IsOnline.FireUpdate();
}
#region IStatusMonitor Members
public StatusMonitorBase CommunicationMonitor { get; private set; }
#endregion
#region IUsageTracking Members
public UsageTracking UsageTracker { get; set; }
#endregion
}
//***********************************************************************************
public class CrestronGenericBaseDeviceEventIds
{
public const uint IsOnline = 1;
public const uint IpConnectionsText =2;
}
/// <summary>
/// Adds logging to Register() failure
/// </summary>
public static class GenericBaseExtensions
{
public static eDeviceRegistrationUnRegistrationResponse RegisterWithLogging(this GenericBase device, string key)
{
var result = device.Register();
if (result != eDeviceRegistrationUnRegistrationResponse.Success)
{
Debug.Console(0, Debug.ErrorLogLevel.Error, "Cannot register device '{0}': {1}", key, result);
}
return result;
}
}
}

View File

@@ -56,8 +56,6 @@ namespace PepperDash.Essentials.Core
System.Globalization.CultureInfo.InvariantCulture))
.ToArray();
object ret = method.Invoke(obj, convertedParams);
//Debug.Console(0, JsonConvert.SerializeObject(ret));
// return something?
}
/// <summary>

View File

@@ -29,10 +29,6 @@ namespace PepperDash.Essentials.Core
public static void Initialize(CrestronControlSystem cs)
{
//CrestronConsole.AddNewConsoleCommand(ListDeviceCommands, "devcmdlist", "Lists commands",
// ConsoleAccessLevelEnum.AccessOperator);
//CrestronConsole.AddNewConsoleCommand(DoDeviceCommand, "devcmd", "Runs a command on device - key Name value",
// ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ListDeviceCommStatuses, "devcommstatus", "Lists the communication status of all devices",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ListDeviceFeedbacks, "devfb", "Lists current feedbacks",
@@ -64,8 +60,15 @@ namespace PepperDash.Essentials.Core
{
foreach (var d in Devices.Values)
{
if (d is Device)
(d as Device).Activate();
try
{
if (d is Device)
(d as Device).Activate();
}
catch (Exception e)
{
Debug.Console(0, d, "ERROR: Device activation failure:\r{0}", e);
}
}
}

View File

@@ -0,0 +1,253 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.EthernetCommunication;
using Crestron.SimplSharpPro.UI;
using Crestron.SimplSharp.Reflection;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
public static class DeviceManager
{
//public static List<Device> Devices { get { return _Devices; } }
//static List<Device> _Devices = new List<Device>();
static Dictionary<string, IKeyed> Devices = new Dictionary<string, IKeyed>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Returns a copy of all the devices in a list
/// </summary>
public static List<IKeyed> AllDevices { get { return new List<IKeyed>(Devices.Values); } }
public static void Initialize(CrestronControlSystem cs)
{
CrestronConsole.AddNewConsoleCommand(ListDeviceCommStatuses, "devcommstatus", "Lists the communication status of all devices",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ListDeviceFeedbacks, "devfb", "Lists current feedbacks",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ListDevices, "devlist", "Lists current managed devices",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(DeviceJsonApi.DoDeviceActionWithJson, "devjson", "",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>
{
CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetProperties(s));
}, "devprops", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>
{
CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetMethods(s));
}, "devmethods", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>
{
CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetApiMethods(s));
}, "apimethods", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(SimulateComReceiveOnDevice, "devsimreceive",
"Simulates incoming data on a com device", ConsoleAccessLevelEnum.AccessOperator);
}
/// <summary>
/// Calls activate on all Device class items
/// </summary>
public static void ActivateAll()
{
foreach (var d in Devices.Values)
<<<<<<< HEAD
{
try
{
if (d is Device)
(d as Device).Activate();
}
catch (Exception e)
{
Debug.Console(0, d, "ERROR: Device activation failure:\r{0}", e);
}
}
=======
{
try
{
if (d is Device)
(d as Device).Activate();
}
catch (Exception e)
{
Debug.Console(0, d, "ERROR: Device activation failure:\r{0}", e);
}
}
>>>>>>> origin/feature/ecs-342-neil
}
/// <summary>
/// Calls activate on all Device class items
/// </summary>
public static void DeactivateAll()
{
foreach (var d in Devices.Values)
{
if (d is Device)
(d as Device).Deactivate();
}
}
//static void ListMethods(string devKey)
//{
// var dev = GetDeviceForKey(devKey);
// if(dev != null)
// {
// var type = dev.GetType().GetCType();
// var methods = type.GetMethods(BindingFlags.Public|BindingFlags.Instance);
// var sb = new StringBuilder();
// sb.AppendLine(string.Format("{2} methods on [{0}] ({1}):", dev.Key, type.Name, methods.Length));
// foreach (var m in methods)
// {
// sb.Append(string.Format("{0}(", m.Name));
// var pars = m.GetParameters();
// foreach (var p in pars)
// sb.Append(string.Format("({1}){0} ", p.Name, p.ParameterType.Name));
// sb.AppendLine(")");
// }
// CrestronConsole.ConsoleCommandResponse(sb.ToString());
// }
//}
static void ListDevices(string s)
{
Debug.Console(0, "{0} Devices registered with Device Mangager:",Devices.Count);
var sorted = Devices.Values.ToList();
sorted.Sort((a, b) => a.Key.CompareTo(b.Key));
foreach (var d in sorted)
{
var name = d is IKeyName ? (d as IKeyName).Name : "---";
Debug.Console(0, " [{0}] {1}", d.Key, name);
}
}
static void ListDeviceFeedbacks(string devKey)
{
var dev = GetDeviceForKey(devKey);
if(dev == null)
{
Debug.Console(0, "Device '{0}' not found", devKey);
return;
}
var statusDev = dev as IHasFeedback;
if(statusDev == null)
{
Debug.Console(0, "Device '{0}' does not have visible feedbacks", devKey);
return;
}
statusDev.DumpFeedbacksToConsole(true);
}
//static void ListDeviceCommands(string devKey)
//{
// var dev = GetDeviceForKey(devKey);
// if (dev == null)
// {
// Debug.Console(0, "Device '{0}' not found", devKey);
// return;
// }
// Debug.Console(0, "This needs to be reworked. Stay tuned.", devKey);
//}
static void ListDeviceCommStatuses(string input)
{
var sb = new StringBuilder();
foreach (var dev in Devices.Values)
{
if (dev is ICommunicationMonitor)
sb.Append(string.Format("{0}: {1}\r", dev.Key, (dev as ICommunicationMonitor).CommunicationMonitor.Status));
}
CrestronConsole.ConsoleCommandResponse(sb.ToString());
}
//static void DoDeviceCommand(string command)
//{
// Debug.Console(0, "Not yet implemented. Stay tuned");
//}
public static void AddDevice(IKeyed newDev)
{
// Check for device with same key
//var existingDevice = _Devices.FirstOrDefault(d => d.Key.Equals(newDev.Key, StringComparison.OrdinalIgnoreCase));
////// If it exists, remove or warn??
//if (existingDevice != null)
if(Devices.ContainsKey(newDev.Key))
{
Debug.Console(0, newDev, "WARNING: A device with this key already exists. Not added to manager");
return;
}
Devices.Add(newDev.Key, newDev);
//if (!(_Devices.Contains(newDev)))
// _Devices.Add(newDev);
}
public static void RemoveDevice(IKeyed newDev)
{
if(newDev == null)
return;
if (Devices.ContainsKey(newDev.Key))
Devices.Remove(newDev.Key);
//if (_Devices.Contains(newDev))
// _Devices.Remove(newDev);
else
Debug.Console(0, "Device manager: Device '{0}' does not exist in manager. Cannot remove", newDev.Key);
}
public static IEnumerable<string> GetDeviceKeys()
{
//return _Devices.Select(d => d.Key).ToList();
return Devices.Keys;
}
public static IEnumerable<IKeyed> GetDevices()
{
//return _Devices.Select(d => d.Key).ToList();
return Devices.Values;
}
public static IKeyed GetDeviceForKey(string key)
{
//return _Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
if (key != null && Devices.ContainsKey(key))
return Devices[key];
return null;
}
/// <summary>
/// Console handler that simulates com port data receive
/// </summary>
/// <param name="s"></param>
public static void SimulateComReceiveOnDevice(string s)
{
// devcomsim:1 xyzabc
var match = Regex.Match(s, @"(\S*)\s*(.*)");
if (match.Groups.Count < 3)
{
CrestronConsole.ConsoleCommandResponse(" Format: devsimreceive:P <device key> <string to send>");
return;
}
//Debug.Console(2, "**** {0} - {1} ****", match.Groups[1].Value, match.Groups[2].Value);
ComPortController com = GetDeviceForKey(match.Groups[1].Value) as ComPortController;
if (com == null)
{
CrestronConsole.ConsoleCommandResponse("'{0}' is not a comm port device", match.Groups[1].Value);
return;
}
com.SimulateReceive(match.Groups[2].Value);
}
}
}

View File

@@ -28,6 +28,15 @@ namespace PepperDash.Essentials.Core
BoolFeedback MuteFeedback { get; }
}
/// <summary>
/// A class that implements this contains a reference to a current IBasicVolumeControls device.
/// The class may have multiple IBasicVolumeControls.
/// </summary>
public interface IHasCurrentVolumeControls
{
IBasicVolumeControls CurrentVolumeControls { get; }
}
/// <summary>
///

View File

@@ -23,6 +23,7 @@ namespace PepperDash.Essentials.Core
/// </summary>
public class SourceListItem
{
[JsonProperty("sourceKey")]
public string SourceKey { get; set; }
/// <summary>
/// Returns the source Device for this, if it exists in DeviceManager
@@ -43,6 +44,7 @@ namespace PepperDash.Essentials.Core
/// Gets either the source's Name or this AlternateName property, if
/// defined. If source doesn't exist, returns "Missing source"
/// </summary>
[JsonProperty("preferredName")]
public string PreferredName
{
get
@@ -60,15 +62,36 @@ namespace PepperDash.Essentials.Core
/// <summary>
/// A name that will override the source's name on the UI
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("icon")]
public string Icon { get; set; }
[JsonProperty("altIcon")]
public string AltIcon { get; set; }
[JsonProperty("includeInSourceList")]
public bool IncludeInSourceList { get; set; }
public int Order { get; set; }
[JsonProperty("order")]
public int Order { get; set; }
[JsonProperty("volumeControlKey")]
public string VolumeControlKey { get; set; }
[JsonProperty("type")]
public eSourceListItemType Type { get; set; }
[JsonProperty("routeList")]
public List<SourceRouteListItem> RouteList { get; set; }
[JsonProperty("disableCodecSharing")]
public bool DisableCodecSharing { get; set; }
[JsonProperty("disableRoutedSharing")]
public bool DisableRoutedSharing { get; set; }
public SourceListItem()
{
Icon = "Blank";

View File

@@ -64,14 +64,14 @@ namespace PepperDash.Essentials.Core
if (!PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown)
{
_IsWarmingUp = true;
IsWarmingUpFeedback.FireUpdate();
IsWarmingUpFeedback.InvokeFireUpdate();
// Fake power-up cycle
WarmupTimer = new CTimer(o =>
{
_IsWarmingUp = false;
_PowerIsOn = true;
IsWarmingUpFeedback.FireUpdate();
PowerIsOnFeedback.FireUpdate();
IsWarmingUpFeedback.InvokeFireUpdate();
PowerIsOnFeedback.InvokeFireUpdate();
}, WarmupTime);
}
}
@@ -84,14 +84,14 @@ namespace PepperDash.Essentials.Core
{
_IsCoolingDown = true;
_PowerIsOn = false;
PowerIsOnFeedback.FireUpdate();
IsCoolingDownFeedback.FireUpdate();
PowerIsOnFeedback.InvokeFireUpdate();
IsCoolingDownFeedback.InvokeFireUpdate();
// Fake cool-down cycle
CooldownTimer = new CTimer(o =>
{
Debug.Console(2, this, "Cooldown timer ending");
_IsCoolingDown = false;
IsCoolingDownFeedback.FireUpdate();
IsCoolingDownFeedback.InvokeFireUpdate();
}, CooldownTime);
}
}
@@ -117,20 +117,20 @@ namespace PepperDash.Essentials.Core
public void SetVolume(ushort level)
{
_FakeVolumeLevel = level;
VolumeLevelFeedback.FireUpdate();
_FakeVolumeLevel = level;
VolumeLevelFeedback.InvokeFireUpdate();
}
public void MuteOn()
{
_IsMuted = true;
MuteFeedback.FireUpdate();
MuteFeedback.InvokeFireUpdate();
}
public void MuteOff()
{
_IsMuted = false;
MuteFeedback.FireUpdate();
MuteFeedback.InvokeFireUpdate();
}
public BoolFeedback MuteFeedback { get; private set; }
@@ -170,7 +170,7 @@ namespace PepperDash.Essentials.Core
public void MuteToggle()
{
_IsMuted = !_IsMuted;
MuteFeedback.FireUpdate();
MuteFeedback.InvokeFireUpdate();
}
#endregion

View File

@@ -19,6 +19,16 @@ namespace PepperDash.Essentials.Core
public abstract eCueType Type { get; }
/// <summary>
/// Feedbacks can be put into test mode for simulation of events without real data.
/// Using JSON debugging methods and the Set/ClearTestValue methods, we can simulate
/// Feedback behaviors
/// </summary>
public bool InTestMode { get; protected set; }
/// <summary>
/// Base Constructor - empty
/// </summary>
protected Feedback()
{
}
@@ -28,8 +38,31 @@ namespace PepperDash.Essentials.Core
Cue = cue;
}
/// <summary>
/// Clears test mode and fires update.
/// </summary>
public void ClearTestValue()
{
InTestMode = false;
FireUpdate();
}
/// <summary>
/// Fires an update synchronously
/// </summary>
public abstract void FireUpdate();
/// <summary>
/// Fires the update asynchronously within a CrestronInvoke
/// </summary>
public void InvokeFireUpdate()
{
CrestronInvoke.BeginInvoke(o => FireUpdate());
}
/// <summary>
/// Helper method that fires event. Use this intstead of calling OutputChange
/// </summary>
protected void OnOutputChange()
{
if (OutputChange != null) OutputChange(this, EventArgs.Empty);
@@ -42,18 +75,24 @@ namespace PepperDash.Essentials.Core
public class BoolFeedback : Feedback
{
/// <summary>
/// Returns the current value of the feedback, derived from the ValueFunc
/// Returns the current value of the feedback, derived from the ValueFunc. The ValueFunc is
/// evaluated whenever FireUpdate() is called
/// </summary>
public override bool BoolValue { get { return _BoolValue; } }
bool _BoolValue;
public override eCueType Type { get { return eCueType.Bool; } }
/// <summary>
/// Fake value to be used in test mode
/// </summary>
public bool TestValue { get; private set; }
/// <summary>
/// Func that evaluates on FireUpdate
/// </summary>
public Func<bool> ValueFunc { get; private set; }
/// <summary>
/// The last value delivered on FireUpdate
/// </summary>
//public bool PreviousValue { get; private set; }
List<BoolInputSig> LinkedInputSigs = new List<BoolInputSig>();
List<BoolInputSig> LinkedComplementInputSigs = new List<BoolInputSig>();
@@ -71,7 +110,7 @@ namespace PepperDash.Essentials.Core
public override void FireUpdate()
{
var newValue = ValueFunc.Invoke();
bool newValue = InTestMode ? TestValue : ValueFunc.Invoke();
if (newValue != _BoolValue)
{
_BoolValue = newValue;
@@ -105,9 +144,20 @@ namespace PepperDash.Essentials.Core
public override string ToString()
{
return BoolValue.ToString();
return (InTestMode ? "TEST -- " : "") + BoolValue.ToString();
}
/// <summary>
/// Puts this in test mode, sets the test value and fires an update.
/// </summary>
/// <param name="value"></param>
public void SetTestValue(bool value)
{
TestValue = value;
InTestMode = true;
FireUpdate();
}
void UpdateSig(BoolInputSig sig)
{
sig.BoolValue = _BoolValue;
@@ -125,9 +175,14 @@ namespace PepperDash.Essentials.Core
public override int IntValue { get { return _IntValue; } } // ValueFunc.Invoke(); } }
int _IntValue;
public ushort UShortValue { get { return (ushort)_IntValue; } }
public override eCueType Type { get { return eCueType.Int; } }
//public int PreviousValue { get; private set; }
public override eCueType Type { get { return eCueType.Int; } }
public int TestValue { get; private set; }
/// <summary>
/// Func evaluated on FireUpdate
/// </summary>
Func<int> ValueFunc;
List<UShortInputSig> LinkedInputSigs = new List<UShortInputSig>();
@@ -145,7 +200,7 @@ namespace PepperDash.Essentials.Core
public override void FireUpdate()
{
var newValue = ValueFunc.Invoke();
var newValue = InTestMode ? TestValue : ValueFunc.Invoke();
if (newValue != _IntValue)
{
_IntValue = newValue;
@@ -167,9 +222,20 @@ namespace PepperDash.Essentials.Core
public override string ToString()
{
return IntValue.ToString();
return (InTestMode ? "TEST -- " : "") + IntValue.ToString();
}
/// <summary>
/// Puts this in test mode, sets the test value and fires an update.
/// </summary>
/// <param name="value"></param>
public void SetTestValue(int value)
{
TestValue = value;
InTestMode = true;
FireUpdate();
}
void UpdateSig(UShortInputSig sig)
{
sig.UShortValue = UShortValue;
@@ -182,8 +248,17 @@ namespace PepperDash.Essentials.Core
{
public override string StringValue { get { return _StringValue; } } // ValueFunc.Invoke(); } }
string _StringValue;
public override eCueType Type { get { return eCueType.String; } }
//public string PreviousValue { get; private set; }
/// <summary>
/// Used in testing. Set/Clear functions
/// </summary>
public string TestValue { get; private set; }
/// <summary>
/// Evalutated on FireUpdate
/// </summary>
public Func<string> ValueFunc { get; private set; }
List<StringInputSig> LinkedInputSigs = new List<StringInputSig>();
@@ -202,7 +277,7 @@ namespace PepperDash.Essentials.Core
public override void FireUpdate()
{
var newValue = ValueFunc.Invoke();
var newValue = InTestMode ? TestValue : ValueFunc.Invoke();
if (newValue != _StringValue)
{
_StringValue = newValue;
@@ -224,9 +299,20 @@ namespace PepperDash.Essentials.Core
public override string ToString()
{
return StringValue;
return (InTestMode ? "TEST -- " : "") + StringValue;
}
/// <summary>
/// Puts this in test mode, sets the test value and fires an update.
/// </summary>
/// <param name="value"></param>
public void SetTestValue(string value)
{
TestValue = value;
InTestMode = true;
FireUpdate();
}
void UpdateSig(StringInputSig sig)
{
sig.StringValue = _StringValue;

View File

@@ -1,42 +1,49 @@
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronDataStore;
using Crestron.SimplSharpPro;
//using PepperDash.Essentials.Core.Http;
using PepperDash.Essentials.License;
namespace PepperDash.Essentials.Core
{
public static class Global
{
public static CrestronControlSystem ControlSystem { get; set; }
public static LicenseManager LicenseManager { get; set; }
//public static EssentialsHttpServer HttpConfigServer
//{
// get
// {
// if (_HttpConfigServer == null)
// _HttpConfigServer = new EssentialsHttpServer();
// return _HttpConfigServer;
// }
//}
//static EssentialsHttpServer _HttpConfigServer;
static Global()
{
// Fire up CrestronDataStoreStatic
var err = CrestronDataStoreStatic.InitCrestronDataStore();
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
{
CrestronConsole.PrintLine("Error starting CrestronDataStoreStatic: {0}", err);
return;
}
}
}
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronDataStore;
using Crestron.SimplSharpPro;
//using PepperDash.Essentials.Core.Http;
using PepperDash.Essentials.License;
namespace PepperDash.Essentials.Core
{
public static class Global
{
public static CrestronControlSystem ControlSystem { get; set; }
public static LicenseManager LicenseManager { get; set; }
public static string FilePathPrefix { get; private set; }
public static char DirectorySeparator
{
get
{
return System.IO.Path.DirectorySeparatorChar;
}
}
/// <summary>
/// Sets the file path prefix
/// </summary>
/// <param name="prefix"></param>
public static void SetFilePathPrefix(string prefix)
{
FilePathPrefix = prefix;
}
static Global()
{
// Fire up CrestronDataStoreStatic
var err = CrestronDataStoreStatic.InitCrestronDataStore();
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
{
CrestronConsole.PrintLine("Error starting CrestronDataStoreStatic: {0}", err);
return;
}
}
}
}

View File

@@ -72,6 +72,7 @@ namespace PepperDash.Essentials.Core
public enum eJobTimerCycleTypes
{
RunEveryDay,
RunEveryHour,
RunEveryHalfHour,
RunEveryMinute

View File

@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
namespace PepperDash.Essentials.Core.Lighting
{
/// <summary>
/// Requirements for a device that implements lighting scene control
/// </summary>
public interface ILightingScenes
{
event EventHandler<LightingSceneChangeEventArgs> LightingSceneChange;
List<LightingScene> LightingScenes { get; }
void SelectScene(LightingScene scene);
LightingScene CurrentLightingScene { get; }
}
/// <summary>
/// Requirements for a device that implements master raise/lower
/// </summary>
public interface ILightingMasterRaiseLower
{
void MasterRaise();
void MasterLower();
void MasterRaiseLowerStop();
}
/// <summary>
/// Requiremnts for controlling a lighting load
/// </summary>
public interface ILightingLoad
{
void SetLoadLevel(int level);
void Raise();
void Lower();
IntFeedback LoadLevelFeedback { get; }
BoolFeedback LoadIsOnFeedback { get; }
}
public class LightingSceneChangeEventArgs : EventArgs
{
public LightingScene CurrentLightingScene { get; private set; }
public LightingSceneChangeEventArgs(LightingScene scene)
{
CurrentLightingScene = scene;
}
}
}

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using PepperDash.Core;
namespace PepperDash.Essentials.Core.Lighting
{
public abstract class LightingBase : Device, ILightingScenes
{
#region ILightingScenes Members
public event EventHandler<LightingSceneChangeEventArgs> LightingSceneChange;
public List<LightingScene> LightingScenes { get; protected set; }
public LightingScene CurrentLightingScene { get; protected set; }
#endregion
public LightingBase(string key, string name)
: base(key, name)
{
LightingScenes = new List<LightingScene>();
CurrentLightingScene = new LightingScene();
}
public abstract void SelectScene(LightingScene scene);
public void SimulateSceneSelect(string sceneName)
{
Debug.Console(1, this, "Simulating selection of scene '{0}'", sceneName);
var scene = LightingScenes.FirstOrDefault(s => s.Name.Equals(sceneName));
if (scene != null)
{
CurrentLightingScene = scene;
OnLightingSceneChange();
}
}
/// <summary>
/// Sets the IsActive property on each scene and fires the LightingSceneChange event
/// </summary>
protected void OnLightingSceneChange()
{
foreach (var scene in LightingScenes)
{
if (scene == CurrentLightingScene)
scene.IsActive = true;
else
scene.IsActive = false;
}
var handler = LightingSceneChange;
if (handler != null)
{
handler(this, new LightingSceneChangeEventArgs(CurrentLightingScene));
}
}
}
public class LightingScene
{
public string Name { get; set; }
public string ID { get; set; }
bool _IsActive;
public bool IsActive
{
get
{
return _IsActive;
}
set
{
_IsActive = value;
IsActiveFeedback.FireUpdate();
}
}
public BoolFeedback IsActiveFeedback { get; set; }
public LightingScene()
{
IsActiveFeedback = new BoolFeedback(new Func<bool>(() => IsActive));
}
}
}

View File

@@ -73,7 +73,7 @@
<Reference Include="mscorlib" />
<Reference Include="PepperDash_Core, Version=1.0.1.26313, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\PepperDash.Core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll</HintPath>
<HintPath>..\..\..\pepperdash-simplsharp-core\Pepperdash Core\CLZ Builds\PepperDash_Core.dll</HintPath>
</Reference>
<Reference Include="SimplSharpCustomAttributesInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -103,11 +103,17 @@
<Reference Include="System.Data" />
</ItemGroup>
<ItemGroup>
<Compile Include="Config\SourceDevicePropertiesConfigBase.cs" />
<Compile Include="Crestron IO\Inputs\GenericDigitalInputDevice.cs" />
<Compile Include="Crestron IO\Inputs\GenericVersiportInputDevice.cs" />
<Compile Include="Crestron IO\Inputs\IDigitalInput.cs" />
<Compile Include="Crestron IO\IOPortConfig.cs" />
<Compile Include="Crestron IO\Relay\GenericRelayDevice.cs" />
<Compile Include="Crestron IO\Relay\ISwitchedOutput.cs" />
<Compile Include="Devices\CodecInterfaces.cs" />
<Compile Include="Global\JobTimer.cs" />
<Compile Include="Lighting\Lighting Interfaces.cs" />
<Compile Include="Lighting\LightingBase.cs" />
<Compile Include="Ramps and Increments\ActionIncrementer.cs" />
<Compile Include="Comm and IR\CommFactory.cs" />
<Compile Include="Comm and IR\CommunicationExtras.cs" />
@@ -177,6 +183,9 @@
<Compile Include="Feedbacks\BoolFeedbackPulseExtender.cs" />
<Compile Include="Routing\RoutingPortNames.cs" />
<Compile Include="Routing\TieLineConfig.cs" />
<Compile Include="Shades\Shade Interfaces.cs" />
<Compile Include="Shades\ShadeBase.cs" />
<Compile Include="Shades\ShadeController.cs" />
<Compile Include="SmartObjects\SmartObjectNumeric.cs" />
<Compile Include="SmartObjects\SmartObjectDynamicList.cs" />
<Compile Include="SmartObjects\SmartObjectDPad.cs" />

View File

@@ -4,5 +4,4 @@
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PepperDashEssentialsBase")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyVersion("1.0.0.*")]
[assembly: AssemblyVersion("1.0.3.*")]

View File

@@ -108,10 +108,18 @@ namespace PepperDash.Essentials.Core.Routing
/// </summary>
public const string HdmiIn1 = "hdmiIn1";
/// <summary>
/// hdmiIn1PC
/// </summary>
public const string HdmiIn1PC = "hdmiIn1PC";
/// <summary>
/// hdmiIn2
/// </summary>
public const string HdmiIn2 = "hdmiIn2";
/// <summary>
/// hdmiIn2PC
/// </summary>
public const string HdmiIn2PC = "hdmiIn2PC";
/// <summary>
/// hdmiIn3
/// </summary>
public const string HdmiIn3 = "hdmiIn3";
@@ -176,8 +184,20 @@ namespace PepperDash.Essentials.Core.Routing
/// </summary>
public const string VgaIn = "vgaIn";
/// <summary>
/// vgaIn1
/// </summary>
public const string VgaIn1 = "vgaIn1";
/// <summary>
/// vgaOut
/// </summary>
public const string VgaOut = "vgaOut";
/// <summary>
/// IPC/OPS
/// </summary>
public const string IpcOps = "ipcOps";
/// <summary>
/// MediaPlayer
/// </summary>
public const string MediaPlayer = "mediaPlayer";
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
namespace PepperDash.Essentials.Core.Shades
{
public interface IShades
{
List<ShadeBase> Shades { get; }
}
/// <summary>
/// Requirements for a device that implements basic Open/Close shade control
/// </summary>
public interface IShadesOpenClose
{
void Open();
void Close();
}
/// <summary>
/// Requirements for a device that implements basic Open/Close/Stop shade control (Uses 3 relays)
/// </summary>
public interface IShadesOpenCloseStop : IShadesOpenClose
{
void StopOrPreset();
}
/// <summary>
/// Requirements for a shade device that provides open/closed feedback
/// </summary>
public interface iShadesRaiseLowerFeedback
{
BoolFeedback ShadeIsOpenFeedback { get; }
BoolFeedback ShadeIsClosedFeedback { get; }
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using PepperDash.Essentials.Core.CrestronIO;
namespace PepperDash.Essentials.Core.Shades
{
/// <summary>
/// Base class for a shade device
/// </summary>
public abstract class ShadeBase : Device, IShadesOpenClose
{
public ShadeBase(string key, string name)
: base(key, name)
{
}
#region iShadesOpenClose Members
public abstract void Open();
public abstract void StopOrPreset();
public abstract void Close();
#endregion
}
}

View File

@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
namespace PepperDash.Essentials.Core.Shades
{
/// <summary>
/// Class that contains the shades to be controlled in a room
/// </summary>
public class ShadeController : Device, IShades
{
ShadeControllerConfigProperties Config;
public List<ShadeBase> Shades { get; private set; }
public ShadeController(string key, string name, ShadeControllerConfigProperties config)
: base(key, name)
{
Config = config;
Shades = new List<ShadeBase>();
}
public override bool CustomActivate()
{
foreach (var shadeConfig in Config.Shades)
{
var shade = DeviceManager.GetDeviceForKey(shadeConfig.Key) as ShadeBase;
if (shade != null)
{
AddShade(shade);
}
}
return base.CustomActivate();
}
void AddShade(ShadeBase shade)
{
Shades.Add(shade);
}
}
public class ShadeControllerConfigProperties
{
public List<ShadeConfig> Shades { get; set; }
public class ShadeConfig
{
public string Key { get; set; }
}
}
}

View File

@@ -68,7 +68,7 @@ namespace PepperDash.Essentials.Core
// Count the enable lines to see what max items is
MaxDefinedItems = (ushort)SRL.BooleanInput
.Where(s => s.Name.Contains("Enable")).Count();
Debug.Console(0, "SRL {0} contains max {1} items", SRL.ID, MaxDefinedItems);
Debug.Console(2, "SRL {0} contains max {1} items", SRL.ID, MaxDefinedItems);
SRL.SigChange -= new SmartObjectSigChangeEventHandler(SRL_SigChange);
SRL.SigChange += new SmartObjectSigChangeEventHandler(SRL_SigChange);

View File

@@ -144,23 +144,47 @@ namespace PepperDash.Essentials.Core
sig.UserObject = a;
return sig;
}
/// <summary>
///
/// </summary>
/// <param name="tl"></param>
/// <param name="sigNum"></param>
/// <param name="a"></param>
/// <returns></returns>
public static UShortOutputSig SetUShortSigAction(this BasicTriList tl, uint sigNum, Action<ushort> a)
{
return tl.UShortOutput[sigNum].SetUShortSigAction(a);
}
/// <summary>
///
/// </summary>
/// <param name="sig"></param>
/// <param name="a"></param>
/// <returns></returns>
public static StringOutputSig SetStringSigAction(this StringOutputSig sig, Action<string> a)
{
sig.UserObject = a;
return sig;
}
/// <summary>
///
/// </summary>
/// <param name="tl"></param>
/// <param name="sigNum"></param>
/// <param name="a"></param>
/// <returns></returns>
public static StringOutputSig SetStringSigAction(this BasicTriList tl, uint sigNum, Action<string> a)
{
return tl.SetStringSigAction(sigNum, a);
return tl.StringOutput[sigNum].SetStringSigAction(a);
}
/// <summary>
///
/// </summary>
/// <param name="sig"></param>
/// <returns></returns>
public static Sig ClearSigAction(this Sig sig)
{
sig.UserObject = null;
@@ -190,6 +214,27 @@ namespace PepperDash.Essentials.Core
tl.BooleanInput[sigNum].BoolValue = value;
}
/// <summary>
/// Sends an true-false pulse to the sig
/// </summary>
/// <param name="tl"></param>
/// <param name="sigNum"></param>
public static void PulseBool(this BasicTriList tl, uint sigNum)
{
tl.BooleanInput[sigNum].Pulse();
}
/// <summary>
/// Sends a timed pulse to the sig
/// </summary>
/// <param name="tl"></param>
/// <param name="sigNum"></param>
/// <param name="ms"></param>
public static void PulseBool(this BasicTriList tl, uint sigNum, int ms)
{
tl.BooleanInput[sigNum].Pulse(ms);
}
/// <summary>
/// Helper method to set the value of a ushort Sig on TriList
/// </summary>

View File

@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.DM;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.DM.Config;
namespace PepperDash.Essentials.DM.Chassis
{
public class HdMdNxM4kEController : Device, IRoutingInputsOutputs, IRouting
{
public HdMdNxM Chassis { get; private set; }
public RoutingPortCollection<RoutingInputPort> InputPorts { get; private set; }
public RoutingPortCollection<RoutingOutputPort> OutputPorts { get; private set; }
/// <summary>
///
/// </summary>
/// <param name="key"></param>
/// <param name="name"></param>
/// <param name="chassis"></param>
public HdMdNxM4kEController(string key, string name, HdMdNxM chassis,
HdMdNxM4kEPropertiesConfig props)
: base(key, name)
{
Chassis = chassis;
// logical ports
InputPorts = new RoutingPortCollection<RoutingInputPort>();
for (uint i = 1; i <= 4; i++)
{
InputPorts.Add(new RoutingInputPort("hdmiIn" + i, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, i, this));
}
OutputPorts = new RoutingPortCollection<RoutingOutputPort>();
OutputPorts.Add(new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, null, this));
// physical settings
if (props != null && props.Inputs != null)
{
foreach (var kvp in props.Inputs)
{
// strip "hdmiIn"
var inputNum = Convert.ToUInt32(kvp.Key.Substring(6));
var port = chassis.HdmiInputs[inputNum].HdmiInputPort;
// set hdcp disables
if (kvp.Value.DisableHdcp)
{
Debug.Console(0, this, "Configuration disables HDCP support on {0}", kvp.Key);
port.HdcpSupportOff();
}
else
port.HdcpSupportOn();
}
}
}
public override bool CustomActivate()
{
var result = Chassis.Register();
if (result != Crestron.SimplSharpPro.eDeviceRegistrationUnRegistrationResponse.Success)
{
Debug.Console(0, this, "Device registration failed: {0}", result);
return false;
}
return base.CustomActivate();
}
#region IRouting Members
public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType)
{
// Try to make switch only when necessary. The unit appears to toggle when already selected.
var current = Chassis.HdmiOutputs[1].VideoOut;
if(current != Chassis.HdmiInputs[(uint)inputSelector])
Chassis.HdmiOutputs[1].VideoOut = Chassis.HdmiInputs[(uint)inputSelector];
}
#endregion
/////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
/// <param name="key"></param>
/// <param name="name"></param>
/// <param name="type"></param>
/// <param name="properties"></param>
/// <returns></returns>
public static HdMdNxM4kEController GetController(string key, string name,
string type, HdMdNxM4kEPropertiesConfig properties)
{
try
{
var ipid = properties.Control.IpIdInt;
var address = properties.Control.TcpSshProperties.Address;
type = type.ToLower();
if (type == "hdmd4x14ke")
{
var chassis = new HdMd4x14kE(ipid, address, Global.ControlSystem);
return new HdMdNxM4kEController(key, name, chassis, properties);
}
return null;
}
catch (Exception e)
{
Debug.Console(0, "ERROR Creating device key {0}: \r{1}", key, e);
return null;
}
}
}
}

View File

@@ -47,6 +47,13 @@ namespace PepperDash.Essentials.DM
return PepperDash.Essentials.DM.DmRmcHelper.GetDmRmcController(key, name, type, props);
}
else if (typeName.Equals("hdmd4x14ke"))
{
var props = JsonConvert.DeserializeObject
<PepperDash.Essentials.DM.Config.HdMdNxM4kEPropertiesConfig>(properties.ToString());
return PepperDash.Essentials.DM.Chassis.HdMdNxM4kEController.GetController(key, name, type, props);
}
return null;
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
using PepperDash.Core;
namespace PepperDash.Essentials.DM.Config
{
/// <summary>
/// Defines the properties section of HdMdNxM boxes
/// </summary>
public class HdMdNxM4kEPropertiesConfig
{
[JsonProperty("control")]
public ControlPropertiesConfig Control { get; set; }
[JsonProperty("inputs")]
public Dictionary<string, InputPropertiesConfig> Inputs { get; set; }
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.DM.Config
{
public class InputPropertiesConfig
{
public string Name { get; set; }
public bool DisableHdcp { get; set; }
}
}

View File

@@ -55,13 +55,9 @@
<HintPath>..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll</HintPath>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="PepperDash_Core, Version=1.0.0.15153, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="PepperDash_Core, Version=1.0.4.20530, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\pepperdash-simplsharp-core\Pepperdash Core\CLZ Builds\PepperDash_Core.dll</HintPath>
</Reference>
<Reference Include="PepperDash_Essentials_Core, Version=1.0.0.24289, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Essentials Core\PepperDashEssentialsBase\bin\PepperDash_Essentials_Core.dll</HintPath>
<HintPath>..\..\..\PepperDash.Core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll</HintPath>
</Reference>
<Reference Include="SimplSharpCustomAttributesInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -95,9 +91,12 @@
<Compile Include="Cards REMOVE\DmInputCardBase.cs" />
<Compile Include="Chassis\DmCardAudioOutput.cs" />
<Compile Include="Chassis\DmChassisController.cs" />
<Compile Include="Chassis\HdMdNxM4kEController.cs" />
<Compile Include="Config\DmRmcConfig.cs" />
<Compile Include="Config\DmTxConfig.cs" />
<Compile Include="Config\DMChassisConfig.cs" />
<Compile Include="Config\HdMdNxM4kEPropertiesConfig.cs" />
<Compile Include="Config\InputPropertiesConfig.cs" />
<Compile Include="DmPortName.cs" />
<Compile Include="Endpoints\Receivers\DmRmc4KScalerCController.cs" />
<Compile Include="Endpoints\Receivers\DmRmcScalerCController.cs" />
@@ -115,6 +114,12 @@
<Compile Include="VideoStatusHelpers.cs" />
<None Include="Properties\ControlSystem.cfg" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Essentials Core\PepperDashEssentialsBase\PepperDash_Essentials_Core.csproj">
<Project>{A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5}</Project>
<Name>PepperDash_Essentials_Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CompactFramework.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>

View File

@@ -4,5 +4,4 @@
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Essentials_DM")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyVersion("1.0.0.*")]
[assembly: AssemblyVersion("1.0.3.*")]

View File

@@ -0,0 +1,740 @@
<?xml version="1.0" encoding="utf-8"?>
<ClassDiagram MajorVersion="1" MinorVersion="1">
<Class Name="PepperDash.Essentials.Devices.Common.GenericAudioOut" Collapsed="true">
<Position X="0.5" Y="7.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAA=</HashCode>
<FileName>Audio\GenericAudioOut.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.GenericAudioOutWithVolume" Collapsed="true">
<Position X="0.5" Y="9" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAgAAAAQA=</HashCode>
<FileName>Audio\GenericAudioOut.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.CenRfgwController" Collapsed="true">
<Position X="19" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAIIAAAAAAACABAAAAAAAAAAAA=</HashCode>
<FileName>Crestron\Gateways\CenRfgwController.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.IRBlurayBase" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="17.25" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>gUyjAAoIAAAAxgYAABAAgAECABAoAEAQqcCAQHIABAI=</HashCode>
<FileName>DiscPlayer\IRDiscPlayerBase.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.DeviceFactory" Collapsed="true">
<Position X="15.5" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAA=</HashCode>
<FileName>Factory\DeviceFactory.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.GenericSource" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="12" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAgAAAAAAAACAAAAAAAAAAAAAAAgAAAAAAAAAAIAAAA=</HashCode>
<FileName>Generic\GenericSource.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.InRoomPc" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="15.5" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAwAAAAAAAACAAAAAAACAAAAAQAgAAAAAAAAAAIAAAA=</HashCode>
<FileName>PC\InRoomPc.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Laptop" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="22.5" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAwAAAAAAAACAAAAAAACAAAAAQAgAAAAAAAAAAIAAAA=</HashCode>
<FileName>PC\Laptop.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.IRSetTopBoxBase" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="19" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AUgjCAqAEAASwgYAACAAoAECABAoAEAwrcCAQHKABAI=</HashCode>
<FileName>SetTopBox\IRSetTopBoxBase.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.SetTopBoxPropertiesConfig" Collapsed="true">
<Position X="13.75" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAEEAAQAAAAAAAAIAAAAAAAAAAAAAAAAACAAAA=</HashCode>
<FileName>SetTopBox\SetTopBoxPropertiesConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.AppleTV" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="10.25" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAggAAIAAAAARAAAAAAAgAACABAoAEAQqACAQAAABAI=</HashCode>
<FileName>Streaming\AppleTV.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Roku2" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="10.25" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAggAAIAAAAABAAAAAAAgAACABAoAEAQqACAQAAABAI=</HashCode>
<FileName>Streaming\Roku.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem" Collapsed="true">
<Position X="19" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAEAAAAAAAAAOAAAAQAAAAAAAAAAQAAAAAAAAA=</HashCode>
<FileName>Codec\CodecActiveCallItem.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.CodecCallStatusItemChangeEventArgs" Collapsed="true">
<Position X="15.5" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Codec\CodecActiveCallItem.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.CodecCallDirection" Collapsed="true">
<Position X="20.75" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAA=</HashCode>
<FileName>Codec\eCodecCallDirection.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.CodecCallStatus" Collapsed="true">
<Position X="13.75" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAA=</HashCode>
<FileName>Codec\eCodecCallStatus.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.CodecCallType" Collapsed="true">
<Position X="17.25" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAA=</HashCode>
<FileName>Codec\eCodecCallType.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.CodecCallPrivacy" Collapsed="true">
<Position X="12" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAA=</HashCode>
<FileName>Codec\eMeetingPrivacy.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.VideoCodecInfo" Collapsed="true">
<Position X="5.5" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>gIAAAAAAAAAAAACCAACAAAAAAAAAAAACAAAAAABAAAA=</HashCode>
<FileName>Codec\iCodecInfo.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.CodecCallFavorites" Collapsed="true">
<Position X="22.5" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAA=</HashCode>
<FileName>Codec\iHasCallFavorites.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.CodecCallHistory" Collapsed="true">
<Position X="10.25" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAQAAAAAgAAAAAAAAAAAAAAGAAAABAAAAAAACQ=</HashCode>
<FileName>Codec\iHasCallHistory.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.DirectoryEventArgs" Collapsed="true">
<Position X="17.25" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Codec\iHasDirectory.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.CodecDirectory" Collapsed="true">
<Position X="20.75" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAIAAAAAAAAAAAABAAAAAIAAIAAQAAAAAA=</HashCode>
<FileName>Codec\iHasDirectory.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.DirectoryItem" Collapsed="true">
<Position X="6.75" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Codec\iHasDirectory.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.DirectoryFolder" Collapsed="true">
<Position X="5.5" Y="1.75" Width="1.5" />
<TypeIdentifier>
<HashCode>BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAA=</HashCode>
<FileName>Codec\iHasDirectory.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.DirectoryContact" Collapsed="true">
<Position X="7.75" Y="1.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAgAAAAAAAEAAAAAAAAAAAAAAAAEIAAAAAAAAAAA=</HashCode>
<FileName>Codec\iHasDirectory.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.ContactMethod" Collapsed="true">
<Position X="13.75" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAIAAAAAAAgAAAEAAAAAAAAAIAAAAAAAAAAAAAAA=</HashCode>
<FileName>Codec\iHasDirectory.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.DirectorySearchResultEventArgs" Collapsed="true">
<Position X="19" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Codec\iHasDirectory.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.CodecScheduleAwareness" Collapsed="true">
<Position X="10.25" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAABAAAAAEAAAAAAAgAAEAAAABAAAAAAAIQAAAAA=</HashCode>
<FileName>Codec\iHasScheduleAwareness.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.Meeting" Collapsed="true">
<Position X="13.75" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAAAAACEEgAAAAAAAgQIAAoAQABAAQAAEgAAAA=</HashCode>
<FileName>Codec\iHasScheduleAwareness.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.Call" Collapsed="true">
<Position X="17.25" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAEAAAABAAAAIAAAAAAAAAAgAAAA=</HashCode>
<FileName>Codec\iHasScheduleAwareness.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.MeetingEventArgs" Collapsed="true">
<Position X="15.5" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAQAAAAA=</HashCode>
<FileName>Codec\iHasScheduleAwareness.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.CiscoSparkCodecPropertiesConfig" Collapsed="true">
<Position X="17.25" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAQAAAAAAAAAAAAAAAAAIQAAACAABAAAAA=</HashCode>
<FileName>VideoCodec\CiscoCodec\CiscoSparkCodecPropertiesConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Codec.SharingProperties" Collapsed="true">
<Position X="15.5" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAA=</HashCode>
<FileName>VideoCodec\CiscoCodec\CiscoSparkCodecPropertiesConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Displays.AvocorDisplay" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="12" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>hC4gIEAIUAABoQAEgBACAAAdBC1gmsGRICKGdQNBACw=</HashCode>
<FileName>Display\AvocorVTFDisplay.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Displays.ComTcpDisplayBase" Collapsed="true">
<Position X="6.5" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAEQAAAAA=</HashCode>
<FileName>Display\ComTcpDisplayBase.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Displays.DisplayDeviceFactory" Collapsed="true">
<Position X="20.75" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAA=</HashCode>
<FileName>Display\DeviceFactory.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Displays.NecPaSeriesProjector" Collapsed="true">
<Position X="6.5" Y="8" Width="1.5" />
<TypeIdentifier>
<HashCode>ogSIAAKIAAALCAAEABAQEAgABAAAAMIAAAAAEABAACA=</HashCode>
<FileName>Display\NecPaSeriesProjector.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Displays.NecPSXMDisplay" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="22.5" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>hLQgIShIAABFQQRUAvICDRMQESwSEQKRKACCZQNAESY=</HashCode>
<FileName>Display\NECPSXMDisplay.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Displays.SamsungMDC" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="12" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>hKwwIEAIQAABoUAGgBACAAAEBGxAmYORKCCGZQJAACw=</HashCode>
<FileName>Display\SamsungMDCDisplay.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.DSP.BiampTesiraForteDsp" Collapsed="true">
<Position X="3.5" Y="8" Width="1.5" />
<TypeIdentifier>
<HashCode>gACAAIAAAAEAQYCigAAAAAAQAACgCAAAAAAAAAMAAAI=</HashCode>
<FileName>DSP\BiampTesira\BiampTesiraForteDsp.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.DSP.TesiraForteLevelControl" Collapsed="true">
<Position X="0.5" Y="6.25" Width="1.5" />
<TypeIdentifier>
<HashCode>RAAgJACAAAAAAYAAAkIAAAAAIAQCEACJABACRAAAAUQ=</HashCode>
<FileName>DSP\BiampTesira\BiampTesiraForteDspLevel.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.DSP.BiampTesiraFortePropertiesConfig" Collapsed="true">
<Position X="15.5" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>BAAAAAAEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAA=</HashCode>
<FileName>DSP\BiampTesira\BiampTesiraFortePropertiesConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.DSP.BiampTesiraForteLevelControlBlockConfig" Collapsed="true">
<Position X="13.75" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAEAAAAIAAAAAAAAAAAAAAAAAIAAEAAAAAAEQ=</HashCode>
<FileName>DSP\BiampTesira\BiampTesiraFortePropertiesConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.DSP.TesiraForteControlPoint" Collapsed="true">
<Position X="0.5" Y="4.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAIAAAAACAAQQAAAAAQAAAAAAAIAAAABEAAAAAAEA=</HashCode>
<FileName>DSP\BiampTesira\TesiraForteControlPoint.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.DSP.DspBase" Collapsed="true">
<Position X="3.5" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAEAIAgAAAAAAIAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>DSP\DspBase.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.DSP.DspControlPoint" Collapsed="true">
<Position X="1.75" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>DSP\DspBase.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.DSP.DspMuteControl" Collapsed="true">
<Position X="2.75" Y="4.75" Width="1.5" />
<TypeIdentifier>
<HashCode>BAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAQAAAAAQ=</HashCode>
<FileName>DSP\DspBase.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.DSP.SoundStructureBasics" Collapsed="true">
<Position X="17.25" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAA=</HashCode>
<FileName>DSP\PolycomSoundStructure\SoundStructureBasics.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Environment.Lutron.LutronQuantumArea" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="10.25" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>gACAEBAAAoAABQBAASAAAAAAAAEgAAACAAACAAMAQAI=</HashCode>
<FileName>Environment\Lutron\LutronQuantum.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Environment.Lutron.LutronQuantumPropertiesConfig" Collapsed="true">
<Position X="12" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAEAAAAAAAAgQAAAAAAAAAAAAQCAAACAAAAAAA=</HashCode>
<FileName>Environment\Lutron\LutronQuantum.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController" Collapsed="true">
<Position X="17.25" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>gQAABAAAAxAAIACAgAgAgCBAUQAAQAgCIAEQAACBAAA=</HashCode>
<FileName>Microphone\MicrophonePrivacyController.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyControllerConfig" Collapsed="true">
<Position X="19" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAEAAAAAgAIAAAAAAAAAA=</HashCode>
<FileName>Microphone\MicrophonePrivacyControllerConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Microphones.KeyedDevice" Collapsed="true">
<Position X="20.75" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Microphone\MicrophonePrivacyControllerConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Occupancy.EssentialsGlsOccupancySensorBaseController" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="22.5" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAIAAAAAAAABAABgAAAAAASEABAAAAAAAAA=</HashCode>
<FileName>Occupancy\EssentialsGlsOccupancySensorBaseController.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.Occupancy.EssentialsOccupancyAggregator" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="10.25" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACBAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Occupancy\EssentialsOccupancyAggregator.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.CiscoCodecBookings" Collapsed="true">
<Position X="22.5" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg=</HashCode>
<FileName>VideoCodec\CiscoCodec\BookingsDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.CiscoCallHistory" Collapsed="true">
<Position X="20.75" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>VideoCodec\CiscoCodec\CallHistoryDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.CiscoCodecPhonebook" Collapsed="true">
<Position X="13.75" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>EAAAABAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAA=</HashCode>
<FileName>VideoCodec\CiscoCodec\PhonebookDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.MockVC" Collapsed="true">
<Position X="0.5" Y="2" Width="1.5" />
<TypeIdentifier>
<HashCode>BgYAIAACAQaKBACAAwAAUSQAAWYCEACDAAiAQBBCgQU=</HashCode>
<FileName>VideoCodec\MockVC\MockVC.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.MockCodecInfo" Collapsed="true">
<Position X="5.5" Y="4.75" Width="1.5" />
<TypeIdentifier>
<HashCode>gKAAAAAAAAAAAACCAACAAAAAAAAAAAACAAAAAABBAAA=</HashCode>
<FileName>VideoCodec\MockVC\MockVC.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.MockVcPropertiesConfig" Collapsed="true">
<Position X="20.75" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAA=</HashCode>
<FileName>VideoCodec\MockVC\MockVcPropertiesConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.VideoCodecBase" Collapsed="true">
<Position X="1.75" Y="2" Width="1.5" />
<TypeIdentifier>
<HashCode>BiwgAAECAASAAECAgQEDIABAAAYiEBCpCEigQBZCAQU=</HashCode>
<FileName>VideoCodec\VideoCodecBase.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.CodecPhonebookSyncState" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="22.5" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>EAiCAAQAAgEAAQQAEAAAAAAAAAEAAAAAAACAAAAAAAM=</HashCode>
<FileName>VideoCodec\VideoCodecBase.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.CiscoSparkCodec" Collapsed="true">
<Position X="2.75" Y="2" Width="1.5" />
<TypeIdentifier>
<HashCode>jhQEtAJaASb7kSCwAwtxECSABsf2n1GBJEmAVJFKWTc=</HashCode>
<FileName>VideoCodec\CiscoCodec\CiscoSparkCodec.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.CodecCommandWithLabel" Collapsed="true">
<Position X="19" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAEAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>VideoCodec\CiscoCodec\CiscoSparkCodec.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.CodecSyncState" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="12" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AIAAAAQACAEAAAQAEAAAAAAAAAAgAAAAAAAACAACACE=</HashCode>
<FileName>VideoCodec\CiscoCodec\CiscoSparkCodec.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.HttpApiServer" Collapsed="true">
<Position X="13.75" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAQAAAACAAAAABAAAAAAAIAIAAAAAAAQAAgAAAAAA=</HashCode>
<FileName>VideoCodec\CiscoCodec\HttpApiServer.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.CiscoCodecConfiguration" Collapsed="true">
<Position X="10.25" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>VideoCodec\CiscoCodec\xConfiguration.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.CiscoCodecEvents" Collapsed="true">
<Position X="12" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>VideoCodec\CiscoCodec\xEvent.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.CiscoCodecStatus" Collapsed="true">
<Position X="15.5" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>VideoCodec\CiscoCodec\xStatus.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Devices.Common.VideoCodec.CiscoCodec.xStatusSparkPlus" Collapsed="true">
<Position X="19" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>VideoCodec\CiscoCodec\xStatusSparkPlus.cs</FileName>
</TypeIdentifier>
</Class>
<Interface Name="PepperDash.Essentials.Devices.Common.Codec.ICodecAudio" Collapsed="true">
<Position X="10.25" Y="7.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Codec\iCodecAudio.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Common.Codec.iCodecInfo" Collapsed="true">
<Position X="12" Y="7.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Codec\iCodecInfo.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Common.Codec.IHasCallFavorites" Collapsed="true">
<Position X="15.5" Y="7.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Codec\iHasCallFavorites.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Common.Codec.IHasCallHistory" Collapsed="true">
<Position X="17.25" Y="7.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Codec\iHasCallHistory.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Common.Codec.IHasContentSharing" Collapsed="true">
<Position X="22.5" Y="7.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAACAAAAAAAAAAAAAAAAAAAAAAAIAEAgABAAAAA=</HashCode>
<FileName>Codec\iHasContentSharing.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Common.Codec.IHasDialer" Collapsed="true">
<Position X="10.25" Y="8.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAgAAAACAAAAAACAAAAAAAAAAAICAAAAAAAAAAQCAAA=</HashCode>
<FileName>Codec\iHasDialer.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Common.Codec.IHasDirectory" Collapsed="true">
<Position X="12" Y="8.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAgAAAAAAAAAAAAgCAAAAABAAAEAAAAAA=</HashCode>
<FileName>Codec\iHasDirectory.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Common.Codec.IHasScheduleAwareness" Collapsed="true">
<Position X="13.75" Y="8.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAACQAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Codec\iHasScheduleAwareness.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Displays.IInputHdmi1" Collapsed="true">
<Position X="19" Y="8.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=</HashCode>
<FileName>Display\InputInterfaces.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Displays.IInputHdmi2" Collapsed="true">
<Position X="20.75" Y="8.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAA=</HashCode>
<FileName>Display\InputInterfaces.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Displays.IInputHdmi3" Collapsed="true">
<Position X="22.5" Y="8.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAA=</HashCode>
<FileName>Display\InputInterfaces.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Displays.IInputHdmi4" Collapsed="true">
<Position X="10.25" Y="9.25" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA=</HashCode>
<FileName>Display\InputInterfaces.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Displays.IInputDisplayPort1" Collapsed="true">
<Position X="15.5" Y="8.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAA=</HashCode>
<FileName>Display\InputInterfaces.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Displays.IInputDisplayPort2" Collapsed="true">
<Position X="17.25" Y="8.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAA=</HashCode>
<FileName>Display\InputInterfaces.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Displays.IInputVga1" Collapsed="true">
<Position X="12" Y="9.25" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Display\InputInterfaces.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Common.DSP.IDspLevelControl" Collapsed="true">
<Position X="13.75" Y="7.75" Width="1.5" />
<TypeIdentifier>
<HashCode>QAAAAACAAAAAAAAAAAAAAAAAAAAAAAAIAAEAAAAAAAQ=</HashCode>
<FileName>DSP\DspBase.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Common.Occupancy.IOccupancyStatusProvider" Collapsed="true">
<Position X="13.75" Y="9.25" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Occupancy\iOccupancyStatusProvider.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Common.VideoCodec.IHasCodecLayouts" Collapsed="true">
<Position X="19" Y="7.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAgAAAAAAAAAAAAAAAEEgAAAAAAAAAAAAA=</HashCode>
<FileName>VideoCodec\Interfaces\IHasCodecLayouts.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Devices.Common.VideoCodec.IHasCodecSelfview" Collapsed="true">
<Position X="20.75" Y="7.75" Width="1.5" />
<TypeIdentifier>
<HashCode>ABAAEAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAABAAAAI=</HashCode>
<FileName>VideoCodec\Interfaces\IHasCodecSelfview.cs</FileName>
</TypeIdentifier>
</Interface>
<Enum Name="PepperDash.Essentials.Devices.Common.eExGatewayType" Collapsed="true">
<Position X="10.25" Y="11" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAgAAAAAAAAAAAAAIAAAEAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Crestron\Gateways\CenRfgwController.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.Devices.Common.Codec.eCodecCallDirection" Collapsed="true">
<Position X="12" Y="10.25" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAAAAAABA=</HashCode>
<FileName>Codec\eCodecCallDirection.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.Devices.Common.Codec.eCodecCallStatus" Collapsed="true">
<Position X="13.75" Y="10.25" Width="1.5" />
<TypeIdentifier>
<HashCode>CAgAAABAAAAAQAACAAABAAAAAAAAAAABCAAAAiAAQBA=</HashCode>
<FileName>Codec\eCodecCallStatus.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.Devices.Common.Codec.eCodecCallType" Collapsed="true">
<Position X="15.5" Y="10.25" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAoBg=</HashCode>
<FileName>Codec\eCodecCallType.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.Devices.Common.Codec.eMeetingPrivacy" Collapsed="true">
<Position X="13.75" Y="11" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAQBA=</HashCode>
<FileName>Codec\eMeetingPrivacy.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.Devices.Common.Codec.eCodecOccurrenceType" Collapsed="true">
<Position X="17.25" Y="10.25" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAABAAAAAIAAAAAAAAAAAAAAAAAAAEAAAAAAABA=</HashCode>
<FileName>Codec\iHasCallHistory.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.Devices.Common.Codec.eContactMethodDevice" Collapsed="true">
<Position X="22.5" Y="10.25" Width="1.5" />
<TypeIdentifier>
<HashCode>BAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAIAAAAAAABg=</HashCode>
<FileName>Codec\iHasDirectory.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.Devices.Common.Codec.eContactMethodCallType" Collapsed="true">
<Position X="20.75" Y="10.25" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAABg=</HashCode>
<FileName>Codec\iHasDirectory.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.Devices.Common.Codec.eMeetingEventChangeType" Collapsed="true">
<Position X="12" Y="11" Width="1.5" />
<TypeIdentifier>
<HashCode>BAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAIACg=</HashCode>
<FileName>Codec\iHasScheduleAwareness.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.Devices.Common.Environment.Lutron.eAction" Collapsed="true">
<Position X="10.25" Y="10.25" Width="1.5" />
<TypeIdentifier>
<HashCode>AACAACAAABAAgIAAgAAQAAAAAAAAIAAAIAAAACAEAAg=</HashCode>
<FileName>Environment\Lutron\LutronQuantum.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.eCommandType" Collapsed="true">
<Position X="19" Y="10.25" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAACAQAAAAABAAEAAAAAAAAgAAAAAAAAAA=</HashCode>
<FileName>VideoCodec\CiscoCodec\CiscoSparkCodec.cs</FileName>
</TypeIdentifier>
</Enum>
<Font Name="Segoe UI" Size="9" />
</ClassDiagram>

View File

@@ -38,4 +38,17 @@ namespace PepperDash.Essentials.Devices.Common.Codec
}
}
}
/// <summary>
///
/// </summary>
public class CodecCallStatusItemChangeEventArgs : EventArgs
{
public CodecActiveCallItem CallItem { get; private set; }
public CodecCallStatusItemChangeEventArgs(CodecActiveCallItem item)
{
CallItem = item;
}
}
}

View File

@@ -24,27 +24,7 @@ namespace PepperDash.Essentials.Devices.Common.Codec
void RejectCall(CodecActiveCallItem item);
void SendDtmf(string digit);
//BoolFeedback IncomingCallFeedback { get; }
bool IsInCall { get; }
}
/// <summary>
///
/// </summary>
public class CodecCallStatusItemChangeEventArgs : EventArgs
{
public CodecActiveCallItem CallItem { get; private set; }
//public eCodecCallStatus PreviousStatus { get; private set; }
//public eCodecCallStatus NewStatus { get; private set; }
public CodecCallStatusItemChangeEventArgs(/*eCodecCallStatus previousStatus,
eCodecCallStatus newStatus,*/ CodecActiveCallItem item)
{
//PreviousStatus = previousStatus;
//NewStatus = newStatus;
CallItem = item;
}
}
}

View File

@@ -30,6 +30,9 @@ namespace PepperDash.Essentials.Devices.Common.Codec
public event EventHandler<EventArgs> MeetingsListHasChanged;
/// <summary>
/// Setter triggers MeetingsListHasChanged event
/// </summary>
public List<Meeting> Meetings
{
get
@@ -131,7 +134,7 @@ namespace PepperDash.Essentials.Devices.Common.Codec
get
{
return StartTime.AddMinutes(-5) <= DateTime.Now
&& DateTime.Now <= EndTime.AddMinutes(-5);
&& DateTime.Now <= EndTime; //.AddMinutes(-5);
}
}
//public string ConferenceNumberToDial { get; set; }

View File

@@ -0,0 +1,720 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;
using Crestron.SimplSharpPro;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Routing;
namespace PepperDash.Essentials.Devices.Displays
{
/// <summary>
///
/// </summary>
public class AvocorDisplay : TwoWayDisplayBase, IBasicVolumeWithFeedback, ICommunicationMonitor, IInputDisplayPort1,
IInputHdmi1, IInputHdmi2, IInputHdmi3, IInputHdmi4, IInputVga1
{
public IBasicCommunication Communication { get; private set; }
public CommunicationGather PortGather { get; private set; }
public StatusMonitorBase CommunicationMonitor { get; private set; }
public byte ID { get; private set; }
/// <summary>
/// 0x08
/// </summary>
const byte InputVga1Value = 0x00;
/// <summary>
/// 0x09
/// </summary>
const byte InputHdmi1Value = 0x09;
/// <summary>
/// 0x0a
/// </summary>
const byte InputHdmi2Value = 0x0a;
/// <summary>
/// 0x0b
/// </summary>
const byte InputHdmi3Value = 0x0b;
/// <summary>
/// 0x0c
/// </summary>
const byte InputHdmi4Value = 0x0c;
/// <summary>
/// 0c0d
/// </summary>
const byte InputDisplayPort1Value = 0x0d;
/// <summary>
/// 0x0e
/// </summary>
const byte InputIpcOpsValue = 0x0e;
/// <summary>
/// 0x11
/// </summary>
const byte InputHdmi5Value = 0x11;
/// <summary>
/// 0x12
/// </summary>
const byte InputMediaPlayerValue = 0x12;
bool _PowerIsOn;
bool _IsWarmingUp;
bool _IsCoolingDown;
ushort _VolumeLevelForSig;
int _LastVolumeSent;
ushort _PreMuteVolumeLevel;
bool _IsMuted;
RoutingInputPort _CurrentInputPort;
ActionIncrementer VolumeIncrementer;
bool VolumeIsRamping;
public bool IsInStandby { get; private set; }
protected override Func<bool> PowerIsOnFeedbackFunc { get { return () => _PowerIsOn; } }
protected override Func<bool> IsCoolingDownFeedbackFunc { get { return () => _IsCoolingDown; } }
protected override Func<bool> IsWarmingUpFeedbackFunc { get { return () => _IsWarmingUp; } }
protected override Func<string> CurrentInputFeedbackFunc { get { return () => _CurrentInputPort.Key; } }
/// <summary>
/// Constructor for IBasicCommunication
/// </summary>
public AvocorDisplay(string key, string name, IBasicCommunication comm, string id)
: base(key, name)
{
Communication = comm;
//Communication.BytesReceived += new EventHandler<GenericCommMethodReceiveBytesArgs>(Communication_BytesReceived);
PortGather = new CommunicationGather(Communication, '\x08');
PortGather.IncludeDelimiter = true;
PortGather.LineReceived += new EventHandler<GenericCommMethodReceiveTextArgs>(PortGather_LineReceived);
ID = id == null ? (byte)0x01 : Convert.ToByte(id, 16); // If id is null, set default value of 0x01, otherwise assign value passed in constructor
Init();
}
/// <summary>
/// Constructor for TCP
/// </summary>
public AvocorDisplay(string key, string name, string hostname, int port, string id)
: base(key, name)
{
Communication = new GenericTcpIpClient(key + "-tcp", hostname, port, 5000);
PortGather = new CommunicationGather(Communication, '\x0d');
PortGather.IncludeDelimiter = true;
PortGather.LineReceived += new EventHandler<GenericCommMethodReceiveTextArgs>(PortGather_LineReceived);
ID = id == null ? (byte)0x01 : Convert.ToByte(id, 16); // If id is null, set default value of 0x01, otherwise assign value passed in constructor
Init();
}
/// <summary>
/// Constructor for COM
/// </summary>
public AvocorDisplay(string key, string name, ComPort port, ComPort.ComPortSpec spec, string id)
: base(key, name)
{
Communication = new ComPortController(key + "-com", port, spec);
PortGather = new CommunicationGather(Communication, '\x0d');
PortGather.IncludeDelimiter = true;
PortGather.LineReceived += new EventHandler<GenericCommMethodReceiveTextArgs>(PortGather_LineReceived);
ID = id == null ? (byte)0x01 : Convert.ToByte(id, 16); // If id is null, set default value of 0x01, otherwise assign value passed in constructor
Init();
}
void AddRoutingInputPort(RoutingInputPort port, byte fbMatch)
{
port.FeedbackMatchObject = fbMatch;
InputPorts.Add(port);
}
void Init()
{
WarmupTime = 10000;
CooldownTime = 8000;
CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, StatusGet);
DeviceManager.AddDevice(CommunicationMonitor);
VolumeIncrementer = new ActionIncrementer(655, 0, 65535, 800, 80,
v => SetVolume((ushort)v),
() => _LastVolumeSent);
AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, new Action(InputHdmi1), this), InputHdmi1Value);
AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, new Action(InputHdmi2), this), InputHdmi2Value);
AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, new Action(InputHdmi3), this), InputHdmi3Value);
AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn4, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, new Action(InputHdmi4), this), InputHdmi4Value);
AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn5, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, new Action(InputHdmi5), this), InputHdmi5Value);
AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.DisplayPortIn1, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.DisplayPort, new Action(InputDisplayPort1), this), InputDisplayPort1Value);
AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.VgaIn, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Dvi, new Action(InputVga1), this), InputVga1Value);
AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.IpcOps, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Composite, new Action(InputIpcOps), this), InputIpcOpsValue);
AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.MediaPlayer, eRoutingSignalType.Video,
eRoutingPortConnectionType.Vga, new Action(InputMediaPlayer), this), InputMediaPlayerValue);
VolumeLevelFeedback = new IntFeedback(() => { return _VolumeLevelForSig; });
MuteFeedback = new BoolFeedback(() => _IsMuted);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override bool CustomActivate()
{
Communication.Connect();
var socket = Communication as ISocketStatus;
if (socket != null)
{
socket.ConnectionChange += new EventHandler<GenericSocketStatusChageEventArgs>(socket_ConnectionChange);
}
CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); };
CommunicationMonitor.Start();
StatusGet();
return true;
}
void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e)
{
if (e.Client.IsConnected)
StatusGet();
}
public override List<Feedback> Feedbacks
{
get
{
var list = base.Feedbacks;
list.AddRange(new List<Feedback>
{
VolumeLevelFeedback,
MuteFeedback,
CurrentInputFeedback
});
return list;
}
}
///// <summary>
///// /
///// </summary>
///// <param name="sender"></param>
//void Communication_BytesReceived(object sender, GenericCommMethodReceiveBytesArgs e)
//{
// // This is probably not thread-safe buffering
// // Append the incoming bytes with whatever is in the buffer
// var newBytes = new byte[IncomingBuffer.Length + e.Bytes.Length];
// IncomingBuffer.CopyTo(newBytes, 0);
// e.Bytes.CopyTo(newBytes, IncomingBuffer.Length);
// if (Debug.Level == 2) // This check is here to prevent following string format from building unnecessarily on level 0 or 1
// Debug.Console(2, this, "Received:{0}", ComTextHelper.GetEscapedText(newBytes));
// // Need to find AA FF and have
// for (int i = 0; i < newBytes.Length; i++)
// {
// if (newBytes[i] == 0xAA && newBytes[i + 1] == 0xFF)
// {
// newBytes = newBytes.Skip(i).ToArray(); // Trim off junk if there's "dirt" in the buffer
// // parse it
// // If it's at least got the header, then process it,
// while (newBytes.Length > 4 && newBytes[0] == 0xAA && newBytes[1] == 0xFF)
// {
// var msgLen = newBytes[3];
// // if the buffer is shorter than the header (3) + message (msgLen) + checksum (1),
// // give and save it for next time
// if (newBytes.Length < msgLen + 4)
// break;
// // Good length, grab the message
// var message = newBytes.Skip(4).Take(msgLen).ToArray();
// // At this point, the ack/nak is the first byte
// if (message[0] == 0x41)
// {
// switch (message[1]) // type byte
// {
// case 0x00: // General status
// UpdatePowerFB(message[2], message[5]); // "power" can be misrepresented when the display sleeps
// UpdateInputFb(message[5]);
// UpdateVolumeFB(message[3]);
// UpdateMuteFb(message[4]);
// UpdateInputFb(message[5]);
// break;
// case 0x11:
// UpdatePowerFB(message[2]);
// break;
// case 0x12:
// UpdateVolumeFB(message[2]);
// break;
// case 0x13:
// UpdateMuteFb(message[2]);
// break;
// case 0x14:
// UpdateInputFb(message[2]);
// break;
// default:
// break;
// }
// }
// // Skip over what we've used and save the rest for next time
// newBytes = newBytes.Skip(5 + msgLen).ToArray();
// }
// break; // parsing will mean we can stop looking for header in loop
// }
// }
// // Save whatever partial message is here
// IncomingBuffer = newBytes;
//}
void PortGather_LineReceived(object sender, GenericCommMethodReceiveTextArgs e)
{
Debug.Console(1, this, "Receivied: '{0}'", ComTextHelper.GetEscapedText(e.Text));
if (e.Text.IndexOf("\x50\x4F\x57") > -1)
{
// Power Status Response
var value = e.Text.ToCharArray();
switch (value[6])
{
case '\x00':
{
_PowerIsOn = false;
break;
}
case '\x01':
{
_PowerIsOn = true;
break;
}
}
PowerIsOnFeedback.FireUpdate();
Debug.Console(1, this, "PowerIsOn State: {0}", PowerIsOnFeedback.BoolValue);
}
else if (e.Text.IndexOf("\x4D\x49\x4E") > -1)
{
var value = e.Text.ToCharArray();
var b = value[6];
var newInput = InputPorts.FirstOrDefault(i => i.FeedbackMatchObject.Equals(b));
if (newInput != null && newInput != _CurrentInputPort)
{
_CurrentInputPort = newInput;
CurrentInputFeedback.FireUpdate();
Debug.Console(1, this, "Current Input: {0}", CurrentInputFeedback.StringValue);
}
}
else if (e.Text.IndexOf("\x56\x4F\x4C") > -1)
{
// Volume Status Response
var value = e.Text.ToCharArray();
var b = value[6];
var newVol = (ushort)NumericalHelpers.Scale((double)b, 0, 100, 0, 65535);
if (!VolumeIsRamping)
_LastVolumeSent = newVol;
if (newVol != _VolumeLevelForSig)
{
_VolumeLevelForSig = newVol;
VolumeLevelFeedback.FireUpdate();
if (_VolumeLevelForSig > 0)
_IsMuted = false;
else
_IsMuted = true;
MuteFeedback.FireUpdate();
Debug.Console(1, this, "Volume Level: {0}", VolumeLevelFeedback.IntValue);
}
}
}
/// <summary>
///
/// </summary>
void UpdatePowerFB(byte powerByte)
{
var newVal = powerByte == 1;
if (newVal != _PowerIsOn)
{
_PowerIsOn = newVal;
PowerIsOnFeedback.FireUpdate();
}
}
/// <summary>
/// Updates power status from general updates where source is included.
/// Compensates for errant standby / power off hiccups by ignoring
/// power off states with input < 0x10
/// </summary>
void UpdatePowerFB(byte powerByte, byte inputByte)
{
// This should reject errant power feedbacks when switching away from input on standby.
if (powerByte == 0x01 && inputByte < 0x10)
IsInStandby = true;
if (powerByte == 0x00 && IsInStandby) // Ignore power off if coming from standby - glitch
{
IsInStandby = false;
return;
}
UpdatePowerFB(powerByte);
}
/// <summary>
///
/// </summary>
void UpdateVolumeFB(byte b)
{
var newVol = (ushort)NumericalHelpers.Scale((double)b, 0, 100, 0, 65535);
if (!VolumeIsRamping)
_LastVolumeSent = newVol;
if (newVol != _VolumeLevelForSig)
{
_VolumeLevelForSig = newVol;
VolumeLevelFeedback.FireUpdate();
}
}
/// <summary>
///
/// </summary>
void UpdateMuteFb(byte b)
{
var newMute = b == 1;
if (newMute != _IsMuted)
{
_IsMuted = newMute;
MuteFeedback.FireUpdate();
}
}
/// <summary>
///
/// </summary>
void UpdateInputFb(byte b)
{
var newInput = InputPorts.FirstOrDefault(i => i.FeedbackMatchObject.Equals(b));
if (newInput != null && newInput != _CurrentInputPort)
{
_CurrentInputPort = newInput;
CurrentInputFeedback.FireUpdate();
}
}
/// <summary>
/// Formats an outgoing message. Replaces third byte with ID and replaces last byte with checksum
/// </summary>
/// <param name="b"></param>
void SendBytes(byte[] b)
{
b[1] = ID;
var command = b.ToString();
Debug.Console(1, this, "Sending: '{0}'",ComTextHelper.GetEscapedText(b));
Communication.SendBytes(b);
}
/// <summary>
///
/// </summary>
public void StatusGet()
{
PowerGet();
CrestronEnvironment.Sleep(100);
InputGet();
CrestronEnvironment.Sleep(100);
VolumeGet();
}
/// <summary>
///
/// </summary>
public override void PowerOn()
{
//Send(PowerOnCmd);
SendBytes(new byte[] { 0x07, ID, 0x02, 0x50, 0x4F, 0x57, 0x01, 0x08, 0x0d });
if (!PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown)
{
_IsWarmingUp = true;
IsWarmingUpFeedback.FireUpdate();
// Fake power-up cycle
WarmupTimer = new CTimer(o =>
{
_IsWarmingUp = false;
_PowerIsOn = true;
IsWarmingUpFeedback.FireUpdate();
PowerIsOnFeedback.FireUpdate();
}, WarmupTime);
}
}
/// <summary>
///
/// </summary>
public override void PowerOff()
{
// If a display has unreliable-power off feedback, just override this and
// remove this check.
if (!_IsWarmingUp && !_IsCoolingDown) // PowerIsOnFeedback.BoolValue &&
{
//Send(PowerOffCmd);
SendBytes(new byte[] { 0x07, ID, 0x02, 0x50, 0x4F, 0x57, 0x00, 0x08, 0x0d });
_IsCoolingDown = true;
_PowerIsOn = false;
PowerIsOnFeedback.FireUpdate();
IsCoolingDownFeedback.FireUpdate();
// Fake cool-down cycle
CooldownTimer = new CTimer(o =>
{
_IsCoolingDown = false;
IsCoolingDownFeedback.FireUpdate();
}, CooldownTime);
}
}
public override void PowerToggle()
{
if (PowerIsOnFeedback.BoolValue && !IsWarmingUpFeedback.BoolValue)
PowerOff();
else if (!PowerIsOnFeedback.BoolValue && !IsCoolingDownFeedback.BoolValue)
PowerOn();
}
public void PowerGet()
{
SendBytes(new byte[] { 0x07, ID, 0x01, 0x50, 0x4F, 0x57, 0x08, 0x0d });
}
public void InputHdmi1()
{
SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputHdmi1Value, 0x08, 0x0d });
}
public void InputHdmi2()
{
SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputHdmi2Value, 0x08, 0x0d });
}
public void InputHdmi3()
{
SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputHdmi3Value, 0x08, 0x0d });
}
public void InputHdmi4()
{
SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputHdmi4Value, 0x08, 0x0d });
}
public void InputHdmi5()
{
SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputHdmi5Value, 0x08, 0x0d });
}
public void InputDisplayPort1()
{
SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputDisplayPort1Value, 0x08, 0x0d });
}
public void InputVga1()
{
SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputVga1Value, 0x08, 0x0d });
}
public void InputIpcOps()
{
SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputIpcOpsValue, 0x08, 0x0d });
}
public void InputMediaPlayer()
{
SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputMediaPlayerValue, 0x08, 0x0d });
}
public void InputGet()
{
SendBytes(new byte[] { 0x07, ID, 0x01, 0x4D, 0x49, 0x4E, 0x08, 0x0d });
}
public void VolumeGet()
{
SendBytes(new byte[] { 0x07, ID, 0x01, 0x56, 0x4F, 0x4C, 0x08, 0x0d });
}
/// <summary>
/// Executes a switch, turning on display if necessary.
/// </summary>
/// <param name="selector"></param>
public override void ExecuteSwitch(object selector)
{
//if (!(selector is Action))
// Debug.Console(1, this, "WARNING: ExecuteSwitch cannot handle type {0}", selector.GetType());
if (_PowerIsOn)
(selector as Action)();
else // if power is off, wait until we get on FB to send it.
{
// One-time event handler to wait for power on before executing switch
EventHandler<EventArgs> handler = null; // necessary to allow reference inside lambda to handler
handler = (o, a) =>
{
if (!_IsWarmingUp) // Done warming
{
IsWarmingUpFeedback.OutputChange -= handler;
(selector as Action)();
}
};
IsWarmingUpFeedback.OutputChange += handler; // attach and wait for on FB
PowerOn();
}
}
/// <summary>
/// Scales the level to the range of the display and sends the command
/// </summary>
/// <param name="level"></param>
public void SetVolume(ushort level)
{
_LastVolumeSent = level;
var scaled = (int)NumericalHelpers.Scale(level, 0, 65535, 0, 100);
// The inputs to Scale ensure that byte won't overflow
SendBytes(new byte[] { 0x07, ID, 0x02, 0x56, 0x4F, 0x4C, Convert.ToByte(scaled), 0x08, 0x0d });
}
#region IBasicVolumeWithFeedback Members
public IntFeedback VolumeLevelFeedback { get; private set; }
public BoolFeedback MuteFeedback { get; private set; }
/// <summary>
///
/// </summary>
public void MuteOff()
{
SetVolume(_PreMuteVolumeLevel);
}
/// <summary>
///
/// </summary>
public void MuteOn()
{
_PreMuteVolumeLevel = _VolumeLevelForSig;
SetVolume(0);
}
///// <summary>
/////
///// </summary>
//public void MuteGet()
//{
// SendBytes(new byte[] { 0x07, ID, 0x01, });
//}
#endregion
#region IBasicVolumeControls Members
/// <summary>
///
/// </summary>
public void MuteToggle()
{
if (_IsMuted)
MuteOff();
else
MuteOn();
}
/// <summary>
///
/// </summary>
/// <param name="pressRelease"></param>
public void VolumeDown(bool pressRelease)
{
if (pressRelease)
{
VolumeIncrementer.StartDown();
VolumeIsRamping = true;
}
else
{
VolumeIsRamping = false;
VolumeIncrementer.Stop();
}
}
/// <summary>
///
/// </summary>
/// <param name="pressRelease"></param>
public void VolumeUp(bool pressRelease)
{
if (pressRelease)
{
VolumeIncrementer.StartUp();
VolumeIsRamping = true;
}
else
{
VolumeIsRamping = false;
VolumeIncrementer.Stop();
}
}
#endregion
}
}

View File

@@ -36,6 +36,12 @@ namespace PepperDash.Essentials.Devices.Displays
var comm = CommFactory.CreateCommForDevice(dc);
if (comm != null)
return new SamsungMDC(dc.Key, dc.Name, comm, dc.Properties["id"].Value<string>());
}
if (typeName == "avocorvtf")
{
var comm = CommFactory.CreateCommForDevice(dc);
if (comm != null)
return new AvocorDisplay(dc.Key, dc.Name, comm, null);
}
}

View File

@@ -11,6 +11,7 @@ namespace PepperDash.Essentials.Devices.Displays
public interface IInputHdmi3 { void InputHdmi3(); }
public interface IInputHdmi4 { void InputHdmi4(); }
public interface IInputDisplayPort1 { void InputDisplayPort1(); }
public interface IInputDisplayPort2 { void InputDisplayPort2(); }
public interface IInputDisplayPort2 { void InputDisplayPort2(); }
public interface IInputVga1 { void InputVga1(); }
}

View File

@@ -23,6 +23,7 @@ namespace PepperDash.Essentials.Devices.Displays
public byte ID { get; private set; }
bool LastCommandSentWasVolume;
bool _PowerIsOn;
bool _IsWarmingUp;
@@ -99,9 +100,15 @@ namespace PepperDash.Essentials.Devices.Displays
AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, new Action(InputHdmi1), this), 0x21);
AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn1PC, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, new Action(InputHdmi1PC), this), 0x22);
AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, new Action(InputHdmi2), this), 0x23);
AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn2PC, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, new Action(InputHdmi2PC), this), 0x24);
AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, new Action(InputHdmi3), this), 0x32);
@@ -165,6 +172,9 @@ namespace PepperDash.Essentials.Devices.Displays
IncomingBuffer.CopyTo(newBytes, 0);
e.Bytes.CopyTo(newBytes, IncomingBuffer.Length);
if (Debug.Level == 2) // This check is here to prevent following string format from building unnecessarily on level 0 or 1
Debug.Console(2, this, "Received:{0}", ComTextHelper.GetEscapedText(newBytes));
// Need to find AA FF and have
for (int i = 0; i < newBytes.Length; i++)
{
@@ -309,6 +319,10 @@ namespace PepperDash.Essentials.Devices.Displays
/// <param name="b"></param>
void SendBytes(byte[] b)
{
if (LastCommandSentWasVolume) // If the last command sent was volume
if (b[1] != 0x12) // Check if this command is volume, and if not, delay this command
CrestronEnvironment.Sleep(100);
b[2] = ID;
// append checksum by adding all bytes, except last which should be 00
int checksum = 0;
@@ -320,6 +334,12 @@ namespace PepperDash.Essentials.Devices.Displays
b[b.Length - 1] = (byte)checksum;
if(Debug.Level == 2) // This check is here to prevent following string format from building unnecessarily on level 0 or 1
Debug.Console(2, this, "Sending:{0}", ComTextHelper.GetEscapedText(b));
if (b[1] == 0x12)
LastCommandSentWasVolume = true;
else
LastCommandSentWasVolume = false;
Communication.SendBytes(b);
}
@@ -393,11 +413,21 @@ namespace PepperDash.Essentials.Devices.Displays
public void InputHdmi1()
{
SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x22, 0x00 });
SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x21, 0x00 });
}
public void InputHdmi1PC()
{
SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x22, 0x00 });
}
public void InputHdmi2()
{
SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x23, 0x00 });
}
public void InputHdmi2PC()
{
SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x24, 0x00 });
}

View File

@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.Lighting;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.CrestronIO;
namespace PepperDash.Essentials.Devices.Common.Environment.Lighting
{
public class Din8sw8Controller : Device, ISwitchedOutputCollection
{
// Need to figure out some sort of interface to make these switched outputs behave like processor relays so they can be used interchangably
public Din8Sw8 SwitchModule { get; private set; }
/// <summary>
/// Collection of generic switched outputs
/// </summary>
public Dictionary<uint, ISwitchedOutput> SwitchedOutputs { get; private set; }
public Din8sw8Controller(string key, uint cresnetId)
: base(key)
{
SwitchedOutputs = new Dictionary<uint, ISwitchedOutput>();
SwitchModule = new Din8Sw8(cresnetId, Global.ControlSystem);
if (SwitchModule.Register() != eDeviceRegistrationUnRegistrationResponse.Success)
{
Debug.Console(2, this, "Error registering Din8sw8. Reason: {0}", SwitchModule.RegistrationFailureReason);
}
PopulateDictionary();
}
public override bool CustomActivate()
{
return base.CustomActivate();
}
/// <summary>
/// Populates the generic collection with the loads from the Crestron collection
/// </summary>
void PopulateDictionary()
{
foreach (var item in SwitchModule.SwitchedLoads)
{
SwitchedOutputs.Add(item.Number, new Din8sw8Output(item));
}
}
}
/// <summary>
/// Wrapper class to
/// </summary>
public class Din8sw8Output : ISwitchedOutput
{
SwitchedLoadWithOverrideParameter SwitchedOutput;
public BoolFeedback OutputIsOnFeedback { get; protected set; }
public Din8sw8Output(SwitchedLoadWithOverrideParameter switchedOutput)
{
SwitchedOutput = switchedOutput;
OutputIsOnFeedback = new BoolFeedback(new Func<bool>(() => SwitchedOutput.IsOn));
}
public void On()
{
SwitchedOutput.FullOn();
}
public void Off()
{
SwitchedOutput.FullOff();
}
}
}

View File

@@ -0,0 +1,249 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Lighting;
namespace PepperDash.Essentials.Devices.Common.Environment.Lutron
{
public class LutronQuantumArea : LightingBase, ILightingMasterRaiseLower, ICommunicationMonitor
{
public IBasicCommunication Communication { get; private set; }
public CommunicationGather PortGather { get; private set; }
public StatusMonitorBase CommunicationMonitor { get; private set; }
CTimer SubscribeAfterLogin;
string IntegrationId;
string Username;
string Password;
const string Delimiter = "\x0d\x0a";
const string Set = "#";
const string Get = "?";
public LutronQuantumArea(string key, string name, IBasicCommunication comm, LutronQuantumPropertiesConfig props)
: base(key, name)
{
Communication = comm;
IntegrationId = props.IntegrationId;
Username = props.Control.TcpSshProperties.Username;
Password = props.Control.TcpSshProperties.Password;
LightingScenes = props.Scenes;
var socket = comm as ISocketStatus;
if (socket != null)
{
// IP Control
socket.ConnectionChange += new EventHandler<GenericSocketStatusChageEventArgs>(socket_ConnectionChange);
}
else
{
// RS-232 Control
}
Communication.TextReceived += new EventHandler<GenericCommMethodReceiveTextArgs>(Communication_TextReceived);
PortGather = new CommunicationGather(Communication, Delimiter);
PortGather.LineReceived += new EventHandler<GenericCommMethodReceiveTextArgs>(PortGather_LineReceived);
if (props.CommunicationMonitorProperties != null)
{
CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties);
}
else
{
CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 120000, 120000, 300000, "?ETHERNET,0\x0d\x0a");
}
}
public override bool CustomActivate()
{
Communication.Connect();
CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); };
CommunicationMonitor.Start();
return true;
}
void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e)
{
Debug.Console(2, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString());
if (e.Client.IsConnected)
{
// Tasks on connect
}
}
/// <summary>
/// Checks for responses that do not contain the delimiter
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
void Communication_TextReceived(object sender, GenericCommMethodReceiveTextArgs args)
{
Debug.Console(2, this, "Text Received: '{0}'", args.Text);
if (args.Text.Contains("login:"))
{
// Login
SendLine(Username);
}
else if (args.Text.Contains("password:"))
{
// Login
SendLine(Password);
SubscribeAfterLogin = new CTimer(x => SubscribeToFeedback(), null, 5000);
}
else if (args.Text.Contains("Access Granted"))
{
if (SubscribeAfterLogin != null)
{
SubscribeAfterLogin.Stop();
}
SubscribeToFeedback();
}
}
/// <summary>
/// Handles all responses that contain the delimiter
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
void PortGather_LineReceived(object sender, GenericCommMethodReceiveTextArgs args)
{
Debug.Console(2, this, "Line Received: '{0}'", args.Text);
try
{
if (args.Text.Contains("~AREA"))
{
var response = args.Text.Split(',');
var integrationId = response[1];
if (integrationId != IntegrationId)
{
Debug.Console(2, this, "Response is not for correct Integration ID");
return;
}
else
{
var action = Int32.Parse(response[2]);
switch (action)
{
case (int)eAction.Scene:
{
var scene = response[3];
CurrentLightingScene = LightingScenes.FirstOrDefault(s => s.ID.Equals(scene));
OnLightingSceneChange();
break;
}
default:
break;
}
}
}
}
catch (Exception e)
{
Debug.Console(2, this, "Error parsing response:\n{0}", e);
}
}
/// <summary>
/// Subscribes to feedback
/// </summary>
public void SubscribeToFeedback()
{
Debug.Console(1, "Sending Monitoring Subscriptions");
SendLine("#MONITORING,6,1");
SendLine("#MONITORING,8,1");
SendLine("#MONITORING,5,2");
}
/// <summary>
/// Recalls the specified scene
/// </summary>
/// <param name="scene"></param>
public override void SelectScene(LightingScene scene)
{
Debug.Console(1, this, "Selecting Scene: '{0}'", scene.Name);
SendLine(string.Format("{0}AREA,{1},{2},{3}", Set, IntegrationId, (int)eAction.Scene, scene.ID));
}
/// <summary>
/// Begins raising the lights in the area
/// </summary>
public void MasterRaise()
{
SendLine(string.Format("{0}AREA,{1},{2}", Set, IntegrationId, (int)eAction.Raise));
}
/// <summary>
/// Begins lowering the lights in the area
/// </summary>
public void MasterLower()
{
SendLine(string.Format("{0}AREA,{1},{2}", Set, IntegrationId, (int)eAction.Lower));
}
/// <summary>
/// Stops the current raise/lower action
/// </summary>
public void MasterRaiseLowerStop()
{
SendLine(string.Format("{0}AREA,{1},{2}", Set, IntegrationId, (int)eAction.Stop));
}
/// <summary>
/// Appends the delimiter and sends the string
/// </summary>
/// <param name="s"></param>
public void SendLine(string s)
{
Debug.Console(2, this, "TX: '{0}'", s);
Communication.SendText(s + Delimiter);
}
}
public enum eAction : int
{
SetLevel = 1,
Raise = 2,
Lower = 3,
Stop = 4,
Scene = 6,
DaylightMode = 7,
OccupancyState = 8,
OccupancyMode = 9,
OccupiedLevelOrScene = 12,
UnoccupiedLevelOrScene = 13,
HyperionShaddowSensorOverrideState = 26,
HyperionBrightnessSensorOverrideStatue = 27
}
public class LutronQuantumPropertiesConfig
{
public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; }
public ControlPropertiesConfig Control { get; set; }
public string IntegrationId { get; set; }
public List<LightingScene> Scenes { get; set; }
// Moved to use existing properties in Control object
//public string Username { get; set; }
//public string Password { get; set; }
}
}

View File

@@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.CrestronIO;
using PepperDash.Essentials.Core.Shades;
namespace PepperDash.Essentials.Devices.Common.Environment.Somfy
{
/// <summary>
/// Controls a single shade using three relays
/// </summary>
public class RelayControlledShade : ShadeBase, IShadesOpenCloseStop
{
RelayControlledShadeConfigProperties Config;
ISwitchedOutput OpenRelay;
ISwitchedOutput StopOrPresetRelay;
ISwitchedOutput CloseRelay;
int RelayPulseTime;
public string StopOrPresetButtonLabel { get; set; }
public RelayControlledShade(string key, string name, RelayControlledShadeConfigProperties config)
: base(key, name)
{
Config = config;
RelayPulseTime = Config.RelayPulseTime;
StopOrPresetButtonLabel = Config.StopOrPresetLabel;
}
public override bool CustomActivate()
{
//Create ISwitchedOutput objects based on props
OpenRelay = GetSwitchedOutputFromDevice(Config.Relays.Open);
StopOrPresetRelay = GetSwitchedOutputFromDevice(Config.Relays.StopOrPreset);
CloseRelay = GetSwitchedOutputFromDevice(Config.Relays.Close);
return base.CustomActivate();
}
public override void Open()
{
Debug.Console(1, this, "Opening Shade: '{0}'", this.Name);
PulseOutput(OpenRelay, RelayPulseTime);
}
public override void StopOrPreset()
{
Debug.Console(1, this, "Stopping or recalling preset on Shade: '{0}'", this.Name);
PulseOutput(StopOrPresetRelay, RelayPulseTime);
}
public override void Close()
{
Debug.Console(1, this, "Closing Shade: '{0}'", this.Name);
PulseOutput(CloseRelay, RelayPulseTime);
}
void PulseOutput(ISwitchedOutput output, int pulseTime)
{
output.On();
CTimer pulseTimer = new CTimer(new CTimerCallbackFunction((o) => output.Off()), pulseTime);
}
/// <summary>
/// Attempts to get the port on teh specified device from config
/// </summary>
/// <param name="relayConfig"></param>
/// <returns></returns>
ISwitchedOutput GetSwitchedOutputFromDevice(IOPortConfig relayConfig)
{
var portDevice = DeviceManager.GetDeviceForKey(relayConfig.PortDeviceKey);
if (portDevice != null)
{
return (portDevice as ISwitchedOutputCollection).SwitchedOutputs[relayConfig.PortNumber];
}
else
{
Debug.Console(1, this, "Error: Unable to get relay on port '{0}' from device with key '{1}'", relayConfig.PortNumber, relayConfig.PortDeviceKey);
return null;
}
}
}
public class RelayControlledShadeConfigProperties
{
public int RelayPulseTime { get; set; }
public ShadeRelaysConfig Relays { get; set; }
public string StopOrPresetLabel { get; set; }
public class ShadeRelaysConfig
{
public IOPortConfig Open { get; set; }
public IOPortConfig StopOrPreset { get; set; }
public IOPortConfig Close { get; set; }
}
}
}

View File

@@ -58,14 +58,14 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.GeneralIO.dll</HintPath>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="PepperDash_Core, Version=1.0.6284.20368, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Crestron.SimplSharpPro.Lighting, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\pepperdash-simplsharp-core\Pepperdash Core\CLZ Builds\PepperDash_Core.dll</HintPath>
<HintPath>..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Lighting.dll</HintPath>
</Reference>
<Reference Include="PepperDash_Essentials_Core, Version=1.0.0.24289, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="mscorlib" />
<Reference Include="PepperDash_Core, Version=1.0.4.20530, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Essentials Core\PepperDashEssentialsBase\bin\PepperDash_Essentials_Core.dll</HintPath>
<HintPath>..\..\..\PepperDash.Core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll</HintPath>
</Reference>
<Reference Include="SimplSharpCustomAttributesInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -109,6 +109,7 @@
<Compile Include="Codec\iHasScheduleAwareness.cs" />
<Compile Include="Crestron\Gateways\CenRfgwController.cs" />
<Compile Include="Display\ComTcpDisplayBase.cs" />
<Compile Include="Display\AvocorVTFDisplay.cs" />
<Compile Include="Display\InputInterfaces.cs" />
<Compile Include="Display\SamsungMDCDisplay.cs" />
<Compile Include="Display\DeviceFactory.cs" />
@@ -121,8 +122,13 @@
<Compile Include="DSP\BiampTesira\BiampTesiraForteDsp.cs" />
<Compile Include="DSP\BiampTesira\BiampTesiraFortePropertiesConfig.cs" />
<Compile Include="DSP\PolycomSoundStructure\SoundStructureBasics.cs" />
<Compile Include="Environment\Crestron Lighting\Din8sw8.cs" />
<Compile Include="Environment\Lutron\LutronQuantum.cs" />
<Compile Include="Environment\Somfy\RelayControlledShade.cs" />
<Compile Include="Factory\DeviceFactory.cs" />
<Compile Include="Generic\GenericSource.cs" />
<Compile Include="Microphone\MicrophonePrivacyController.cs" />
<Compile Include="Microphone\MicrophonePrivacyControllerConfig.cs" />
<Compile Include="Occupancy\EssentialsGlsOccupancySensorBaseController.cs" />
<Compile Include="Occupancy\EssentialsOccupancyAggregator.cs" />
<Compile Include="Occupancy\iOccupancyStatusProvider.cs" />
@@ -154,6 +160,12 @@
<Compile Include="VideoCodec\VideoCodecBase.cs" />
<None Include="Properties\ControlSystem.cfg" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Essentials Core\PepperDashEssentialsBase\PepperDash_Essentials_Core.csproj">
<Project>{A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5}</Project>
<Name>PepperDash_Essentials_Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CompactFramework.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>

View File

@@ -3,17 +3,22 @@ using System.Collections.Generic;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.GeneralIO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Devices.Common.DSP;
using PepperDash.Essentials.Devices.Common.VideoCodec;
using PepperDash.Essentials.Core.CrestronIO;
using PepperDash.Essentials.Devices.Common;
using PepperDash.Essentials.Devices.Common.DSP;
using PepperDash.Essentials.Devices.Common.VideoCodec;
using PepperDash.Essentials.Devices.Common.Occupancy;
using PepperDash.Essentials.Devices.Common.Environment;
namespace PepperDash.Essentials.Devices.Common
{
@@ -25,6 +30,8 @@ namespace PepperDash.Essentials.Devices.Common
var name = dc.Name;
var type = dc.Type;
var properties = dc.Properties;
var propAnon = new {};
JsonConvert.DeserializeAnonymousType(dc.Properties.ToString(), propAnon);
var typeName = dc.Type.ToLower();
var groupName = dc.Group.ToLower();
@@ -103,17 +110,136 @@ namespace PepperDash.Essentials.Devices.Common
else if (typeName == "mockvc")
{
var props = JsonConvert.DeserializeObject
<PepperDash.Essentials.Devices.Common.VideoCodec.MockVcPropertiesConfig>(properties.ToString());
return new PepperDash.Essentials.Devices.Common.VideoCodec
.MockVC(key, name, props);
var props = JsonConvert.DeserializeObject<VideoCodec.MockVcPropertiesConfig>(properties.ToString());
return new VideoCodec.MockVC(key, name, props);
}
else if (typeName.StartsWith("ciscospark"))
{
var comm = CommFactory.CreateCommForDevice(dc);
var props = JsonConvert.DeserializeObject<Codec.CiscoSparkCodecPropertiesConfig>(properties.ToString());
return new PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.CiscoSparkCodec(key, name, comm, props);
return new VideoCodec.Cisco.CiscoSparkCodec(key, name, comm, props);
}
else if (typeName == "digitalinput")
{
var props = JsonConvert.DeserializeObject<IOPortConfig>(properties.ToString());
IDigitalInputPorts portDevice;
if (props.PortDeviceKey == "processor")
portDevice = Global.ControlSystem as IDigitalInputPorts;
else
portDevice = DeviceManager.GetDeviceForKey(props.PortDeviceKey) as IDigitalInputPorts;
if (portDevice == null)
Debug.Console(0, "ERROR: Unable to add digital input device with key '{0}'. Port Device does not support digital inputs", key);
else
{
var cs = (portDevice as CrestronControlSystem);
if (cs == null)
{
Debug.Console(0, "ERROR: Port device for [{0}] is not control system", props.PortDeviceKey);
return null;
}
if (cs.SupportsVersiport)
{
Debug.Console(1, "Attempting to add Digital Input device to Versiport port '{0}'", props.PortNumber);
if (props.PortNumber > cs.NumberOfVersiPorts)
{
Debug.Console(0, "WARNING: Cannot add Vesiport {0} on {1}. Out of range",
props.PortNumber, props.PortDeviceKey);
return null;
}
Versiport vp = cs.VersiPorts[props.PortNumber];
if (!vp.Registered)
{
var regSuccess = vp.Register();
if (regSuccess == eDeviceRegistrationUnRegistrationResponse.Success)
{
Debug.Console(1, "Successfully Created Digital Input Device on Versiport");
return new GenericVersiportDigitalInputDevice(key, vp, props);
}
else
{
Debug.Console(0, "WARNING: Attempt to register versiport {0} on device with key '{1}' failed: {2}",
props.PortNumber, props.PortDeviceKey, regSuccess);
return null;
}
}
}
else if (cs.SupportsDigitalInput)
{
Debug.Console(1, "Attempting to add Digital Input device to Digital Input port '{0}'", props.PortNumber);
if (props.PortNumber > cs.NumberOfDigitalInputPorts)
{
Debug.Console(0, "WARNING: Cannot register DIO port {0} on {1}. Out of range",
props.PortNumber, props.PortDeviceKey);
return null;
}
DigitalInput digitalInput = cs.DigitalInputPorts[props.PortNumber];
if (!digitalInput.Registered)
{
if (digitalInput.Register() == eDeviceRegistrationUnRegistrationResponse.Success)
{
Debug.Console(1, "Successfully Created Digital Input Device on Digital Input");
return new GenericDigitalInputDevice(key, digitalInput);
}
else
Debug.Console(0, "WARNING: Attempt to register digital input {0} on device with key '{1}' failed.",
props.PortNumber, props.PortDeviceKey);
}
}
}
}
else if (typeName == "relayoutput")
{
var props = JsonConvert.DeserializeObject<IOPortConfig>(properties.ToString());
IRelayPorts portDevice;
if (props.PortDeviceKey == "processor")
portDevice = Global.ControlSystem as IRelayPorts;
else
portDevice = DeviceManager.GetDeviceForKey(props.PortDeviceKey) as IRelayPorts;
if (portDevice == null)
Debug.Console(0, "Unable to add relay device with key '{0}'. Port Device does not support relays", key);
else
{
var cs = (portDevice as CrestronControlSystem);
if (cs != null)
if (cs.SupportsRelay && props.PortNumber <= cs.NumberOfRelayPorts)
{
Relay relay = cs.RelayPorts[props.PortNumber];
if (!relay.Registered)
{
if (relay.Register() == eDeviceRegistrationUnRegistrationResponse.Success)
return new GenericRelayDevice(key, relay);
else
Debug.Console(0, "Attempt to register relay {0} on device with key '{1}' failed.", props.PortNumber, props.PortDeviceKey);
}
}
// Future: Check if portDevice is 3-series card or other non control system that supports versiports
}
}
else if (typeName == "microphoneprivacycontroller")
{
var props = JsonConvert.DeserializeObject<Microphones.MicrophonePrivacyControllerConfig>(properties.ToString());
return new Microphones.MicrophonePrivacyController(key, props);
}
else if (groupName == "settopbox") //(typeName == "irstbbase")
@@ -135,6 +261,71 @@ namespace PepperDash.Essentials.Devices.Common
return new Roku2(key, name, irCont);
}
else if (typeName == "glsoirccn")
{
var comm = CommFactory.GetControlPropertiesConfig(dc);
GlsOccupancySensorBase occSensor = null;
occSensor = new GlsOirCCn(comm.CresnetIdInt, Global.ControlSystem);
if (occSensor != null)
return new EssentialsGlsOccupancySensorBaseController(key, name, occSensor);
else
Debug.Console(0, "ERROR: Unable to create Occupancy Sensor Device. Key: '{0}'", key);
}
else if (typeName == "glsodtccn")
{
var comm = CommFactory.GetControlPropertiesConfig(dc);
GlsOccupancySensorBase occSensor = null;
occSensor = new GlsOdtCCn(comm.CresnetIdInt, Global.ControlSystem);
if (occSensor != null)
return new EssentialsGlsOccupancySensorBaseController(key, name, occSensor);
else
Debug.Console(0, "ERROR: Unable to create Occupancy Sensor Device. Key: '{0}'", key);
}
else if (groupName == "lighting")
{
if (typeName == "lutronqs")
{
var comm = CommFactory.CreateCommForDevice(dc);
var props = JsonConvert.DeserializeObject<Environment.Lutron.LutronQuantumPropertiesConfig>(properties.ToString());
return new Environment.Lutron.LutronQuantumArea(key, name, comm, props);
}
else if (typeName == "din8sw8")
{
var comm = CommFactory.GetControlPropertiesConfig(dc);
return new Environment.Lighting.Din8sw8Controller(key, comm.CresnetIdInt);
}
}
else if (groupName == "environment")
{
if (typeName == "shadecontroller")
{
var props = JsonConvert.DeserializeObject<Core.Shades.ShadeControllerConfigProperties>(properties.ToString());
return new Core.Shades.ShadeController(key, name, props);
}
else if (typeName == "relaycontrolledshade")
{
var props = JsonConvert.DeserializeObject<Environment.Somfy.RelayControlledShadeConfigProperties>(properties.ToString());
return new Environment.Somfy.RelayControlledShade(key, name, props);
}
}
return null;
}
}

View File

@@ -0,0 +1,226 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.CrestronIO;
namespace PepperDash.Essentials.Devices.Common.Microphones
{
/// <summary>
/// Used for applications where one or more microphones with momentary contact closure outputs are used to
/// toggle the privacy state of the room. Privacy state feedback is represented
/// </summary>
public class MicrophonePrivacyController : Device
{
MicrophonePrivacyControllerConfig Config;
bool initialized;
public bool EnableLeds
{
get
{
return _enableLeds;
}
set
{
_enableLeds = value;
if (initialized)
{
if (value)
{
CheckPrivacyMode();
SetLedStates();
}
else
TurnOffAllLeds();
}
}
}
bool _enableLeds;
public List<IDigitalInput> Inputs { get; private set; }
public GenericRelayDevice RedLedRelay { get; private set; }
bool _redLedRelayState;
public GenericRelayDevice GreenLedRelay { get; private set; }
bool _greenLedRelayState;
public IPrivacy PrivacyDevice { get; private set; }
public MicrophonePrivacyController(string key, MicrophonePrivacyControllerConfig config) :
base(key)
{
Config = config;
Inputs = new List<IDigitalInput>();
}
public override bool CustomActivate()
{
foreach (var i in Config.Inputs)
{
var input = DeviceManager.GetDeviceForKey(i.DeviceKey) as IDigitalInput;
if(input != null)
AddInput(input);
}
var greenLed = DeviceManager.GetDeviceForKey(Config.GreenLedRelay.DeviceKey) as GenericRelayDevice;
if (greenLed != null)
GreenLedRelay = greenLed;
else
Debug.Console(0, this, "Unable to add Green LED device");
var redLed = DeviceManager.GetDeviceForKey(Config.RedLedRelay.DeviceKey) as GenericRelayDevice;
if (redLed != null)
RedLedRelay = redLed;
else
Debug.Console(0, this, "Unable to add Red LED device");
CheckPrivacyMode();
initialized = true;
return base.CustomActivate();
}
public void SetPrivacyDevice(IPrivacy privacyDevice)
{
PrivacyDevice = privacyDevice;
PrivacyDevice.PrivacyModeIsOnFeedback.OutputChange += new EventHandler<EventArgs>(PrivacyModeIsOnFeedback_OutputChange);
}
void PrivacyModeIsOnFeedback_OutputChange(object sender, EventArgs e)
{
Debug.Console(1, this, "Privacy mode change: {0}", sender as BoolFeedback);
CheckPrivacyMode();
}
void CheckPrivacyMode()
{
if (PrivacyDevice != null)
{
var privacyState = PrivacyDevice.PrivacyModeIsOnFeedback.BoolValue;
if (privacyState)
TurnOnRedLeds();
else
TurnOnGreenLeds();
}
}
void AddInput(IDigitalInput input)
{
Inputs.Add(input);
input.InputStateFeedback.OutputChange += new EventHandler<EventArgs>(InputStateFeedback_OutputChange);
}
void RemoveInput(IDigitalInput input)
{
var tempInput = Inputs.FirstOrDefault(i => i.Equals(input));
if (tempInput != null)
tempInput.InputStateFeedback.OutputChange -= InputStateFeedback_OutputChange;
Inputs.Remove(input);
}
void SetRedLedRelay(GenericRelayDevice relay)
{
RedLedRelay = relay;
}
void SetGreenLedRelay(GenericRelayDevice relay)
{
GreenLedRelay = relay;
}
/// <summary>
/// Check the state of the input change and handle accordingly
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void InputStateFeedback_OutputChange(object sender, EventArgs e)
{
if ((sender as BoolFeedback).BoolValue == true)
TogglePrivacyMute();
}
/// <summary>
/// Toggles the state of the privacy mute
/// </summary>
public void TogglePrivacyMute()
{
PrivacyDevice.PrivacyModeToggle();
}
void TurnOnRedLeds()
{
_greenLedRelayState = false;
_redLedRelayState = true;
SetLedStates();
}
void TurnOnGreenLeds()
{
_redLedRelayState = false;
_greenLedRelayState = true;
SetLedStates();
}
/// <summary>
/// If enabled, sets the actual state of the relays
/// </summary>
void SetLedStates()
{
if (_enableLeds)
{
SetRelayStates();
}
else
TurnOffAllLeds();
}
/// <summary>
/// Turns off all LEDs
/// </summary>
void TurnOffAllLeds()
{
_redLedRelayState = false;
_greenLedRelayState = false;
SetRelayStates();
}
void SetRelayStates()
{
if (RedLedRelay != null)
{
if (_redLedRelayState)
RedLedRelay.CloseRelay();
else
RedLedRelay.OpenRelay();
}
if(GreenLedRelay != null)
{
if (_greenLedRelayState)
GreenLedRelay.CloseRelay();
else
GreenLedRelay.OpenRelay();
}
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core.CrestronIO;
namespace PepperDash.Essentials.Devices.Common.Microphones
{
public class MicrophonePrivacyControllerConfig
{
public List<KeyedDevice> Inputs { get; set; }
public KeyedDevice GreenLedRelay { get; set; }
public KeyedDevice RedLedRelay { get; set; }
}
public class KeyedDevice
{
public string DeviceKey { get; set; }
}
}

View File

@@ -5,6 +5,7 @@ using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.GeneralIO;
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Occupancy
@@ -15,15 +16,51 @@ namespace PepperDash.Essentials.Devices.Common.Occupancy
public BoolFeedback RoomIsOccupiedFeedback { get; private set; }
public EssentialsGlsOccupancySensorBaseController(string key, string name, GlsOccupancySensorBase sensor, GlsOccupancySensorConfigurationProperties props)
// Debug properties
public bool InTestMode { get; private set; }
public bool TestRoomIsOccupiedFeedback { get; private set; }
public Func<bool> RoomIsOccupiedFeedbackFunc
{
get
{
return () => InTestMode ? TestRoomIsOccupiedFeedback : OccSensor.OccupancyDetectedFeedback.BoolValue;
}
}
public EssentialsGlsOccupancySensorBaseController(string key, string name, GlsOccupancySensorBase sensor)
: base(key, name, sensor)
{
OccSensor = sensor;
RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc);
OccSensor.GlsOccupancySensorChange += new GlsOccupancySensorChangeEventHandler(sensor_GlsOccupancySensorChange);
}
void sensor_GlsOccupancySensorChange(GlsOccupancySensorBase device, GlsOccupancySensorChangeEventArgs args)
{
RoomIsOccupiedFeedback.FireUpdate();
}
public void SetTestMode(bool mode)
{
InTestMode = mode;
Debug.Console(1, this, "In Mock Mode: '{0}'", InTestMode);
}
public void SetTestOccupiedState(bool state)
{
if (!InTestMode)
Debug.Console(1, "Mock mode not enabled");
else
{
TestRoomIsOccupiedFeedback = state;
RoomIsOccupiedFeedback.FireUpdate();
}
}
}
public class GlsOccupancySensorConfigurationProperties
{
}
}

View File

@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.GeneralIO;
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Occupancy
{
public class EssentialsGlsOccupancySensorBaseController : CrestronGenericBaseDevice, IOccupancyStatusProvider
{
public GlsOccupancySensorBase OccSensor { get; private set; }
public BoolFeedback RoomIsOccupiedFeedback { get; private set; }
<<<<<<< HEAD
/// <summary>
/// Set by debugging functions
/// </summary>
public bool InMockMode { get; private set; }
public bool MockRoomIsOccupiedFeedback { get; private set; }
=======
// Debug properties
public bool InTestMode { get; private set; }
public bool TestRoomIsOccupiedFeedback { get; private set; }
>>>>>>> origin/feature/ecs-342-neil
public Func<bool> RoomIsOccupiedFeedbackFunc
{
get
{
<<<<<<< HEAD
return () => InMockMode ? MockRoomIsOccupiedFeedback : OccSensor.OccupancyDetectedFeedback.BoolValue;
=======
return () => InTestMode ? TestRoomIsOccupiedFeedback : OccSensor.OccupancyDetectedFeedback.BoolValue;
>>>>>>> origin/feature/ecs-342-neil
}
}
public EssentialsGlsOccupancySensorBaseController(string key, string name, GlsOccupancySensorBase sensor, GlsOccupancySensorConfigurationProperties props)
: base(key, name, sensor)
{
OccSensor = sensor;
RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc);
OccSensor.GlsOccupancySensorChange += new GlsOccupancySensorChangeEventHandler(sensor_GlsOccupancySensorChange);
}
void sensor_GlsOccupancySensorChange(GlsOccupancySensorBase device, GlsOccupancySensorChangeEventArgs args)
{
RoomIsOccupiedFeedback.FireUpdate();
}
public void SetTestMode(bool mode)
{
InTestMode = mode;
Debug.Console(1, this, "In Mock Mode: '{0}'", InTestMode);
}
public void SetTestOccupiedState(bool state)
{
if (!InTestMode)
Debug.Console(1, "Mock mode not enabled");
else
{
TestRoomIsOccupiedFeedback = state;
RoomIsOccupiedFeedback.FireUpdate();
}
}
}
/// <summary>
///
/// </summary>
public class GlsOccupancySensorConfigurationProperties
{
public string CresnetId { get; set; }
public string Model { get; set; }
}
}

View File

@@ -4,5 +4,4 @@
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Essentials_Devices_Common")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyVersion("1.0.0.*")]
[assembly: AssemblyVersion("1.0.3.*")]

View File

@@ -8,7 +8,7 @@ using PepperDash.Core;
namespace PepperDash.Essentials.Devices.Common
{
public class SetTopBoxPropertiesConfig
public class SetTopBoxPropertiesConfig : PepperDash.Essentials.Core.Config.SourceDevicePropertiesConfigBase
{
public bool HasPresets { get; set; }
public bool HasDvr { get; set; }

View File

@@ -33,6 +33,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public StatusMonitorBase CommunicationMonitor { get; private set; }
public BoolFeedback PresentationViewMaximizedFeedback { get; private set; }
string CurrentPresentationView;
public BoolFeedback RoomIsOccupiedFeedback { get; private set; }
public IntFeedback PeopleCountFeedback { get; private set; }
@@ -45,6 +49,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public StringFeedback LocalLayoutFeedback { get; private set; }
public BoolFeedback LocalLayoutIsProminentFeedback { get; private set; }
public BoolFeedback FarEndIsSharingContentFeedback { get; private set; }
private CodecCommandWithLabel CurrentSelfviewPipPosition;
private CodecCommandWithLabel CurrentLocalLayout;
@@ -68,15 +76,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// </summary>
public List<CodecCommandWithLabel> LocalLayouts = new List<CodecCommandWithLabel>()
{
new CodecCommandWithLabel("auto", "Auto"),
//new CodecCommandWithLabel("auto", "Auto"),
//new CiscoCodecLocalLayout("custom", "Custom"), // Left out for now
new CodecCommandWithLabel("equal","Equal"),
new CodecCommandWithLabel("overlay","Overlay"),
new CodecCommandWithLabel("prominent","Prominent"),
new CodecCommandWithLabel("single","Single")
};
private CiscoCodecConfiguration.RootObject CodecConfiguration;
private CiscoCodecConfiguration.RootObject CodecConfiguration = new CiscoCodecConfiguration.RootObject();
private CiscoCodecStatus.RootObject CodecStatus = new CiscoCodecStatus.RootObject();
@@ -134,6 +142,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
}
}
protected Func<bool> FarEndIsSharingContentFeedbackFunc
{
get
{
return () => CodecStatus.Status.Conference.Presentation.Mode.Value == "Receiving";
}
}
protected override Func<bool> MuteFeedbackFunc
{
get
@@ -190,6 +206,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
}
}
protected Func<bool> LocalLayoutIsProminentFeedbackFunc
{
get
{
return () => CurrentLocalLayout.Label == "Prominent";
}
}
private string CliFeedbackRegistrationExpression;
@@ -241,6 +265,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc);
SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc);
LocalLayoutFeedback = new StringFeedback(LocalLayoutFeedbackFunc);
LocalLayoutIsProminentFeedback = new BoolFeedback(LocalLayoutIsProminentFeedbackFunc);
FarEndIsSharingContentFeedback = new BoolFeedback(FarEndIsSharingContentFeedbackFunc);
PresentationViewMaximizedFeedback = new BoolFeedback(() => CurrentPresentationView == "Maximized");
Communication = comm;
@@ -272,9 +300,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
PortGather.IncludeDelimiter = true;
PortGather.LineReceived += this.Port_LineReceived;
CodecConfiguration = new CiscoCodecConfiguration.RootObject();
//CodecStatus = new CiscoCodecStatus.RootObject();
CodecInfo = new CiscoCodecInfo(CodecStatus, CodecConfiguration);
CallHistory = new CodecCallHistory();
@@ -300,7 +325,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
CodecStatus.Status.Video.Selfview.Mode.ValueChangedAction = SelfviewIsOnFeedback.FireUpdate;
CodecStatus.Status.Video.Selfview.PIPPosition.ValueChangedAction = ComputeSelfviewPipStatus;
CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout;
CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = SharingContentIsOnFeedback.FireUpdate;
CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction += SharingContentIsOnFeedback.FireUpdate;
CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction += FarEndIsSharingContentFeedback.FireUpdate;
CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.AudioVideo,
eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this);
@@ -671,6 +697,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
EvalutateDisconnectEvent(eventReceived);
}
else if (response.IndexOf("\"Bookings\":{") > -1) // The list has changed, reload it
{
GetBookings(null);
}
}
else if (response.IndexOf("\"CommandResponse\":{") > -1)
{
@@ -1229,6 +1259,35 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
}
}
/// <summary>
/// Toggles between single/prominent layouts
/// </summary>
public void LocalLayoutToggleSingleProminent()
{
if (CurrentLocalLayout != null)
{
if (CurrentLocalLayout.Label != "Prominent")
LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Prominent")));
else
LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Single")));
}
}
/// <summary>
///
/// </summary>
public void MinMaxLayoutToggle()
{
if (PresentationViewMaximizedFeedback.BoolValue)
CurrentPresentationView = "Minimized";
else
CurrentPresentationView = "Maximized";
SendText(string.Format("xCommand Video PresentationView Set View: {0}", CurrentPresentationView));
PresentationViewMaximizedFeedback.FireUpdate();
}
/// <summary>
/// Calculates the current selfview PIP position
/// </summary>
@@ -1264,7 +1323,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public override bool MultiSiteOptionIsEnabled
{
get
get
{
if (CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value.ToLower() == "true")
return true;
@@ -1300,7 +1359,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
get
{
if (CodecConfiguration.Configuration.H323.H323Alias.ID != null)
return CodecConfiguration.Configuration.H323.H323Alias.E164.Value;
return CodecConfiguration.Configuration.H323.H323Alias.ID.Value;
else
return string.Empty;
}

View File

@@ -16,5 +16,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
StringFeedback LocalLayoutFeedback { get; }
void LocalLayoutToggle();
void LocalLayoutToggleSingleProminent();
void MinMaxLayoutToggle();
}
}

View File

@@ -50,7 +50,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
{
get
{
var value = ActiveCalls.Any(c => c.IsActiveCall);
bool value;
if (ActiveCalls != null)
value = ActiveCalls.Any(c => c.IsActiveCall);
else
value = false;
return value;
}
}

View File

@@ -0,0 +1,881 @@
<?xml version="1.0" encoding="utf-8"?>
<ClassDiagram MajorVersion="1" MinorVersion="1">
<Class Name="PepperDash.Essentials.EssentialsRoomVolumesConfig" Collapsed="true">
<Position X="19.25" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAQAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAIAgA=</HashCode>
<FileName>Audio\EssentialsVolumeLevelConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.EssentialsVolumeLevelConfig" Collapsed="true">
<Position X="22.75" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAEAAAAAAAAAAAAAAAAIAAAAgBAAAAAAAAAAA=</HashCode>
<FileName>Audio\EssentialsVolumeLevelConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.ConfigReader" Collapsed="true">
<Position X="26.25" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAIAAAAAAAABAAAACAAIAAAAQAAAAAAAAAAEAA=</HashCode>
<FileName>Config\ConfigReader.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.DeviceFactory" Collapsed="true">
<Position X="29.75" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAA=</HashCode>
<FileName>Config\DeviceFactory.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.EssentialsConfig" Collapsed="true">
<Position X="33.25" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAEAAAAAQAIAAAAAAAACAAAAAAAAAAAAAAEAAAA=</HashCode>
<FileName>Config\EssentialsConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.SystemTemplateConfigs" Collapsed="true">
<Position X="33.25" Y="7.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Config\EssentialsConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.ConfigTieLine" Collapsed="true">
<Position X="31.5" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAgEAAAAAAEAAAAAAAAAAAAAAAAAAAAAEAIAIA=</HashCode>
<FileName>Configuration ORIGINAL\ConfigTieLine.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Configuration" Collapsed="true">
<Position X="33.25" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AEAAAAAAAAAAgQAAAAAAgAAAAAAAAAAAAAAgEDAADAQ=</HashCode>
<FileName>Configuration ORIGINAL\Configuration.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.ConfigSourceList" Collapsed="true">
<Position X="29.75" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAQAAAAAAAQAAACAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Configuration ORIGINAL\Configuration.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.ConfigSourceItem" Collapsed="true">
<Position X="28" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAgAAAAAAAEAAAAAAAAAAAAAAAAAgAAAAAAA=</HashCode>
<FileName>Configuration ORIGINAL\Configuration.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.ConfigInfo" Collapsed="true">
<Position X="24.5" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>IQAAAAAEAAAAAAADAAACABQAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Configuration ORIGINAL\Configuration.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.SourceListConfigProperties" Collapsed="true">
<Position X="24.5" Y="7.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAEAAAAAAAAAAAIAAAAAgAAAIAAA=</HashCode>
<FileName>Configuration ORIGINAL\ConfigurationHelpers.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.DmFactory" Collapsed="true">
<Position X="31.5" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAQAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAA=</HashCode>
<FileName>Configuration ORIGINAL\Factories\DmFactory.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.FactoryHelper" Collapsed="true">
<Position X="26.25" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAEDAAKAA=</HashCode>
<FileName>Configuration ORIGINAL\Factories\FactoryHelper.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.IrOutPortConfig" Collapsed="true">
<Position X="31.5" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAABAAAA=</HashCode>
<FileName>Configuration ORIGINAL\Factories\FactoryHelper.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.ControlSystem" Collapsed="true">
<Position X="35" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAoAgAAIAEAACAAEAIEIBICAAAAAAABAAAAAAAAAAAA=</HashCode>
<FileName>ControlSystem.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Amplifier" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="21" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA=</HashCode>
<FileName>Devices\Amplifier.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.CotijaConfig" Collapsed="true">
<Position X="19.25" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAA=</HashCode>
<FileName>Room\Cotija\CotijaConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.CotijaDdvc01RoomBridgePropertiesConfig" Collapsed="true">
<Position X="22.75" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Cotija\CotijaConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.CotijaSystemController" Collapsed="true">
<Position X="24.5" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>CAAABAABAgACCAKGBIAAEyBAFAAACYSAgIAAAAJkAAA=</HashCode>
<FileName>Room\Cotija\CotijaSystemController.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.CotijaBridgeBase" Collapsed="true">
<Position X="9.5" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAQIQAAAAAAAAgAAAAAAQAAAAAAAAAAAAAAAABAAAAA=</HashCode>
<FileName>Room\Cotija\RoomBridges\CotijaBridgeBase.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.CotijaEssentialsHuddleSpaceRoomBridge" Collapsed="true">
<Position X="8.25" Y="5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAIAAAAIABAAkgAABgIAAAAAAAAAEAAAAAAgBAAACA=</HashCode>
<FileName>Room\Cotija\RoomBridges\CotijaEssentialsHuddleSpaceRoomBridge.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.SourceSelectMessageContent" Collapsed="true">
<Position X="26.25" Y="7.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAA=</HashCode>
<FileName>Room\Cotija\RoomBridges\CotijaEssentialsHuddleSpaceRoomBridge.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.EssentialsHuddleSpaceRoom" Collapsed="true">
<Position X="0.5" Y="9" Width="1.5" />
<TypeIdentifier>
<HashCode>iQFBQAIAAgAAgQAEQACAMABAAABAACAQUAAQAAgCgBA=</HashCode>
<FileName>Room\Types\EssentialsHuddleSpaceRoom.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="PepperDash.Essentials.EssentialsHuddleVtc1Room" Collapsed="true">
<Position X="2.75" Y="9" Width="1.5" />
<TypeIdentifier>
<HashCode>iQFBQAIAIgQMgQAEQAigMABAAABAADIwUACQAAgCgTE=</HashCode>
<FileName>Room\Types\EssentialsHuddleVtc1Room.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="PepperDash.Essentials.EssentialsPresentationRoom" Collapsed="true">
<Position X="5" Y="9" Width="1.5" />
<TypeIdentifier>
<HashCode>iQAEAAIACkAAAQAOQASgMAAJAABgAAAQQAAQAAgCgBA=</HashCode>
<FileName>Room\Types\EssentialsPresentationRoom.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="PepperDash.Essentials.EssentialsRoomBase" Collapsed="true">
<Position X="2.75" Y="7.5" Width="1.5" />
<TypeIdentifier>
<HashCode>gQAAEAICECgCAQAEAAAUIwIyAAAAgACiAgAAAQECAgA=</HashCode>
<FileName>Room\Types\EssentialsRoomBase.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.CrestronTouchpanelPropertiesConfig" Collapsed="true">
<Position X="26.25" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AICCIAAIGIAAAAAgCAAIAEAAAAAAAAAAAAEAIAAAAAA=</HashCode>
<FileName>UI\CrestronTouchpanelPropertiesConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.UiSetupPropertiesConfig" Collapsed="true">
<Position X="19.25" Y="8.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>UI\CrestronTouchpanelPropertiesConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.EssentialsTouchpanelController" Collapsed="true">
<Position X="21" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAIAEAAAAAAAAAAAAAAAAEAAAAACKIBAAgACAAAAAA=</HashCode>
<FileName>UI\EssentialsTouchpanelController.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.HttpLogoServer" Collapsed="true">
<Position X="19.25" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAQAAAACAAAAABAAAAAAAAAABAAAAAAAAAAkAAAAA=</HashCode>
<FileName>UI\HttpLogoServer.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.UIBoolJoin" Collapsed="true">
<Position X="35" Y="7.5" Width="1.5" />
<TypeIdentifier>
<HashCode>j+jWCNqEIGzi4UTaTgyn37kpncQJK7L42VMLmMgTE5A=</HashCode>
<FileName>UI\JoinConstants\UIBoolJoin.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.UISmartObjectJoin" Collapsed="true">
<Position X="21" Y="8.5" Width="1.5" />
<TypeIdentifier>
<HashCode>BAggBIABCQAAGAAQAAAACAACAAAAAAAAAAIAAAAAAAA=</HashCode>
<FileName>UI\JoinConstants\UISmartObjectJoin.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.UIStringJoin" Collapsed="true">
<Position X="22.75" Y="8.5" Width="1.5" />
<TypeIdentifier>
<HashCode>BkBgIAgAAggOQAFGAYQIABACgCEBjkSQAUEAASIABCE=</HashCode>
<FileName>UI\JoinConstants\UIStringlJoin.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.UIUshortJoin" Collapsed="true">
<Position X="24.5" Y="8.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAQAAAACAAAACAEAAIAAAAAAIABAIAAAABAAAEAAAAA=</HashCode>
<FileName>UI\JoinConstants\UIUshortJoin.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.SubpageReferenceListActivityItem" Collapsed="true">
<Position X="28" Y="7.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA=</HashCode>
<FileName>UI\SubpageReferenceListActivityItem.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.SubpageReferenceListButtonAndModeItem" Collapsed="true">
<Position X="29.75" Y="7.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA=</HashCode>
<FileName>UI\SubpageReferenceListCallStagingItem.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.SubpageReferenceListSourceItem" Collapsed="true">
<Position X="31.5" Y="7.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAABAAAAAAAAAAAAAAAAAAAAgAAAAACAAAAAABgAAA=</HashCode>
<FileName>UI\SubpageReferenceListSourceItem.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.PanelDriverBase" Collapsed="true">
<Position X="8.75" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>CAAIkAAAAAAQEAAAEAACAAAAAIAEABAAAgAACAAAAAA=</HashCode>
<FileName>UIDrivers\enums and base.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.EssentialsPanelMainInterfaceDriver" Collapsed="true">
<Position X="1" Y="1.75" Width="1.5" />
<TypeIdentifier>
<HashCode>ABAAAAAAAhgAAAAAEAAAAAAAAIAEAAAAAAAAAAAAAAA=</HashCode>
<FileName>UIDrivers\Essentials\EssentialsPanelMainInterfaceDriver.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.EssentialsPresentationPanelAvFunctionsDriver" Collapsed="true">
<Position X="3.25" Y="1.75" Width="1.5" />
<TypeIdentifier>
<HashCode>gTEAIIAiCggFNCQ4EA4AWBkAKJCEqAAOS4CKMAQQJQA=</HashCode>
<FileName>UIDrivers\Essentials\EssentialsPresentationPanelAvFunctionsDriver.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.EssentialsHuddlePanelAvFunctionsDriver" Collapsed="true">
<Position X="7.75" Y="1.75" Width="1.5" />
<TypeIdentifier>
<HashCode>gRQAIICuAghENHQpEA4IWCkBMJDEsEAEC4CAMARQIBA=</HashCode>
<FileName>UIDrivers\EssentialsHuddle\EssentialsHuddlePanelAvFunctionsDriver.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.EssentialsHuddleVtc1PanelAvFunctionsDriver" Collapsed="true">
<Position X="12.25" Y="1.75" Width="1.5" />
<TypeIdentifier>
<HashCode>GRwAIYC+oghAeHStEDAIWCdBMADEsBAcDwCAMARYIBg=</HashCode>
<FileName>UIDrivers\EssentialsHuddleVTC\EssentialsHuddleVtc1PanelAvFunctionsDriver.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="PepperDash.Essentials.JoinedSigInterlock" Collapsed="true">
<Position X="19.25" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAQgAIAAAAAAAAAEAAAAAQAAAAEAAAAAAAAEAAAgAA=</HashCode>
<FileName>UIDrivers\JoinedSigInterlock.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.SingleSubpageModalAndBackDriver" Collapsed="true">
<Position X="14.5" Y="1.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAEAAAQAAABIAEAAAAAAAAAAAAAAA=</HashCode>
<FileName>UIDrivers\Page Drivers\SingleSubpageModalAndBackDriver.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.SingleSubpageModalDriver" Collapsed="true">
<Position X="16.75" Y="1.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAEAAAAAAABAAEAAAAAAAAAAAAAAA=</HashCode>
<FileName>UIDrivers\Page Drivers\SingleSubpageModalDriver.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.SigInterlock" Collapsed="true">
<Position X="19.25" Y="7.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAQAAIAAAAAAAAIEAAAAAAAAAAEAAAAAAAAEAAAgAA=</HashCode>
<FileName>UIDrivers\SigInterlock.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.SmartObjectRoomsList" Collapsed="true">
<Position X="21" Y="7.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAACAAACBAAAAAAAAAAAAAAAAAAAAIAAAAAAQA=</HashCode>
<FileName>UIDrivers\SmartObjectRoomsList.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.SmartObjectRoomsListItem" Collapsed="true">
<Position X="22.75" Y="7.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAQAAAAAAAAAAAABAAQAAAAAAAAAACAAEAAAAAAAA=</HashCode>
<FileName>UIDrivers\SmartObjectRoomsList.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.VolumeDeviceChangeEventArgs" Collapsed="true">
<Position X="26.25" Y="8.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAABAAAAAEAAAAAAAAAAAAAAAAAAAQAAAAAAAAA=</HashCode>
<FileName>UIDrivers\VolumeAndSourceChangeArgs.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase" Collapsed="true">
<Position X="8.25" Y="9.5" Width="1.5" />
<TypeIdentifier>
<HashCode>IAGigRBpCgZwAIMSBBIbIgAAImAPtEBiAAgECpJgKQo=</HashCode>
<FileName>OTHER\Fusion\EssentialsHuddleSpaceFusionSystemControllerBase.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="PepperDash.Essentials.Fusion.FusionRoomExtensions" Collapsed="true">
<Position X="31.5" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAACgAIQAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\EssentialsHuddleSpaceFusionSystemControllerBase.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.FusionStaticAssetExtensions" Collapsed="true">
<Position X="35" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAQAAACAAAAAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\EssentialsHuddleSpaceFusionSystemControllerBase.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.EssentialsHuddleVtc1FusionController" Collapsed="true">
<Position X="8.25" Y="11" Width="1.5" />
<TypeIdentifier>
<HashCode>AACAAAAAAgAAAIAAAAAAAAAgIBAQAAAAAAAAAAAAAQA=</HashCode>
<FileName>OTHER\Fusion\EssentialsHuddleVtc1FusionController.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.ScheduleChangeEventArgs" Collapsed="true">
<Position X="33.25" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionEventHandlers.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.MeetingChangeEventArgs" Collapsed="true">
<Position X="26.25" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionEventHandlers.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.ProcessorProgReg" Collapsed="true">
<Position X="19.25" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionProcessorQueries.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.ProcessorProgramItem" Collapsed="true">
<Position X="35" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAEAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionProcessorQueries.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.FusionRoomGuids" Collapsed="true">
<Position X="33.25" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAICAAAAAAAAAAAAAAAAAACAAAATQAAAAAAAABAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.FusionOccupancySensorAsset" Collapsed="true">
<Position X="29.75" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAQAQAAAAAAAAQAAAAAAQAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.FusionAsset" Collapsed="true">
<Position X="28" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAQAQAAAAAAAAQAAAAAAQAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.RoomSchedule" Collapsed="true">
<Position X="31.5" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.LocalTimeRequest" Collapsed="true">
<Position X="24.5" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.RequestSchedule" Collapsed="true">
<Position X="22.75" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAACACAAAAQAAAAAAAAAAAAAAAAAAAACAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.RequestAction" Collapsed="true">
<Position X="21" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAACAAAgAAQAAAAAAAAAAAAAAAAAAIAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.ActionResponse" Collapsed="true">
<Position X="19.25" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAgAAQAAAAAAAAAAAAAAAAAAIAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.Parameter" Collapsed="true">
<Position X="33.25" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAgAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.ScheduleResponse" Collapsed="true">
<Position X="35" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAACAAAAAAQAAAAAAAAAAgAAAAAAAAAAABAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.Event" Collapsed="true">
<Position X="24.5" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AABCAAAAYEAAEBIBAIAAJAQAQKAYAAAIEAAAEAACCgg=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.Resources" Collapsed="true">
<Position X="26.25" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.Rooms" Collapsed="true">
<Position X="29.75" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.Room" Collapsed="true">
<Position X="28" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAgAAAAAAAAAQAAAAAAAAAAAAAAAAEAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.Attendees" Collapsed="true">
<Position X="22.75" Y="0.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAABAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.Required" Collapsed="true">
<Position X="24.5" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.Optional" Collapsed="true">
<Position X="31.5" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.MeetingType" Collapsed="true">
<Position X="28" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAgAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.MeetingTypes" Collapsed="true">
<Position X="29.75" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.LiveMeeting" Collapsed="true">
<Position X="21" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAgAQAAIAAAAAAACAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Fusion.LiveMeetingURL" Collapsed="true">
<Position X="22.75" Y="5.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>OTHER\Fusion\FusionRviDataClasses.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.DDVC01RoomPropertiesConfig" Collapsed="true">
<Position X="5" Y="6.25" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAIAgAAQAAAAAAAAEAAAAAAA=</HashCode>
<FileName>Room\Config\DDVC01RoomPropertiesConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.DDVC01SpeedDial" Collapsed="true">
<Position X="28" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAEAAAAQAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Config\DDVC01RoomPropertiesConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsHuddleRoomPropertiesConfig" Collapsed="true">
<Position X="2.75" Y="4.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAgAAAAAAAAAAACAAAAAABAAAAAAAAABA=</HashCode>
<FileName>Room\Config\EssentialsHuddleRoomPropertiesConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsHuddleVtc1PropertiesConfig" Collapsed="true">
<Position X="5" Y="4.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAgAAAAAAAAAAACIAAAAABAAAAAAAAABA=</HashCode>
<FileName>Room\Config\EssentialsHuddleVtc1PropertiesConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsPresentationRoomPropertiesConfig" Collapsed="true">
<Position X="0.5" Y="4.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAACAIAAAAAAAAAAABAACAAAAAABAAAAAAAAABA=</HashCode>
<FileName>Room\Config\EssentialsPresentationPropertiesConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsRoomConfig" Collapsed="true">
<Position X="26.25" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAEAAAAAA=</HashCode>
<FileName>Room\Config\EssentialsRoomConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsRoomPropertiesConfig" Collapsed="true">
<Position X="2.75" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AABAEQAAAAEoAAEEAAQAAAACAAAAAAgggAAAAQAAAgA=</HashCode>
<FileName>Room\Config\EssentialsRoomConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsLightingPropertiesConfig" Collapsed="true">
<Position X="19.25" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Config\EssentialsRoomConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsRoomMicrophonePrivacyConfig" Collapsed="true">
<Position X="31.5" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAEAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Config\EssentialsRoomConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsHelpPropertiesConfig" Collapsed="true">
<Position X="35" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAIAAAAAA=</HashCode>
<FileName>Room\Config\EssentialsRoomConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsOneButtonMeetingPropertiesConfig" Collapsed="true">
<Position X="22.75" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Config\EssentialsRoomConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsRoomAddressPropertiesConfig" Collapsed="true">
<Position X="24.5" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAQAAAgAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Config\EssentialsRoomConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsLogoPropertiesConfig" Collapsed="true">
<Position X="21" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIACQAAAAAAAAA=</HashCode>
<FileName>Room\Config\EssentialsRoomConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsRoomOccSensorConfig" Collapsed="true">
<Position X="33.25" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AACAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Config\EssentialsRoomConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsRoomTechConfig" Collapsed="true">
<Position X="35" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAA=</HashCode>
<FileName>Room\Config\EssentialsRoomConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsRoomEmergencyConfig" Collapsed="true">
<Position X="28" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAABAACAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Config\EssentialsRoomEmergencyConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Config.EssentialsRoomEmergencyTriggerConfig" Collapsed="true">
<Position X="29.75" Y="2.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAABQAAAAAAAAA=</HashCode>
<FileName>Room\Config\EssentialsRoomEmergencyConfig.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Cotija.CotijaDdvc01DeviceBridge" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="21" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AUAAAAgAAAACwAYAAAAAAAEAAAAAAAAAIcAAAEAAAAA=</HashCode>
<FileName>Room\Cotija\CotijaDdvc01DeviceBridge.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="PepperDash.Essentials.Room.Cotija.IChannelExtensions" Collapsed="true">
<Position X="21" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Cotija\DeviceTypeInterfaces\IChannelExtensions.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Cotija.IColorExtensions" Collapsed="true">
<Position X="22.75" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Cotija\DeviceTypeInterfaces\IColorExtensions.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Cotija.IDPadExtensions" Collapsed="true">
<Position X="24.5" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Cotija\DeviceTypeInterfaces\IDPadExtensions.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Cotija.IDvrExtensions" Collapsed="true">
<Position X="26.25" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Cotija\DeviceTypeInterfaces\IDvrExtensions.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Cotija.INumericExtensions" Collapsed="true">
<Position X="28" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Cotija\DeviceTypeInterfaces\INumericExtensions.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Cotija.IPowerExtensions" Collapsed="true">
<Position X="29.75" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Cotija\DeviceTypeInterfaces\IPowerExtensions.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Cotija.ISetTopBoxControlsExtensions" Collapsed="true">
<Position X="33.25" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Cotija\DeviceTypeInterfaces\ISetTopBoxControlsExtensions.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Cotija.ITransportExtensions" Collapsed="true">
<Position X="35" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAACAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Cotija\DeviceTypeInterfaces\ITransportExtensions.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.Room.Cotija.CotijaDdvc01RoomBridge" Collapsed="true">
<Position X="10.5" Y="5" Width="1.5" />
<TypeIdentifier>
<HashCode>gACIAAAAAAICAFAAAACAAAEIIAAAAAQAQAAAABAAAAA=</HashCode>
<FileName>Room\Cotija\RoomBridges\CotijaDdvc01RoomBridge.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="PepperDash.Essentials.Room.EssentialsRoomEmergencyBase" Collapsed="true">
<Position X="8.25" Y="6.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Emergency\EsentialsRoomEmergencyContactClosure.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Class Name="PepperDash.Essentials.Room.EssentialsRoomEmergencyContactClosure" Collapsed="true">
<Position X="8.25" Y="7.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAASBAAAAAAAAAAAAAABAAAAAAAEAA=</HashCode>
<FileName>Room\Emergency\EsentialsRoomEmergencyContactClosure.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.UIDrivers.EssentialsHuddleTechPageDriver" Collapsed="true">
<Position X="5.5" Y="1.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAQAAAAAiACDAAAGCAACgAIAABECBAgQAAAAQAAAAA=</HashCode>
<FileName>UIDrivers\EssentialsHuddle\EssentialsHuddleTechPageDriver.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver" Collapsed="true">
<Position X="10" Y="1.75" Width="1.5" />
<TypeIdentifier>
<HashCode>XAASAoAiAagwAcBAGAUURWQEOHQFAKCmAABCNSSEDPA=</HashCode>
<FileName>UIDrivers\VC\EssentialsVideoCodecUiDriver.cs</FileName>
</TypeIdentifier>
</Class>
<Interface Name="PepperDash.Essentials.IHasCurrentSourceInfoChange" Collapsed="true">
<Position X="22.75" Y="9.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Types\EssentialsRoomBase.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.IAVDriver" Collapsed="true">
<Position X="19.25" Y="9.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAgAAAAAAAAACBAAACAAAAIBAAAEAAAEAgAAAAAAIAA=</HashCode>
<FileName>UIDrivers\EssentialsHuddleVTC\EssentialsHuddleVtc1PanelAvFunctionsDriver.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="PepperDash.Essentials.Room.Cotija.IDelayedConfiguration" Collapsed="true">
<Position X="21" Y="9.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAA=</HashCode>
<FileName>Room\Cotija\Interfaces.cs</FileName>
</TypeIdentifier>
</Interface>
<Enum Name="PepperDash.Essentials.eShutdownType" Collapsed="true">
<Position X="29.75" Y="10.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAQAAAAAAACAAAAAAAAAAAAAAAAAAAAAEACAA=</HashCode>
<FileName>Room\Types\EssentialsRoomBase.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.eVacancyMode" Collapsed="true">
<Position X="31.5" Y="10.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAACAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAA=</HashCode>
<FileName>Room\Types\EssentialsRoomBase.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.eWarmingCoolingMode" Collapsed="true">
<Position X="33.25" Y="10.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAIEAAAA=</HashCode>
<FileName>Room\Types\EssentialsRoomBase.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.eAvSubpageType" Collapsed="true">
<Position X="24.5" Y="10.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AIAAAAAAAAAAAAAAAhBAAAAAAAAAAACYAAAAAAAAAAA=</HashCode>
<FileName>UIDrivers\enums and base.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.eAvSourceSubpageType" Collapsed="true">
<Position X="22.75" Y="10.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAABAAAAAAAAAAAAAAACAAAEAAAAAAAAA=</HashCode>
<FileName>UIDrivers\enums and base.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.eCommonSubpageType" Collapsed="true">
<Position X="28" Y="10.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAEAAAAAAAAAIAAAAAAAAQAAAAQAAAAAAAAAAA=</HashCode>
<FileName>UIDrivers\enums and base.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.eAvSmartObjects" Collapsed="true">
<Position X="21" Y="10.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAABAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>UIDrivers\enums and base.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.eCommonSmartObjects" Collapsed="true">
<Position X="26.25" Y="10.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>UIDrivers\enums and base.cs</FileName>
</TypeIdentifier>
</Enum>
<Enum Name="PepperDash.Essentials.ChangeType" Collapsed="true">
<Position X="19.25" Y="10.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAgCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>UIDrivers\VolumeAndSourceChangeArgs.cs</FileName>
</TypeIdentifier>
</Enum>
<Delegate Name="PepperDash.Essentials.PressAndHoldAction" Collapsed="true">
<Position X="19.25" Y="11.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Room\Cotija\RoomBridges\CotijaEssentialsHuddleSpaceRoomBridge.cs</FileName>
</TypeIdentifier>
</Delegate>
<Delegate Name="PepperDash.Essentials.SourceInfoChangeHandler" Collapsed="true">
<Position X="21" Y="11.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAACAAACAAAAAAAAAAAAAAAAAAACAAAAA=</HashCode>
<FileName>UIDrivers\VolumeAndSourceChangeArgs.cs</FileName>
</TypeIdentifier>
</Delegate>
<Font Name="Segoe UI" Size="9" />
</ClassDiagram>

View File

@@ -5,6 +5,7 @@ using Crestron.SimplSharp.CrestronIO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials
@@ -16,18 +17,28 @@ namespace PepperDash.Essentials
{
public static EssentialsConfig ConfigObject { get; private set; }
public static void LoadConfig2()
public static bool LoadConfig2()
{
Debug.Console(0, "Using unmerged system/template configs.");
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading unmerged system/template portal configuration file.");
try
{
using (StreamReader fs = new StreamReader(string.Format(@"\NVRAM\program{0}\ConfigurationFile.json",
InitialParametersClass.ApplicationNumber)))
var filePath = Global.FilePathPrefix + "configurationFile.json";
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to load config file: '{0}'", filePath);
if (!File.Exists(filePath))
{
Debug.Console(0, Debug.ErrorLogLevel.Error,
"ERROR: Configuration file not present. Please load file to {0} and reset program", filePath);
return false;
}
using (StreamReader fs = new StreamReader(filePath))
{
var doubleObj = JObject.Parse(fs.ReadToEnd());
ConfigObject = MergeConfigs(doubleObj).ToObject<EssentialsConfig>();
// Extract SystemUrl and TemplateUrl
// Extract SystemUrl and TemplateUrl into final config output
if (doubleObj["system_url"] != null)
{
@@ -38,13 +49,16 @@ namespace PepperDash.Essentials
{
ConfigObject.TemplateUrl= doubleObj["template_url"].Value<string>();
}
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Successfully Loaded Merged Config");
return true;
}
catch (Exception e)
{
Debug.Console(0, "Config failed: \r{0}", e);
Debug.Console(0, Debug.ErrorLogLevel.Error, "ERROR: Config load failed: \r{0}", e);
return false;
}
}
@@ -56,6 +70,8 @@ namespace PepperDash.Essentials
var merged = new JObject();
// Put together top-level objects
// skip any objects that don't have template objects
if (system["info"] != null)
merged.Add("info", Merge(template["info"], system["info"]));
else
@@ -64,27 +80,33 @@ namespace PepperDash.Essentials
merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray,
system["devices"] as JArray, "uid"));
if (system["rooms"] == null)
merged.Add("rooms", template["rooms"]);
else
merged.Add("rooms", MergeArraysOnTopLevelProperty(template["rooms"] as JArray,
system["rooms"] as JArray, "key"));
if (template["rooms"] != null)
{
if (system["rooms"] == null)
merged.Add("rooms", template["rooms"]);
else
merged.Add("rooms", MergeArraysOnTopLevelProperty(template["rooms"] as JArray,
system["rooms"] as JArray, "key"));
}
if (system["sourceLists"] == null)
merged.Add("sourceLists", template["sourceLists"]);
else
merged.Add("sourceLists", Merge(template["sourceLists"], system["sourceLists"]));
if (template["sourceLists"] != null)
{
if (system["sourceLists"] == null)
merged.Add("sourceLists", template["sourceLists"]);
else
merged.Add("sourceLists", Merge(template["sourceLists"], system["sourceLists"]));
}
// Template tie lines take precdence. Config tool probably can't do them at system
// Template tie lines take precdence. Config tool doesn't do them at system
// level anyway...
if (template["tieLines"] != null)
merged.Add("tieLines", template["tieLines"]);
else if (system["tieLines"] != null)
merged.Add("tieLines", system["tieLines"]);
else
merged.Add("tieLines", new JArray());
//else if (system["tieLines"] != null)
// merged.Add("tieLines", system["tieLines"]);
//else
// merged.Add("tieLines", new JArray());
//Debug.Console(0, "MERGED RESULT: \x0d\x0a{0}", merged);
Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged);
return merged;
}
@@ -133,7 +155,6 @@ namespace PepperDash.Essentials
/// <param name="b"></param>
static JObject Merge(JObject o1, JObject o2)
{
//Console.WriteLine("Merging {0}\ronto {1}", o2, o1);
foreach (var o2Prop in o2)
{
var o1Value = o1[o2Prop.Key];
@@ -152,6 +173,11 @@ namespace PepperDash.Essentials
return o1;
}
/// <summary>
/// Returns the group for a given device key in config
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static string GetGroupForDeviceKey(string key)
{
var dev = ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase));

View File

@@ -16,16 +16,21 @@ namespace PepperDash.Essentials
/// </summary>
public class EssentialsConfig : BasicConfig
{
[JsonProperty("system_url")]
public string SystemUrl { get; set; }
[JsonProperty("template_url")]
public string TemplateUrl { get; set; }
public CotijaConfig Cotija { get; private set; }
public string SystemUuid
//public CotijaConfig Cotija { get; private set; }
[JsonProperty("systemUuid")]
public string SystemUuid
{
get
{
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/templates\/(.*)#.*");
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*");
string uuid = result.Groups[1].Value;
@@ -33,11 +38,12 @@ namespace PepperDash.Essentials
}
}
public string TemplateUuid
[JsonProperty("templateUuid")]
public string TemplateUuid
{
get
{
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)#.*");
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*");
string uuid = result.Groups[1].Value;
@@ -46,9 +52,9 @@ namespace PepperDash.Essentials
}
[JsonProperty("rooms")]
public List<EssentialsRoomConfig> Rooms { get; private set; }
public List<EssentialsRoomConfig> Rooms { get; set; }
}
/// <summary>
///
/// </summary>
@@ -57,7 +63,5 @@ namespace PepperDash.Essentials
public EssentialsConfig System { get; set; }
public EssentialsConfig Template { get; set; }
//public CotijaConfig Cotija { get; set; }
}
}

View File

@@ -17,7 +17,7 @@ namespace PepperDash.Essentials
{
public static class FactoryHelper
{
public static string IrDriverPathPrefix = @"\NVRAM\IR\";
public static string IrDriverPathPrefix = Global.FilePathPrefix + "IR" + Global.DirectorySeparator;
public static void HandleUnknownType(JToken devToken, string type)
{

View File

@@ -1,6 +1,7 @@
using System;
using System.Linq;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.CrestronThread;
using PepperDash.Core;
@@ -9,6 +10,7 @@ using PepperDash.Essentials.Core;
using PepperDash.Essentials.Devices.Common;
using PepperDash.Essentials.DM;
using PepperDash.Essentials.Fusion;
using PepperDash.Essentials.Room.Cotija;
namespace PepperDash.Essentials
{
@@ -30,10 +32,11 @@ namespace PepperDash.Essentials
/// </summary>
public override void InitializeSystem()
{
//CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Reloads configuration file",
// ConsoleAccessLevelEnum.AccessOperator);
//CrestronConsole.AddNewConsoleCommand(s => TearDown(), "ungo", "Unloads configuration file",
DeterminePlatform();
//CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file",
// ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>
{
foreach (var tl in TieLineCollection.Default)
@@ -41,41 +44,129 @@ namespace PepperDash.Essentials
},
"listtielines", "Prints out all tie lines", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>
{
CrestronConsole.ConsoleCommandResponse
("Current running configuration. This is the merged system and template configuration");
CrestronConsole.ConsoleCommandResponse(Newtonsoft.Json.JsonConvert.SerializeObject
(ConfigReader.ConfigObject, Newtonsoft.Json.Formatting.Indented));
}, "showconfig", "Shows the current running merged config", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>
{
CrestronConsole.ConsoleCommandResponse("This system can be found at the following URLs:\r" +
"System URL: {0}\r" +
"Template URL: {1}", ConfigReader.ConfigObject.SystemUrl, ConfigReader.ConfigObject.TemplateUrl);
}, "portalinfo", "Shows portal URLS from configuration", ConsoleAccessLevelEnum.AccessOperator);
GoWithLoad();
}
/// <summary>
/// Determines if the program is running on a processor (appliance) or server (XiO Edge).
///
/// Sets Global.FilePathPrefix based on platform
/// </summary>
public void DeterminePlatform()
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Determining Platform....");
string filePathPrefix;
var dirSeparator = Global.DirectorySeparator;
var version = Crestron.SimplSharp.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
var versionString = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
string directoryPrefix;
//directoryPrefix = Crestron.SimplSharp.CrestronIO.Directory.GetApplicationRootDirectory();
#warning ^ For use with beta Include4.dat for XiO Edge
directoryPrefix = "";
if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server)
{
filePathPrefix = directoryPrefix + dirSeparator + "NVRAM"
+ dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator;
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on 3-series Appliance", versionString);
}
else
{
filePathPrefix = directoryPrefix + dirSeparator + "User" + dirSeparator;
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on XiO Edge Server", versionString);
}
Global.SetFilePathPrefix(filePathPrefix);
}
/// <summary>
/// Do it, yo
/// </summary>
public void GoWithLoad()
{
// var thread = new Thread(o =>
// {
try
try
{
CrestronConsole.AddNewConsoleCommand(EnablePortalSync, "portalsync", "Loads Portal Sync",
ConsoleAccessLevelEnum.AccessOperator);
//PortalSync = new PepperDashPortalSyncClient();
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials load from configuration");
var filesReady = SetupFilesystem();
if (filesReady)
{
CrestronConsole.AddNewConsoleCommand(EnablePortalSync, "portalsync", "Loads Portal Sync",
ConsoleAccessLevelEnum.AccessOperator);
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Folder structure verified. Loading config...");
if (!ConfigReader.LoadConfig2())
return;
//PortalSync = new PepperDashPortalSyncClient();
Debug.Console(0, "Starting Essentials load from configuration");
ConfigReader.LoadConfig2();
LoadDevices();
LoadTieLines();
LoadRooms();
LogoServer = new HttpLogoServer(8080, @"\html\logo");
DeviceManager.ActivateAll();
Debug.Console(0, "Essentials load complete\r" +
"-------------------------------------------------------------");
}
catch (Exception e)
{
Debug.Console(0, "FATAL INITIALIZE ERROR. System is in an inconsistent state:\r{0}", e);
Load();
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Essentials load complete\r" +
"-------------------------------------------------------------");
}
// return null;
// }, null);
else
{
Debug.Console(0,
"------------------------------------------------\r" +
"------------------------------------------------\r" +
"------------------------------------------------\r" +
"Essentials file structure setup completed.\r" +
"Please load config, sgd and ir files and\r" +
"restart program.\r" +
"------------------------------------------------\r" +
"------------------------------------------------\r" +
"------------------------------------------------");
}
}
catch (Exception e)
{
Debug.Console(0, "FATAL INITIALIZE ERROR. System is in an inconsistent state:\r{0}", e);
}
}
/// <summary>
/// Verifies filesystem is set up. IR, SGD, and program1 folders
/// </summary>
bool SetupFilesystem()
{
Debug.Console(0, "Verifying and/or creating folder structure");
var configDir = Global.FilePathPrefix;
var configExists = Directory.Exists(configDir);
if (!configExists)
Directory.Create(configDir);
var irDir = Global.FilePathPrefix + "ir";
if (!Directory.Exists(irDir))
Directory.Create(irDir);
var sgdDir = Global.FilePathPrefix + "sgd";
if (!Directory.Exists(sgdDir))
Directory.Create(sgdDir);
return configExists;
}
public void EnablePortalSync(string s)
@@ -100,6 +191,19 @@ namespace PepperDash.Essentials
Debug.Console(0, "Tear down COMPLETE");
}
/// <summary>
///
/// </summary>
void Load()
{
LoadDevices();
LoadTieLines();
LoadRooms();
LoadLogoServer();
DeviceManager.ActivateAll();
}
/// <summary>
/// Reads all devices from config and adds them to DeviceManager
@@ -108,53 +212,37 @@ namespace PepperDash.Essentials
{
foreach (var devConf in ConfigReader.ConfigObject.Devices)
{
Debug.Console(0, "Creating device '{0}'", devConf.Key);
// Skip this to prevent unnecessary warnings
if (devConf.Key == "processor")
continue;
// Try local factory first
var newDev = DeviceFactory.GetDevice(devConf);
// Then associated library factories
if (newDev == null)
newDev = PepperDash.Essentials.Devices.Common.DeviceFactory.GetDevice(devConf);
if (newDev == null)
newDev = PepperDash.Essentials.DM.DeviceFactory.GetDevice(devConf);
if (newDev == null)
newDev = PepperDash.Essentials.Devices.Displays.DisplayDeviceFactory.GetDevice(devConf);
try
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Creating device '{0}'", devConf.Key);
// Skip this to prevent unnecessary warnings
if (devConf.Key == "processor")
continue;
if (newDev != null)
DeviceManager.AddDevice(newDev);
else
Debug.Console(0, "WARNING: Cannot load unknown device type '{0}', key '{1}'.", devConf.Type, devConf.Key);
// Try local factory first
var newDev = DeviceFactory.GetDevice(devConf);
// Then associated library factories
if (newDev == null)
newDev = PepperDash.Essentials.Devices.Common.DeviceFactory.GetDevice(devConf);
if (newDev == null)
newDev = PepperDash.Essentials.DM.DeviceFactory.GetDevice(devConf);
if (newDev == null)
newDev = PepperDash.Essentials.Devices.Displays.DisplayDeviceFactory.GetDevice(devConf);
if (newDev != null)
DeviceManager.AddDevice(newDev);
else
Debug.Console(0, Debug.ErrorLogLevel.Notice, "ERROR: Cannot load unknown device type '{0}', key '{1}'.", devConf.Type, devConf.Key);
}
catch (Exception e)
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "ERROR: Creating device {0}. Skipping device. \r{1}", devConf.Key, e);
}
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Devices Loaded.");
// CODEC TESTING
/*
try
{
GenericSshClient TestCodecClient = new GenericSshClient("TestCodec-1--SshClient", "10.11.50.135", 22, "crestron", "2H3Zu&OvgXp6");
var props = new PepperDash.Essentials.Devices.Common.Codec.CiscoCodecPropertiesConfig();
props.PhonebookMode = "Local";
props.Favorites = new System.Collections.Generic.List<PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem>();
props.Favorites.Add(new PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem() { Name = "NYU Cisco Webex", Number = "10.11.50.211" });
PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.CiscoCodec TestCodec =
new PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.CiscoCodec("TestCodec-1", "Cisco Spark Room Kit", TestCodecClient, props);
TestCodec.CommDebuggingIsOn = true;
TestCodec.CustomActivate();
}
catch (Exception e)
{
Debug.Console(0, "Error in something Neil is working on ;) \r{0}", e);
}
*/
// CODEC TESTING
}
/// <summary>
@@ -167,12 +255,20 @@ namespace PepperDash.Essentials
var tlc = TieLineCollection.Default;
//tlc.Clear();
if (ConfigReader.ConfigObject.TieLines == null)
{
return;
}
foreach (var tieLineConfig in ConfigReader.ConfigObject.TieLines)
{
var newTL = tieLineConfig.GetTieLine();
if (newTL != null)
tlc.Add(newTL);
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Tie Lines Loaded.");
}
/// <summary>
@@ -180,6 +276,12 @@ namespace PepperDash.Essentials
/// </summary>
public void LoadRooms()
{
if (ConfigReader.ConfigObject.Rooms == null)
{
Debug.Console(0, Debug.ErrorLogLevel.Warning, "WARNING: Configuration contains no rooms");
return;
}
foreach (var roomConfig in ConfigReader.ConfigObject.Rooms)
{
var room = roomConfig.GetRoomObject();
@@ -189,32 +291,71 @@ namespace PepperDash.Essentials
{
DeviceManager.AddDevice(room);
Debug.Console(1, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion");
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion");
DeviceManager.AddDevice(new EssentialsHuddleSpaceFusionSystemControllerBase((EssentialsHuddleSpaceRoom)room, 0xf1));
var cotija = DeviceManager.GetDeviceForKey("cotijaServer") as CotijaSystemController;
if (cotija != null)
{
cotija.CotijaRooms.Add(new CotijaEssentialsHuddleSpaceRoomBridge(cotija, room as EssentialsHuddleSpaceRoom));
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Cotija Bridge...");
// Cotija bridge
var bridge = new CotijaEssentialsHuddleSpaceRoomBridge(room as EssentialsHuddleSpaceRoom);
AddBridgePostActivationHelper(bridge); // Lets things happen later when all devices are present
DeviceManager.AddDevice(bridge);
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Cotija Bridge Added...");
}
else if (room is EssentialsHuddleVtc1Room)
{
DeviceManager.AddDevice(room);
Debug.Console(1, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion");
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion");
DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((EssentialsHuddleVtc1Room)room, 0xf1));
}
}
else
{
Debug.Console(1, "Room is NOT EssentialsHuddleSpaceRoom, attempting to add to DeviceManager w/o Fusion");
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is NOT EssentialsHuddleSpaceRoom, attempting to add to DeviceManager w/o Fusion");
DeviceManager.AddDevice(room);
}
}
else
Debug.Console(0, "WARNING: Cannot create room from config, key '{0}'", roomConfig.Key);
Debug.Console(0, Debug.ErrorLogLevel.Notice, "WARNING: Cannot create room from config, key '{0}'", roomConfig.Key);
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Rooms Loaded.");
}
/// <summary>
/// Helps add the post activation steps that link bridges to main controller
/// </summary>
/// <param name="bridge"></param>
void AddBridgePostActivationHelper(CotijaBridgeBase bridge)
{
bridge.AddPostActivationAction(() =>
{
var parent = DeviceManager.AllDevices.FirstOrDefault(d => d.Key == "appServer") as CotijaSystemController;
if (parent == null)
{
Debug.Console(0, bridge, "ERROR: Cannot connect bridge. System controller not present");
}
Debug.Console(0, bridge, "Linking to parent controller");
bridge.AddParent(parent);
parent.AddBridge(bridge);
});
}
/// <summary>
/// Fires up a logo server if not already running
/// </summary>
void LoadLogoServer()
{
try
{
LogoServer = new HttpLogoServer(8080, Global.FilePathPrefix + "html" + Global.DirectorySeparator + "logo");
}
catch (Exception)
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "NOTICE: Logo server cannot be started. Likely already running in another program");
}
}
}

View File

@@ -0,0 +1,367 @@
using System;
using System.Linq;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.CrestronThread;
using PepperDash.Core;
using PepperDash.Core.PortalSync;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Devices.Common;
using PepperDash.Essentials.DM;
using PepperDash.Essentials.Fusion;
using PepperDash.Essentials.Room.Cotija;
namespace PepperDash.Essentials
{
public class ControlSystem : CrestronControlSystem
{
PepperDashPortalSyncClient PortalSync;
HttpLogoServer LogoServer;
public ControlSystem()
: base()
{
Thread.MaxNumberOfUserThreads = 400;
Global.ControlSystem = this;
DeviceManager.Initialize(this);
}
/// <summary>
/// Git 'er goin'
/// </summary>
public override void InitializeSystem()
{
<<<<<<< HEAD
DeterminePlatform();
//CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file",
// ConsoleAccessLevelEnum.AccessOperator);
=======
CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file",
ConsoleAccessLevelEnum.AccessOperator);
>>>>>>> 600b9f11ff1bbc186f7c2a2945955731b3523b3c
CrestronConsole.AddNewConsoleCommand(s =>
{
foreach (var tl in TieLineCollection.Default)
CrestronConsole.ConsoleCommandResponse(" {0}\r", tl);
},
"listtielines", "Prints out all tie lines", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>
{
CrestronConsole.ConsoleCommandResponse
("Current running configuration. This is the merged system and template configuration");
CrestronConsole.ConsoleCommandResponse(Newtonsoft.Json.JsonConvert.SerializeObject
(ConfigReader.ConfigObject, Newtonsoft.Json.Formatting.Indented));
}, "showconfig", "Shows the current running merged config", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>
{
CrestronConsole.ConsoleCommandResponse("This system can be found at the following URLs:\r" +
"System URL: {0}\r" +
"Template URL: {1}", ConfigReader.ConfigObject.SystemUrl, ConfigReader.ConfigObject.TemplateUrl);
}, "portalinfo", "Shows portal URLS from configuration", ConsoleAccessLevelEnum.AccessOperator);
//GoWithLoad();
}
/// <summary>
/// Determines if the program is running on a processor (appliance) or server (XiO Edge).
///
/// Sets Global.FilePathPrefix based on platform
/// </summary>
public void DeterminePlatform()
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Determining Platform....");
string filePathPrefix;
var dirSeparator = Global.DirectorySeparator;
var version = Crestron.SimplSharp.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
var versionString = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
string directoryPrefix;
//directoryPrefix = Crestron.SimplSharp.CrestronIO.Directory.GetApplicationRootDirectory();
#warning ^ For use with beta Include4.dat for XiO Edge
directoryPrefix = "";
if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server)
{
filePathPrefix = directoryPrefix + dirSeparator + "NVRAM"
+ dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator;
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on 3-series Appliance", versionString);
}
else
{
filePathPrefix = directoryPrefix + dirSeparator + "User" + dirSeparator;
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on XiO Edge Server", versionString);
}
Global.SetFilePathPrefix(filePathPrefix);
}
/// <summary>
/// Do it, yo
/// </summary>
public void GoWithLoad()
{
try
{
CrestronConsole.AddNewConsoleCommand(EnablePortalSync, "portalsync", "Loads Portal Sync",
ConsoleAccessLevelEnum.AccessOperator);
//PortalSync = new PepperDashPortalSyncClient();
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials load from configuration");
var filesReady = SetupFilesystem();
if (filesReady)
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Folder structure verified. Loading config...");
if (!ConfigReader.LoadConfig2())
return;
Load();
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Essentials load complete\r" +
"-------------------------------------------------------------");
}
else
{
Debug.Console(0,
"------------------------------------------------\r" +
"------------------------------------------------\r" +
"------------------------------------------------\r" +
"Essentials file structure setup completed.\r" +
"Please load config, sgd and ir files and\r" +
"restart program.\r" +
"------------------------------------------------\r" +
"------------------------------------------------\r" +
"------------------------------------------------");
}
}
catch (Exception e)
{
Debug.Console(0, "FATAL INITIALIZE ERROR. System is in an inconsistent state:\r{0}", e);
}
}
/// <summary>
/// Verifies filesystem is set up. IR, SGD, and program1 folders
/// </summary>
bool SetupFilesystem()
{
Debug.Console(0, "Verifying and/or creating folder structure");
var configDir = Global.FilePathPrefix;
var configExists = Directory.Exists(configDir);
if (!configExists)
Directory.Create(configDir);
var irDir = Global.FilePathPrefix + "ir";
if (!Directory.Exists(irDir))
Directory.Create(irDir);
var sgdDir = Global.FilePathPrefix + "sgd";
if (!Directory.Exists(sgdDir))
Directory.Create(sgdDir);
return configExists;
}
public void EnablePortalSync(string s)
{
if (s.ToLower() == "enable")
{
CrestronConsole.ConsoleCommandResponse("Portal Sync features enabled");
PortalSync = new PepperDashPortalSyncClient();
}
}
public void TearDown()
{
Debug.Console(0, "Tearing down existing system");
DeviceManager.DeactivateAll();
TieLineCollection.Default.Clear();
foreach (var key in DeviceManager.GetDevices())
DeviceManager.RemoveDevice(key);
Debug.Console(0, "Tear down COMPLETE");
}
/// <summary>
///
/// </summary>
void Load()
{
LoadDevices();
LoadTieLines();
LoadRooms();
LoadLogoServer();
DeviceManager.ActivateAll();
}
/// <summary>
/// Reads all devices from config and adds them to DeviceManager
/// </summary>
public void LoadDevices()
{
foreach (var devConf in ConfigReader.ConfigObject.Devices)
{
try
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Creating device '{0}'", devConf.Key);
// Skip this to prevent unnecessary warnings
if (devConf.Key == "processor")
continue;
// Try local factory first
var newDev = DeviceFactory.GetDevice(devConf);
// Then associated library factories
if (newDev == null)
newDev = PepperDash.Essentials.Devices.Common.DeviceFactory.GetDevice(devConf);
if (newDev == null)
newDev = PepperDash.Essentials.DM.DeviceFactory.GetDevice(devConf);
if (newDev == null)
newDev = PepperDash.Essentials.Devices.Displays.DisplayDeviceFactory.GetDevice(devConf);
if (newDev != null)
DeviceManager.AddDevice(newDev);
else
Debug.Console(0, Debug.ErrorLogLevel.Notice, "ERROR: Cannot load unknown device type '{0}', key '{1}'.", devConf.Type, devConf.Key);
}
catch (Exception e)
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "ERROR: Creating device {0}. Skipping device. \r{1}", devConf.Key, e);
}
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Devices Loaded.");
}
/// <summary>
/// Helper method to load tie lines. This should run after devices have loaded
/// </summary>
public void LoadTieLines()
{
// In the future, we can't necessarily just clear here because devices
// might be making their own internal sources/tie lines
var tlc = TieLineCollection.Default;
//tlc.Clear();
if (ConfigReader.ConfigObject.TieLines == null)
{
return;
}
foreach (var tieLineConfig in ConfigReader.ConfigObject.TieLines)
{
var newTL = tieLineConfig.GetTieLine();
if (newTL != null)
tlc.Add(newTL);
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Tie Lines Loaded.");
}
/// <summary>
/// Reads all rooms from config and adds them to DeviceManager
/// </summary>
public void LoadRooms()
{
if (ConfigReader.ConfigObject.Rooms == null)
{
Debug.Console(0, Debug.ErrorLogLevel.Warning, "WARNING: Configuration contains no rooms");
return;
}
foreach (var roomConfig in ConfigReader.ConfigObject.Rooms)
{
var room = roomConfig.GetRoomObject();
if (room != null)
{
if (room is EssentialsHuddleSpaceRoom)
{
DeviceManager.AddDevice(room);
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion");
DeviceManager.AddDevice(new EssentialsHuddleSpaceFusionSystemControllerBase((EssentialsHuddleSpaceRoom)room, 0xf1));
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Cotija Bridge...");
// Cotija bridge
var bridge = new CotijaEssentialsHuddleSpaceRoomBridge(room as EssentialsHuddleSpaceRoom);
AddBridgePostActivationHelper(bridge); // Lets things happen later when all devices are present
DeviceManager.AddDevice(bridge);
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Cotija Bridge Added...");
}
else if (room is EssentialsHuddleVtc1Room)
{
DeviceManager.AddDevice(room);
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion");
DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((EssentialsHuddleVtc1Room)room, 0xf1));
}
else
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is NOT EssentialsHuddleSpaceRoom, attempting to add to DeviceManager w/o Fusion");
DeviceManager.AddDevice(room);
}
}
else
Debug.Console(0, Debug.ErrorLogLevel.Notice, "WARNING: Cannot create room from config, key '{0}'", roomConfig.Key);
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Rooms Loaded.");
}
/// <summary>
/// Helps add the post activation steps that link bridges to main controller
/// </summary>
/// <param name="bridge"></param>
void AddBridgePostActivationHelper(CotijaBridgeBase bridge)
{
bridge.AddPostActivationAction(() =>
{
var parent = DeviceManager.AllDevices.FirstOrDefault(d => d.Key == "appServer") as CotijaSystemController;
if (parent == null)
{
Debug.Console(0, bridge, "ERROR: Cannot connect bridge. System controller not present");
}
Debug.Console(0, bridge, "Linking to parent controller");
bridge.AddParent(parent);
parent.AddBridge(bridge);
});
}
/// <summary>
/// Fires up a logo server if not already running
/// </summary>
void LoadLogoServer()
{
try
{
LogoServer = new HttpLogoServer(8080, Global.FilePathPrefix + "html" + Global.DirectorySeparator + "logo");
}
catch (Exception)
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "NOTICE: Logo server cannot be started. Likely already running in another program");
}
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
@@ -29,11 +30,7 @@ namespace PepperDash.Essentials
}
else if (dc.Group.ToLower() == "touchpanel") // typeName.StartsWith("tsw"))
{
var comm = CommFactory.GetControlPropertiesConfig(dc);
var props = JsonConvert.DeserializeObject<CrestronTouchpanelPropertiesConfig>(
properties.ToString());
return new EssentialsTouchpanelController(key, name, typeName, props, comm.IpIdInt);
return UiDeviceFactory.GetUiDevice(dc);
}
else if (typeName == "mockdisplay")
@@ -62,13 +59,34 @@ namespace PepperDash.Essentials
return new ConsoleCommMockDevice(key, name, props, comm);
}
else if (typeName == "webserver")
else if (typeName == "appserver")
{
var props = JsonConvert.DeserializeObject<CotijaConfig>(properties.ToString());
return new CotijaSystemController(key, name, props);
}
else if (typeName == "mobilecontrolbridge-ddvc01")
{
var comm = CommFactory.GetControlPropertiesConfig(dc);
var bridge = new PepperDash.Essentials.Room.Cotija.CotijaDdvc01RoomBridge(key, name, comm.IpIdInt);
bridge.AddPreActivationAction(() =>
{
var parent = DeviceManager.AllDevices.FirstOrDefault(d => d.Key == "appServer") as CotijaSystemController;
if (parent == null)
{
Debug.Console(0, bridge, "ERROR: Cannot connect bridge. System controller not present");
}
Debug.Console(0, bridge, "Linking to parent controller");
bridge.AddParent(parent);
parent.AddBridge(bridge);
});
return bridge;
}
return null;
}
}
}

View File

@@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.UI;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.PageManagers;
namespace PepperDash.Essentials
{
public class UiDeviceFactory
{
public static IKeyed GetUiDevice(DeviceConfig config)
{
var comm = CommFactory.GetControlPropertiesConfig(config);
var props = JsonConvert.DeserializeObject<CrestronTouchpanelPropertiesConfig>(config.Properties.ToString());
EssentialsTouchpanelController panelController = new EssentialsTouchpanelController(config.Key, config.Name, config.Type, props, comm.IpIdInt);
panelController.AddPostActivationAction(() =>
{
var mainDriver = new EssentialsPanelMainInterfaceDriver(panelController.Panel, props);
// Then the sub drivers
// spin up different room drivers depending on room type
var room = DeviceManager.GetDeviceForKey(props.DefaultRoomKey);
if (room is EssentialsHuddleSpaceRoom)
{
// Header Driver
Debug.Console(0, panelController, "Adding header driver");
mainDriver.HeaderDriver = new EssentialsHeaderDriver(mainDriver, props);
// AV Driver
Debug.Console(0, panelController, "Adding huddle space AV driver");
var avDriver = new EssentialsHuddlePanelAvFunctionsDriver(mainDriver, props);
avDriver.CurrentRoom = room as EssentialsHuddleSpaceRoom;
avDriver.DefaultRoomKey = props.DefaultRoomKey;
mainDriver.AvDriver = avDriver;
// Environment Driver
if (avDriver.CurrentRoom.Config.Environment != null && avDriver.CurrentRoom.Config.Environment.DeviceKeys.Count > 0)
{
Debug.Console(0, panelController, "Adding environment driver");
mainDriver.EnvironmentDriver = new EssentialsEnvironmentDriver(mainDriver, props);
mainDriver.EnvironmentDriver.GetDevicesFromConfig(avDriver.CurrentRoom.Config.Environment);
}
mainDriver.HeaderDriver.SetupHeaderButtons(avDriver, avDriver.CurrentRoom);
panelController.LoadAndShowDriver(mainDriver); // This is a little convoluted.
if (panelController.Panel is TswFt5ButtonSystem)
{
var tsw = panelController.Panel as TswFt5ButtonSystem;
// Wire up hard keys
tsw.Power.UserObject = new Action<bool>(b => { if (!b) avDriver.PowerButtonPressed(); });
//tsw.Home.UserObject = new Action<bool>(b => { if (!b) HomePressed(); });
if(mainDriver.EnvironmentDriver != null)
tsw.Lights.UserObject = new Action<bool>(b =>
{
if (!b)
{
//mainDriver.AvDriver.PopupInterlock.ShowInterlockedWithToggle(mainDriver.EnvironmentDriver.BackgroundSubpageJoin);
mainDriver.EnvironmentDriver.Toggle();
}
});
tsw.Up.UserObject = new Action<bool>(avDriver.VolumeUpPress);
tsw.Down.UserObject = new Action<bool>(avDriver.VolumeDownPress);
}
}
//else if (room is EssentialsPresentationRoom)
//{
// Debug.Console(0, panelController, "Adding presentation room driver");
// var avDriver = new EssentialsPresentationPanelAvFunctionsDriver(mainDriver, props);
// avDriver.CurrentRoom = room as EssentialsPresentationRoom;
// avDriver.DefaultRoomKey = props.DefaultRoomKey;
// mainDriver.AvDriver = avDriver ;
// mainDriver.HeaderDriver = new EssentialsHeaderDriver(mainDriver, props);
// panelController.LoadAndShowDriver(mainDriver);
// if (panelController.Panel is TswFt5ButtonSystem)
// {
// var tsw = panelController.Panel as TswFt5ButtonSystem;
// // Wire up hard keys
// tsw.Power.UserObject = new Action<bool>(b => { if (!b) avDriver.PowerButtonPressed(); });
// //tsw.Home.UserObject = new Action<bool>(b => { if (!b) HomePressed(); });
// tsw.Up.UserObject = new Action<bool>(avDriver.VolumeUpPress);
// tsw.Down.UserObject = new Action<bool>(avDriver.VolumeDownPress);
// }
//}
else if (room is EssentialsHuddleVtc1Room)
{
Debug.Console(0, panelController, "Adding huddle space VTC AV driver");
// Header Driver
mainDriver.HeaderDriver = new EssentialsHeaderDriver(mainDriver, props);
// AV Driver
var avDriver = new EssentialsHuddleVtc1PanelAvFunctionsDriver(mainDriver, props);
var codecDriver = new PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver(panelController.Panel, avDriver,
(room as EssentialsHuddleVtc1Room).VideoCodec, mainDriver.HeaderDriver);
avDriver.SetVideoCodecDriver(codecDriver);
avDriver.CurrentRoom = room as EssentialsHuddleVtc1Room;
avDriver.DefaultRoomKey = props.DefaultRoomKey;
mainDriver.AvDriver = avDriver;
// Environment Driver
if (avDriver.CurrentRoom.Config.Environment != null && avDriver.CurrentRoom.Config.Environment.DeviceKeys.Count > 0)
{
Debug.Console(0, panelController, "Adding environment driver");
mainDriver.EnvironmentDriver = new EssentialsEnvironmentDriver(mainDriver, props);
mainDriver.EnvironmentDriver.GetDevicesFromConfig(avDriver.CurrentRoom.Config.Environment);
}
mainDriver.HeaderDriver.SetupHeaderButtons(avDriver, avDriver.CurrentRoom);
panelController.LoadAndShowDriver(mainDriver); // This is a little convoluted.
if (panelController.Panel is TswFt5ButtonSystem)
{
var tsw = panelController.Panel as TswFt5ButtonSystem;
// Wire up hard keys
tsw.Power.UserObject = new Action<bool>(b => { if (!b) avDriver.EndMeetingPress(); });
//tsw.Home.UserObject = new Action<bool>(b => { if (!b) HomePressed(); });
if (mainDriver.EnvironmentDriver != null)
tsw.Lights.UserObject = new Action<bool>(b =>
{
if (!b)
{
//mainDriver.AvDriver.PopupInterlock.ShowInterlockedWithToggle(mainDriver.EnvironmentDriver.BackgroundSubpageJoin);
mainDriver.EnvironmentDriver.Toggle();
}
});
tsw.Up.UserObject = new Action<bool>(avDriver.VolumeUpPress);
tsw.Down.UserObject = new Action<bool>(avDriver.VolumeDownPress);
}
}
else
{
Debug.Console(0, panelController, "ERROR: Cannot load AvFunctionsDriver for room '{0}'", props.DefaultRoomKey);
}
});
return panelController;
}
}
}

View File

@@ -97,6 +97,8 @@ namespace PepperDash.Essentials.Fusion
CTimer PushNotificationTimer = null;
CTimer DailyTimeRequestTimer = null;
// Default poll time is 5 min unless overridden by config value
public long SchedulePollInterval = 300000;
@@ -126,52 +128,63 @@ namespace PepperDash.Essentials.Fusion
: base(room.Key + "-fusion")
{
Room = room;
IpId = ipId;
FusionStaticAssets = new Dictionary<int, FusionAsset>();
GUIDs = new FusionRoomGuids();
var mac = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0);
var slot = Global.ControlSystem.ProgramNumber;
string guidFilePath = string.Format(@"\NVRAM\Program{0}\{1}-FusionGuids.json", Global.ControlSystem.ProgramNumber, InitialParametersClass.ProgramIDTag);
GuidFileExists = File.Exists(guidFilePath);
if (GuidFileExists)
try
{
ReadGuidFile(guidFilePath);
}
else
{
GUIDs = new FusionRoomGuids(Room.Name, ipId, GUIDs.GenerateNewRoomGuid(slot, mac), FusionStaticAssets);
}
CreateSymbolAndBasicSigs(IpId);
SetUpSources();
SetUpCommunitcationMonitors();
SetUpDisplay();
SetUpError();
ExecuteCustomSteps();
Room = room;
if(Room.RoomOccupancy != null)
{
if(Room.OccupancyStatusProviderIsRemote)
SetUpRemoteOccupancy();
IpId = ipId;
FusionStaticAssets = new Dictionary<int, FusionAsset>();
GUIDs = new FusionRoomGuids();
var mac = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0);
var slot = Global.ControlSystem.ProgramNumber;
string guidFilePath = Global.FilePathPrefix + string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag);
GuidFileExists = File.Exists(guidFilePath);
// Check if file exists
if (!GuidFileExists)
{
// Does not exist. Create GUIDs
GUIDs = new FusionRoomGuids(Room.Name, ipId, GUIDs.GenerateNewRoomGuid(slot, mac), FusionStaticAssets);
}
else
{
SetUpLocalOccupancy();
// Exists. Read GUIDs
ReadGuidFile(guidFilePath);
}
CreateSymbolAndBasicSigs(IpId);
SetUpSources();
SetUpCommunitcationMonitors();
SetUpDisplay();
SetUpError();
ExecuteCustomSteps();
if (Room.RoomOccupancy != null)
{
if (Room.OccupancyStatusProviderIsRemote)
SetUpRemoteOccupancy();
else
{
SetUpLocalOccupancy();
}
}
// Make it so!
FusionRVI.GenerateFileForAllFusionDevices();
GenerateGuidFile(guidFilePath);
}
catch (Exception e)
{
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Building Fusion System Controller: {0}", e);
}
// Make it so!
FusionRVI.GenerateFileForAllFusionDevices();
GenerateGuidFile(guidFilePath);
}
/// <summary>
@@ -240,7 +253,7 @@ namespace PepperDash.Essentials.Fusion
{
if(string.IsNullOrEmpty(filePath))
{
Debug.Console(0, this, "Error reading guid file. No path specified.");
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Error reading guid file. No path specified.");
return;
}
@@ -265,18 +278,18 @@ namespace PepperDash.Essentials.Fusion
}
Debug.Console(0, this, "Fusion Guids successfully read from file:");
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Fusion Guids successfully read from file: {0}", filePath);
Debug.Console(1, this, "\nRoom Name: {0}\nIPID: {1:x}\n RoomGuid: {2}", Room.Name, IpId, RoomGuid);
foreach (KeyValuePair<int, FusionAsset> item in FusionStaticAssets)
foreach (var item in FusionStaticAssets)
{
Debug.Console(1, this, "\nAsset Name: {0}\nAsset No: {1}\n Guid: {2}", item.Value.Name, item.Value.SlotNumber, item.Value.InstanceId);
}
}
catch (Exception e)
{
Debug.Console(0, this, "Error reading guid file: {0}", e);
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Error reading guid file: {0}", e);
}
finally
{
@@ -288,7 +301,7 @@ namespace PepperDash.Essentials.Fusion
protected virtual void CreateSymbolAndBasicSigs(uint ipId)
{
Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid);
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating Fusion Room symbol with GUID: {0}", RoomGuid);
FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid);
FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use();
@@ -390,31 +403,20 @@ namespace PepperDash.Essentials.Fusion
protected void GetProcessorInfo()
{
//SystemName = FusionRoom.CreateOffsetStringSig(50, "Info - Processor - System Name", eSigIoMask.InputSigOnly);
//Model = FusionRoom.CreateOffsetStringSig(51, "Info - Processor - Model", eSigIoMask.InputSigOnly);
//SerialNumber = FusionRoom.CreateOffsetStringSig(52, "Info - Processor - Serial Number", eSigIoMask.InputSigOnly);
//Uptime = FusionRoom.CreateOffsetStringSig(53, "Info - Processor - Uptime", eSigIoMask.InputSigOnly);
Firmware = FusionRoom.CreateOffsetStringSig(61, "Info - Processor - Firmware", eSigIoMask.InputSigOnly);
for (int i = 0; i < Global.ControlSystem.NumProgramsSupported; i++)
if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server)
{
var join = 62 + i;
var progNum = i + 1;
Program[i] = FusionRoom.CreateOffsetStringSig((uint)join, string.Format("Info - Processor - Program {0}", progNum), eSigIoMask.InputSigOnly);
for (int i = 0; i < Global.ControlSystem.NumProgramsSupported; i++)
{
var join = 62 + i;
var progNum = i + 1;
Program[i] = FusionRoom.CreateOffsetStringSig((uint)join, string.Format("Info - Processor - Program {0}", progNum), eSigIoMask.InputSigOnly);
}
}
Firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion;
//var programs = ProcessorProgReg.GetProcessorProgReg();
//for (int i = 1; i < Global.ControlSystem.NumProgramsSupported; i++)
//{
// var join = 62 + i;
// var progNum = i + 1;
// if (programs[i].Exists)
// Program[i].InputSig.StringValue = programs[i].Name;
//}
}
@@ -465,16 +467,35 @@ namespace PepperDash.Essentials.Fusion
// Request current Fusion Server Time
RequestLocalDateTime(null);
string timeRequestID = "TimeRequest";
// Setup timer to request time daily
if (DailyTimeRequestTimer != null && !DailyTimeRequestTimer.Disposed)
{
DailyTimeRequestTimer.Stop();
DailyTimeRequestTimer.Dispose();
}
string timeRequest = string.Format("<LocalTimeRequest><RequestID>{0}</RequestID></LocalTimeRequest>", timeRequestID);
DailyTimeRequestTimer = new CTimer(RequestLocalDateTime, null, 86400000, 86400000);
FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQuery.StringValue = timeRequest;
DailyTimeRequestTimer.Reset(86400000, 86400000);
}
}
/// <summary>
/// Requests the local date and time from the Fusion Server
/// </summary>
/// <param name="callbackObject"></param>
public void RequestLocalDateTime(object callbackObject)
{
string timeRequestID = "TimeRequest";
string timeRequest = string.Format("<LocalTimeRequest><RequestID>{0}</RequestID></LocalTimeRequest>", timeRequestID);
FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQuery.StringValue = timeRequest;
}
/// <summary>
/// Generates a room schedule request for this room for the next 24 hours.
/// </summary>
@@ -984,6 +1005,10 @@ namespace PepperDash.Essentials.Fusion
/// </summary>
void SetUpCommunitcationMonitors()
{
uint displayNum = 0;
uint touchpanelNum = 0;
uint xpanelNum = 0;
// Attach to all room's devices with monitors.
//foreach (var dev in DeviceManager.Devices)
foreach (var dev in DeviceManager.GetDevices())
@@ -991,41 +1016,56 @@ namespace PepperDash.Essentials.Fusion
if (!(dev is ICommunicationMonitor))
continue;
var keyNum = ExtractNumberFromKey(dev.Key);
if (keyNum == -1)
{
Debug.Console(1, this, "WARNING: Cannot link device '{0}' to numbered Fusion monitoring attributes",
dev.Key);
continue;
}
string attrName = null;
uint attrNum = Convert.ToUInt32(keyNum);
string attrName = null;
uint attrNum = 1;
if (dev is EssentialsTouchpanelController)
//var keyNum = ExtractNumberFromKey(dev.Key);
//if (keyNum == -1)
//{
// Debug.Console(1, this, "WARNING: Cannot link device '{0}' to numbered Fusion monitoring attributes",
// dev.Key);
// continue;
//}
//uint attrNum = Convert.ToUInt32(keyNum);
// Check for UI devices
var uiDev = dev as EssentialsTouchpanelController;
if (uiDev != null)
{
if ((dev as EssentialsTouchpanelController).Panel is Crestron.SimplSharpPro.DeviceSupport.TswFt5Button)
{
if (attrNum > 10)
continue;
attrName = "Online - Touch Panel " + attrNum;
attrNum += 150;
}
else if ((dev as EssentialsTouchpanelController).Panel is Crestron.SimplSharpPro.UI.XpanelForSmartGraphics)
if (uiDev.Panel is Crestron.SimplSharpPro.UI.XpanelForSmartGraphics)
{
attrNum = attrNum + touchpanelNum;
if (attrNum > 10)
continue;
attrName = "Online - XPanel " + attrNum;
attrNum += 160;
touchpanelNum++;
}
}
else
{
attrNum = attrNum + xpanelNum;
if (attrNum > 10)
continue;
attrName = "Online - Touch Panel " + attrNum;
attrNum += 150;
xpanelNum++;
}
}
//else
if (dev is DisplayBase)
{
attrNum = attrNum + displayNum;
if (attrNum > 10)
continue;
attrName = "Online - Display " + attrNum;
attrNum += 170;
displayNum++;
}
//else if (dev is DvdDeviceBase)
//{
@@ -1187,14 +1227,23 @@ namespace PepperDash.Essentials.Fusion
{
RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc);
FusionRoom.FusionAssetStateChange += new FusionAssetStateEventHandler(FusionRoom_FusionAssetStateChange);
// Build Occupancy Asset?
// Link sigs?
Room.SetRoomOccupancy(this);
//Room.SetRoomOccupancy(this as IOccupancyStatusProvider, 0);
}
void FusionRoom_FusionAssetStateChange(FusionBase device, FusionAssetStateEventArgs args)
{
if (args.EventId == FusionAssetEventId.RoomOccupiedReceivedEventId || args.EventId == FusionAssetEventId.RoomUnoccupiedReceivedEventId)
RoomIsOccupiedFeedback.FireUpdate();
}
/// <summary>
/// Sets up remote occupancy that will relay the occupancy status determined by local system devices to Fusion
/// </summary>
@@ -1235,7 +1284,7 @@ namespace PepperDash.Essentials.Fusion
/// <returns>-1 if no number matched</returns>
int ExtractNumberFromKey(string key)
{
var capture = System.Text.RegularExpressions.Regex.Match(key, @"\D+(\d+)");
var capture = System.Text.RegularExpressions.Regex.Match(key, @"\b(\d+)");
if (!capture.Success)
return -1;
else return Convert.ToInt32(capture.Groups[1].Value);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -70,22 +70,14 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll</HintPath>
</Reference>
<Reference Include="Essentials Devices Common, Version=1.0.0.18129, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Essentials Devices Common\Essentials Devices Common\bin\Essentials Devices Common.dll</HintPath>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="PepperDashCorePortalSync, Version=1.0.0.27069, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\pepperdash-portal-sync\SspPortalSync\SspPortalSync\bin\PepperDashCorePortalSync.dll</HintPath>
</Reference>
<Reference Include="PepperDash_Core, Version=1.0.1.26313, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="PepperDash_Core, Version=1.0.3.27452, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\PepperDash.Core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll</HintPath>
</Reference>
<Reference Include="PepperDash_Essentials_Core, Version=1.0.0.18243, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Essentials Core\PepperDashEssentialsBase\bin\PepperDash_Essentials_Core.dll</HintPath>
<HintPath>..\..\Release Package\PepperDash_Core.dll</HintPath>
</Reference>
<Reference Include="PepperDash_Essentials_DM, Version=1.0.0.19343, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -107,6 +99,10 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe</HintPath>
</Reference>
<Reference Include="SimplSharpReflectionInterface, Version=1.0.5583.25238, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
@@ -129,11 +125,12 @@
<Compile Include="Configuration ORIGINAL\Factories\FactoryHelper.cs" />
<Compile Include="Config\ConfigReader.cs" />
<Compile Include="Config\EssentialsConfig.cs" />
<Compile Include="Config\DeviceFactory.cs" />
<Compile Include="Factory\DeviceFactory.cs" />
<Compile Include="Devices\Amplifier.cs" />
<Compile Include="Devices\DiscPlayer\OppoExtendedBdp.cs" />
<Compile Include="Devices\NUMERIC AppleTV.cs" />
<Compile Include="ControlSystem.cs" />
<Compile Include="Factory\UiDeviceFactory.cs" />
<Compile Include="OTHER\Fusion\EssentialsHuddleVtc1FusionController.cs" />
<Compile Include="OTHER\Fusion\FusionEventHandlers.cs" />
<Compile Include="OTHER\Fusion\FusionProcessorQueries.cs" />
@@ -142,12 +139,17 @@
<Compile Include="OTHER\Fusion\EssentialsHuddleSpaceFusionSystemControllerBase.cs" />
<Compile Include="HttpApiHandler.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Room\Config\DDVC01RoomPropertiesConfig.cs" />
<Compile Include="Room\Config\EssentialsPresentationPropertiesConfig.cs" />
<Compile Include="Room\Config\EssentialsHuddleRoomPropertiesConfig.cs" />
<Compile Include="Room\Config\EssentialsHuddleVtc1PropertiesConfig.cs" />
<Compile Include="Room\Config\EssentialsRoomEmergencyConfig.cs" />
<Compile Include="Room\Cotija\CotijaConfig.cs" />
<Compile Include="Room\Cotija\CotijaRoomBridge.cs" />
<Compile Include="Room\Cotija\CotijaDdvc01DeviceBridge.cs" />
<Compile Include="Room\Cotija\Interfaces.cs" />
<Compile Include="Room\Cotija\RoomBridges\CotijaBridgeBase.cs" />
<Compile Include="Room\Cotija\RoomBridges\CotijaDdvc01RoomBridge.cs" />
<Compile Include="Room\Cotija\RoomBridges\CotijaEssentialsHuddleSpaceRoomBridge.cs" />
<Compile Include="Room\Cotija\DeviceTypeInterfaces\IChannelExtensions.cs" />
<Compile Include="Room\Cotija\DeviceTypeInterfaces\IColorExtensions.cs" />
<Compile Include="Room\Cotija\DeviceTypeInterfaces\IDPadExtensions.cs" />
@@ -167,6 +169,10 @@
<Compile Include="FOR REFERENCE UI\PageControllers\PageControllerLargeSetTopBoxGeneric.cs" />
<Compile Include="FOR REFERENCE UI\PageControllers\LargeTouchpanelControllerBase.cs" />
<Compile Include="FOR REFERENCE UI\Panels\SmartGraphicsTouchpanelControllerBase.cs" />
<Compile Include="UIDrivers\Environment Drivers\EssentialsLightingDriver.cs" />
<Compile Include="UIDrivers\Environment Drivers\EssentialsEnvironmentDriver.cs" />
<Compile Include="UIDrivers\Environment Drivers\EssentialsShadeDriver.cs" />
<Compile Include="UIDrivers\Essentials\EssentialsHeaderDriver.cs" />
<Compile Include="UIDrivers\SigInterlock.cs" />
<Compile Include="UIDrivers\EssentialsHuddleVTC\EssentialsHuddlePresentationUiDriver.cs" />
<Compile Include="UIDrivers\EssentialsHuddle\EssentialsHuddleTechPageDriver.cs" />
@@ -200,6 +206,19 @@
<Compile Include="UI\SubpageReferenceListSourceItem.cs" />
<None Include="app.config" />
<None Include="Properties\ControlSystem.cfg" />
<EmbeddedResource Include="SGD\PepperDash Essentials iPad.sgd" />
<EmbeddedResource Include="SGD\PepperDash Essentials TSW-560.sgd" />
<EmbeddedResource Include="SGD\PepperDash Essentials TSW-760.sgd" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Essentials Core\PepperDashEssentialsBase\PepperDash_Essentials_Core.csproj">
<Project>{A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5}</Project>
<Name>PepperDash_Essentials_Core</Name>
</ProjectReference>
<ProjectReference Include="..\..\Essentials Devices Common\Essentials Devices Common\Essentials Devices Common.csproj">
<Project>{892B761C-E479-44CE-BD74-243E9214AF13}</Project>
<Name>Essentials Devices Common</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CompactFramework.CSharp.targets" />
<ProjectExtensions>

View File

@@ -4,5 +4,5 @@
[assembly: AssemblyCompany("PepperDash Technology Corp")]
[assembly: AssemblyProduct("PepperDashEssentials")]
[assembly: AssemblyCopyright("Copyright © PepperDash Technology Corp 2017")]
[assembly: AssemblyVersion("1.0.2.*")]
[assembly: AssemblyVersion("1.2.0.*")]

View File

@@ -0,0 +1,12 @@
using System.Reflection;
[assembly: AssemblyTitle("PepperDashEssentials")]
[assembly: AssemblyCompany("PepperDash Technology Corp")]
[assembly: AssemblyProduct("PepperDashEssentials")]
[assembly: AssemblyCopyright("Copyright © PepperDash Technology Corp 2017")]
<<<<<<< HEAD
[assembly: AssemblyVersion("1.1.8.*")]
=======
[assembly: AssemblyVersion("1.3.0.*")]
>>>>>>> feature/ecs-684

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<ControlSystem>
<Name>Desk MC3</Name>
<Address>ssh 10.0.0.15</Address>
<ProgramSlot>Program01</ProgramSlot>
<Storage>Internal Flash</Storage>
<?xml version="1.0" encoding="utf-8"?>
<ControlSystem>
<Name>Test RMC3</Name>
<Address>auto 192.168.1.40</Address>
<ProgramSlot>Program01</ProgramSlot>
<Storage>Internal Flash</Storage>
</ControlSystem>

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Room.Config
{
public class DDVC01RoomPropertiesConfig : EssentialsHuddleVtc1PropertiesConfig
{
[JsonProperty("roomPhoneNumber")]
public string RoomPhoneNumber { get; set; }
[JsonProperty("roomURI")]
public string RoomURI { get; set; }
[JsonProperty("speedDials")]
public List<DDVC01SpeedDial> SpeedDials { get; set; }
[JsonProperty("volumeSliderNames")]
public List<string> VolumeSliderNames { get; set; }
}
public class DDVC01SpeedDial
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("number")]
public string Number { get; set; }
}
}

View File

@@ -3,16 +3,22 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Room.Config
{
public class EssentialsHuddleVtc1PropertiesConfig : EssentialsRoomPropertiesConfig
{
public string DefaultDisplayKey { get; set; }
public string DefaultAudioKey { get; set; }
public string SourceListKey { get; set; }
public string DefaultSourceItem { get; set; }
public string VideoCodecKey { get; set; }
[JsonProperty("defaultDisplayKey")]
public string DefaultDisplayKey { get; set; }
[JsonProperty("defaultAudioKey")]
public string DefaultAudioKey { get; set; }
[JsonProperty("sourceListKey")]
public string SourceListKey { get; set; }
[JsonProperty("defaultSourceItem")]
public string DefaultSourceItem { get; set; }
[JsonProperty("videoCodecKey")]
public string VideoCodecKey { get; set; }
}
}

View File

@@ -29,6 +29,10 @@ namespace PepperDash.Essentials.Room.Config
var disp = DeviceManager.GetDeviceForKey(props.DefaultDisplayKey) as IRoutingSinkWithSwitching;
var audio = DeviceManager.GetDeviceForKey(props.DefaultAudioKey) as IRoutingSinkNoSwitching;
var huddle = new EssentialsHuddleSpaceRoom(Key, Name, disp, audio, props);
if (props.Occupancy != null)
huddle.SetRoomOccupancy(DeviceManager.GetDeviceForKey(props.Occupancy.DeviceKey) as
PepperDash.Essentials.Devices.Common.Occupancy.IOccupancyStatusProvider, props.Occupancy.TimoutMinutes);
huddle.LogoUrl = props.Logo.GetUrl();
huddle.SourceListKey = props.SourceListKey;
huddle.DefaultSourceItem = props.DefaultSourceItem;
@@ -67,16 +71,23 @@ namespace PepperDash.Essentials.Room.Config
// Add Occupancy object from config
if (props.Occupancy != null)
rm.SetRoomOccupancy(DeviceManager.GetDeviceForKey(props.Occupancy.DeviceKey) as PepperDash.Essentials.Devices.Common.Occupancy.IOccupancyStatusProvider);
rm.SetRoomOccupancy(DeviceManager.GetDeviceForKey(props.Occupancy.DeviceKey) as
PepperDash.Essentials.Devices.Common.Occupancy.IOccupancyStatusProvider, props.Occupancy.TimoutMinutes);
rm.LogoUrl = props.Logo.GetUrl();
rm.SourceListKey = props.SourceListKey;
rm.DefaultSourceItem = props.DefaultSourceItem;
rm.DefaultVolume = (ushort)(props.Volumes.Master.Level * 65535 / 100);
rm.MicrophonePrivacy = GetMicrophonePrivacy(props, rm); // Get Microphone Privacy object, if any
rm.Emergency = GetEmergency(props, rm); // Get emergency object, if any
return rm;
}
else if (typeName == "ddvc01Bridge")
{
return new Device(Key, Name); // placeholder device that does nothing.
}
return null;
}
@@ -97,6 +108,70 @@ namespace PepperDash.Essentials.Room.Config
}
return null;
}
/// <summary>
///
/// </summary>
/// <param name="props"></param>
/// <param name="room"></param>
/// <returns></returns>
PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController GetMicrophonePrivacy(
EssentialsRoomPropertiesConfig props, EssentialsHuddleVtc1Room room)
{
var microphonePrivacy = props.MicrophonePrivacy;
if (microphonePrivacy == null)
{
Debug.Console(0, "ERROR: Cannot create microphone privacy with null properties");
return null;
}
// Get the MicrophonePrivacy device from the device manager
var mP = (DeviceManager.GetDeviceForKey(props.MicrophonePrivacy.DeviceKey) as
PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController);
// Set this room as the IPrivacy device
if (mP == null)
{
Debug.Console(0, "ERROR: Selected device {0} is not MicrophonePrivacyController", props.MicrophonePrivacy.DeviceKey);
return null;
}
mP.SetPrivacyDevice(room);
var behaviour = props.MicrophonePrivacy.Behaviour.ToLower();
if (behaviour == null)
{
Debug.Console(0, "WARNING: No behaviour defined for MicrophonePrivacyController");
return null;
}
if (behaviour == "trackroomstate")
{
// Tie LED enable to room power state
room.OnFeedback.OutputChange += (o, a) =>
{
if (room.OnFeedback.BoolValue)
mP.EnableLeds = true;
else
mP.EnableLeds = false;
};
mP.EnableLeds = room.OnFeedback.BoolValue;
}
else if (behaviour == "trackcallstate")
{
// Tie LED enable to room power state
room.InCallFeedback.OutputChange += (o, a) =>
{
if (room.InCallFeedback.BoolValue)
mP.EnableLeds = true;
else
mP.EnableLeds = false;
};
mP.EnableLeds = room.InCallFeedback.BoolValue;
}
return mP;
}
}
/// <summary>
@@ -104,31 +179,91 @@ namespace PepperDash.Essentials.Room.Config
/// </summary>
public class EssentialsRoomPropertiesConfig
{
public EssentialsRoomEmergencyConfig Emergency { get; set; }
[JsonProperty("addresses")]
public EssentialsRoomAddressPropertiesConfig Addresses { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("emergency")]
public EssentialsRoomEmergencyConfig Emergency { get; set; }
[JsonProperty("help")]
public EssentialsHelpPropertiesConfig Help { get; set; }
[JsonProperty("helpMessage")]
public string HelpMessage { get; set; }
public string Description { get; set; }
public int ShutdownVacancySeconds { get; set; }
public int ShutdownPromptSeconds { get; set; }
public EssentialsHelpPropertiesConfig Help { get; set; }
public EssentialsOneButtonMeetingPropertiesConfig OneButtonMeeting { get; set; }
public EssentialsRoomAddressPropertiesConfig Addresses { get; set; }
public EssentialsRoomOccSensorConfig Occupancy { get; set; }
public EssentialsLogoPropertiesConfig Logo { get; set; }
[JsonProperty("environment")]
public EssentialsEnvironmentPropertiesConfig Environment { get; set; }
[JsonProperty("logo")]
public EssentialsLogoPropertiesConfig Logo { get; set; }
[JsonProperty("microphonePrivacy")]
public EssentialsRoomMicrophonePrivacyConfig MicrophonePrivacy { get; set; }
[JsonProperty("occupancy")]
public EssentialsRoomOccSensorConfig Occupancy { get; set; }
[JsonProperty("oneButtonMeeting")]
public EssentialsOneButtonMeetingPropertiesConfig OneButtonMeeting { get; set; }
[JsonProperty("shutdownVacancySeconds")]
public int ShutdownVacancySeconds { get; set; }
[JsonProperty("shutdownPromptSeconds")]
public int ShutdownPromptSeconds { get; set; }
[JsonProperty("tech")]
public EssentialsRoomTechConfig Tech { get; set; }
public EssentialsRoomVolumesConfig Volumes { get; set; }
[JsonProperty("volumes")]
public EssentialsRoomVolumesConfig Volumes { get; set; }
[JsonProperty("zeroVolumeWhenSwtichingVolumeDevices")]
public bool ZeroVolumeWhenSwtichingVolumeDevices { get; set; }
}
public class EssentialsEnvironmentPropertiesConfig
{
public bool Enabled { get; set; }
[JsonProperty("deviceKeys")]
public List<string> DeviceKeys { get; set; }
public EssentialsEnvironmentPropertiesConfig()
{
DeviceKeys = new List<string>();
}
}
public class EssentialsRoomMicrophonePrivacyConfig
{
[JsonProperty("deviceKey")]
public string DeviceKey { get; set; }
[JsonProperty("behaviour")]
public string Behaviour { get; set; }
}
/// <summary>
/// Properties for the help text box
/// </summary>
public class EssentialsHelpPropertiesConfig
{
public string Message { get; set; }
public bool ShowCallButton { get; set; }
/// <summary>
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("showCallButton")]
public bool ShowCallButton { get; set; }
/// <summary>
/// Defaults to "Call Help Desk"
/// </summary>
public string CallButtonText { get; set; }
[JsonProperty("callButtonText")]
public string CallButtonText { get; set; }
public EssentialsHelpPropertiesConfig()
{
@@ -141,13 +276,17 @@ namespace PepperDash.Essentials.Room.Config
/// </summary>
public class EssentialsOneButtonMeetingPropertiesConfig
{
public bool Enable { get; set; }
[JsonProperty("enable")]
public bool Enable { get; set; }
}
public class EssentialsRoomAddressPropertiesConfig
{
public string PhoneNumber { get; set; }
public string SipAddress { get; set; }
[JsonProperty("phoneNumber")]
public string PhoneNumber { get; set; }
[JsonProperty("sipAddress")]
public string SipAddress { get; set; }
}
@@ -156,8 +295,11 @@ namespace PepperDash.Essentials.Room.Config
/// </summary>
public class EssentialsLogoPropertiesConfig
{
public string Type { get; set; }
public string Url { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
/// <summary>
/// Gets either the custom URL, a local-to-processor URL, or null if it's a default logo
/// </summary>
@@ -177,12 +319,16 @@ namespace PepperDash.Essentials.Room.Config
/// </summary>
public class EssentialsRoomOccSensorConfig
{
public string DeviceKey { get; set; }
[JsonProperty("deviceKey")]
public string DeviceKey { get; set; }
[JsonProperty("timoutMinutes")]
public int TimoutMinutes { get; set; }
}
public class EssentialsRoomTechConfig
{
[JsonProperty("password")]
public string Password { get; set; }
}
}

View File

@@ -5,10 +5,29 @@ using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core.Config;
using Newtonsoft.Json;
namespace PepperDash.Essentials
{
public class CotijaConfig : DeviceConfig
/// <summary>
///
/// </summary>
public class CotijaConfig
{
public string serverUrl { get; set; }
[JsonProperty("serverUrl")]
public string ServerUrl { get; set; }
[JsonProperty("clientAppUrl")]
public string ClientAppUrl { get; set; }
}
/// <summary>
///
/// </summary>
public class CotijaDdvc01RoomBridgePropertiesConfig
{
[JsonProperty("eiscId")]
public string EiscId { get; set; }
}
}

View File

@@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.EthernetCommunication;
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Room.Cotija
{
/// <summary>
/// Represents a generic device connection through to and EISC for DDVC01
/// </summary>
public class CotijaDdvc01DeviceBridge : Device, IChannel, INumericKeypad
{
/// <summary>
/// EISC used to talk to Simpl
/// </summary>
ThreeSeriesTcpIpEthernetIntersystemCommunications EISC;
public CotijaDdvc01DeviceBridge(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
}
}

View File

@@ -5,53 +5,83 @@ using System.Text;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.Reflection;
using Crestron.SimplSharpPro.CrestronThread;
using Crestron.SimplSharp.CrestronWebSocketClient;
using Crestron.SimplSharpPro;
using Crestron.SimplSharp.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Room.Cotija;
namespace PepperDash.Essentials
{
public class CotijaSystemController : Device
{
GenericHttpSseClient SseClient;
WebSocketClient WSClient;
CCriticalSection FileLock;
/// <summary>
/// Prevents post operations from stomping on each other and getting lost
/// </summary>
CEvent PostLockEvent = new CEvent(true, true);
CotijaConfig Config;
CEvent RegisterLockEvent = new CEvent(true, true);
HttpClient Client;
public CotijaConfig Config { get; private set; }
Dictionary<string, Object> ActionDictionary = new Dictionary<string, Object>(StringComparer.InvariantCultureIgnoreCase);
Dictionary<string, CTimer> PushedActions = new Dictionary<string, CTimer>();
CTimer ServerHeartbeat;
CTimer ServerHeartbeatCheckTimer;
long ServerHeartbeatInterval = 20000;
CTimer ServerReconnect;
CTimer ServerReconnectTimer;
long ServerReconnectInterval = 5000;
string SystemUuid;
public List<CotijaEssentialsHuddleSpaceRoomBridge> CotijaRooms { get; private set; }
List<CotijaBridgeBase> RoomBridges = new List<CotijaBridgeBase>();
long ButtonHeartbeatInterval = 1000;
/// <summary>
/// Used for tracking HTTP debugging
/// </summary>
bool HttpDebugEnabled;
/// <summary>
///
/// </summary>
/// <param name="key"></param>
/// <param name="name"></param>
/// <param name="config"></param>
public CotijaSystemController(string key, string name, CotijaConfig config) : base(key, name)
{
Config = config;
Debug.Console(0, this, "Mobile UI controller initializing for server:{0}", config.ServerUrl);
CotijaRooms = new List<CotijaEssentialsHuddleSpaceRoomBridge>();
CrestronConsole.AddNewConsoleCommand(AuthorizeSystem,
"mobileauth", "Authorizes system to talk to cotija server", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => ShowInfo(),
"mobileinfo", "Shows information for current mobile control session", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => {
s = s.Trim();
if(!string.IsNullOrEmpty(s))
{
HttpDebugEnabled = (s.Trim() != "0");
}
CrestronConsole.ConsoleCommandResponse("HTTP Debug {0}", HttpDebugEnabled ? "Enabled" : "Disabled");
},
"mobilehttpdebug", "1 enables more verbose HTTP response debugging", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(TestHttpRequest,
"mobilehttprequest", "Tests an HTTP get to URL given", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(RegisterSystemToServer, "InitializeHttpClient", "Initializes a new HTTP client connection to a specified URL", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(DisconnectSseClient, "CloseHttpClient", "Closes the active HTTP client", ConsoleAccessLevelEnum.AccessOperator);
AddPostActivationAction(() => RegisterSystemToServer(null));
}
/// <summary>
@@ -67,12 +97,12 @@ namespace PepperDash.Essentials
}
else
{
Debug.Console(1, this, "Cannot add action with key '{0}' because key already exists in ActionDictionary.");
Debug.Console(1, this, "Cannot add action with key '{0}' because key already exists in ActionDictionary.", key);
}
}
/// <summary>
/// Removes and action from the dictionary
/// Removes an action from the dictionary
/// </summary>
/// <param name="key"></param>
public void RemoveAction(string key)
@@ -81,130 +111,226 @@ namespace PepperDash.Essentials
ActionDictionary.Remove(key);
}
void ReconnectToServer(object o)
/// <summary>
///
/// </summary>
/// <param name="bridge"></param>
public void AddBridge(CotijaBridgeBase bridge)
{
RoomBridges.Add(bridge);
var b = bridge as IDelayedConfiguration;
if (b != null)
{
Debug.Console(0, this, "Adding room bridge with delayed configuration");
b.ConfigurationIsReady += new EventHandler<EventArgs>(bridge_ConfigurationIsReady);
}
else
{
Debug.Console(0, this, "Adding room bridge and sending configuration");
RegisterSystemToServer();
}
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void bridge_ConfigurationIsReady(object sender, EventArgs e)
{
Debug.Console(1, this, "Bridge ready. Registering");
// send the configuration object to the server
RegisterSystemToServer();
}
/// <summary>
///
/// </summary>
/// <param name="o"></param>
void ReconnectToServerTimerCallback(object o)
{
RegisterSystemToServer(null);
RegisterSystemToServer();
}
/// <summary>
/// Verifies system connection with servers
/// </summary>
/// <param name="command"></param>
void AuthorizeSystem(string code)
{
if (string.IsNullOrEmpty(code))
{
CrestronConsole.ConsoleCommandResponse("Please enter a user code to authorize a system");
return;
}
var req = new HttpClientRequest();
string url = string.Format("http://{0}/api/system/grantcode/{1}/{2}", Config.ServerUrl, code, SystemUuid);
Debug.Console(0, this, "Authorizing to: {0}", url);
if (string.IsNullOrEmpty(Config.ServerUrl))
{
CrestronConsole.ConsoleCommandResponse("Config URL address is not set. Check portal configuration");
return;
}
try
{
req.Url.Parse(url);
new HttpClient().DispatchAsync(req, (r, e) =>
{
CheckHttpDebug(r, e);
if (e == HTTP_CALLBACK_ERROR.COMPLETED)
{
if (r.Code == 200)
{
Debug.Console(0, "System authorized, sending config.");
RegisterSystemToServer();
}
else if (r.Code == 404)
{
if (r.ContentString.Contains("codeNotFound"))
{
Debug.Console(0, "Authorization failed, code not found for system UUID {0}", SystemUuid);
}
else if (r.ContentString.Contains("uuidNotFound"))
{
Debug.Console(0, "Authorization failed, uuid {0} not found. Check Essentials configuration is correct",
SystemUuid);
}
}
}
else
Debug.Console(0, this, "Error {0} in authorizing system", e);
});
}
catch (Exception e)
{
Debug.Console(0, this, "Error in authorizing: {0}", e);
}
}
/// <summary>
/// Dumps info in response to console command.
/// </summary>
void ShowInfo()
{
var url = Config != null ? Config.ServerUrl : "No config";
string name;
string code;
if (RoomBridges != null && RoomBridges.Count > 0)
{
name = RoomBridges[0].RoomName;
code = RoomBridges[0].UserCode;
}
else
{
name = "No config";
code = "Not available";
}
var conn = WSClient == null ? "No client" : (WSClient.Connected ? "Yes" : "No");
CrestronConsole.ConsoleCommandResponse(@"Mobile Control Information:
Server address: {0}
System Name: {1}
System UUID: {2}
System User code: {3}
Connected?: {4}", url, name, SystemUuid,
code, conn);
}
/// <summary>
/// Registers the room with the server
/// </summary>
/// <param name="url">URL of the server, including the port number, if not 80. Format: "serverUrlOrIp:port"</param>
void RegisterSystemToServer(string command)
void RegisterSystemToServer()
{
try
var ready = RegisterLockEvent.Wait(20000);
if (!ready)
{
Debug.Console(1, this, "RegisterSystemToServer failed to enter after 20 seconds. Ignoring");
return;
}
RegisterLockEvent.Reset();
try
{
string filePath = string.Format(@"\NVRAM\Program{0}\configurationFile.json", Global.ControlSystem.ProgramNumber);
string postBody = null;
var confObject = ConfigReader.ConfigObject;
confObject.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name;
var version = Assembly.GetExecutingAssembly().GetName().Version;
confObject.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
confObject.Info.RuntimeInfo.OsVersion = Crestron.SimplSharp.CrestronEnvironment.OSVersion.Firmware;
if (string.IsNullOrEmpty(filePath))
{
Debug.Console(0, this, "Error reading file. No path specified.");
return;
}
FileLock = new CCriticalSection();
if (FileLock.TryEnter())
{
Debug.Console(1, this, "Reading configuration file to extract system UUID...");
postBody = File.ReadToEnd(filePath, Encoding.ASCII);
Debug.Console(2, this, "{0}", postBody);
FileLock.Leave();
}
string postBody = JsonConvert.SerializeObject(confObject);
SystemUuid = confObject.SystemUuid;
if (string.IsNullOrEmpty(postBody))
{
Debug.Console(1, "Post Body is null or empty");
Debug.Console(1, this, "ERROR: Config body is empty. Cannot register with server.");
}
else
{
Client = new HttpClient();
var regClient = new HttpClient();
regClient.Verbose = true;
regClient.KeepAlive = true;
string url = string.Format("http://{0}/api/system/join/{1}", Config.ServerUrl, SystemUuid);
Debug.Console(1, this, "Joining server at {0}", url);
HttpClientRequest request = new HttpClientRequest();
Client.Verbose = true;
Client.KeepAlive = true;
SystemUuid = Essentials.ConfigReader.ConfigObject.SystemUuid;
string url = string.Format("http://{0}/api/system/join/{1}", Config.serverUrl, SystemUuid);
request.Url.Parse(url);
request.RequestType = RequestType.Post;
request.Header.SetHeaderValue("Content-Type", "application/json");
request.ContentString = postBody;
Client.DispatchAsync(request, PostConnectionCallback);
var err = regClient.DispatchAsync(request, RegistrationConnectionCallback);
}
}
catch (Exception e)
{
Debug.Console(0, this, "Error Initilizing Room: {0}", e);
Debug.Console(0, this, "ERROR: Initilizing Room: {0}", e);
RegisterLockEvent.Set();
StartReconnectTimer();
}
}
/// <summary>
/// Posts a message to the server from a room
/// Sends a message to the server from a room
/// </summary>
/// <param name="room">room from which the message originates</param>
/// <param name="o">object to be serialized and sent in post body</param>
public void PostToServer(EssentialsRoomBase room, JObject o)
public void SendMessageToServer(JObject o)
{
try
if (WSClient != null && WSClient.Connected)
{
if (Client == null)
Client = new HttpClient();
//HttpClient client = new HttpClient();
HttpClientRequest request = new HttpClientRequest();
Client.Verbose = true;
Client.KeepAlive = true;
string url = string.Format("http://{0}/api/room/{1}/status", Config.serverUrl, string.Format("{0}--{1}", SystemUuid, room.Key));
request.Url.Parse(url);
request.RequestType = RequestType.Post;
request.Header.SetHeaderValue("Content-Type", "application/json");
request.KeepAlive = true;
// Ignore any null objects when serializing and remove formatting
string ignored = JsonConvert.SerializeObject(o, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
request.ContentString = ignored;
Debug.Console(1, this, "Posting to '{0}':\n{1}", url, request.ContentString);
Client.DispatchAsync(request, (r, err) => { if (r != null) { Debug.Console(1, this, "Status Response Code: {0}", r.Code); } });
StartReconnectTimer(ServerReconnectInterval, ServerReconnectInterval);
}
catch(Exception e)
{
Debug.Console(1, this, "Error Posting to Server: {0}", e);
string message = JsonConvert.SerializeObject(o, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
Debug.Console(1, this, "Message TX: {0}", message);
var messageBytes = System.Text.Encoding.UTF8.GetBytes(message);
WSClient.Send(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME);
//WSClient.SendAsync(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME);
}
}
/// <summary>
/// Disconnects the SSE Client and stops the heartbeat timer
/// </summary>
/// <param name="command"></param>
void DisconnectSseClient(string command)
void DisconnectStreamClient(string command)
{
if(SseClient != null)
SseClient.Disconnect();
//if(SseClient != null)
// SseClient.Disconnect();
if (ServerHeartbeat != null)
if (WSClient != null && WSClient.Connected)
WSClient.Disconnect();
if (ServerHeartbeatCheckTimer != null)
{
ServerHeartbeat.Stop();
ServerHeartbeatCheckTimer.Stop();
ServerHeartbeat = null;
ServerHeartbeatCheckTimer = null;
}
}
@@ -213,76 +339,88 @@ namespace PepperDash.Essentials
/// </summary>
/// <param name="resp"></param>
/// <param name="err"></param>
void PostConnectionCallback(HttpClientResponse resp, HTTP_CALLBACK_ERROR err)
void RegistrationConnectionCallback(HttpClientResponse resp, HTTP_CALLBACK_ERROR err)
{
CheckHttpDebug(resp, err);
Debug.Console(1, this, "RegistrationConnectionCallback: {0}", err);
try
{
if (resp != null && resp.Code == 200)
{
if(ServerReconnect != null)
if(ServerReconnectTimer != null)
{
ServerReconnect.Stop();
ServerReconnect = null;
ServerReconnectTimer.Stop();
ServerReconnectTimer = null;
}
if (SseClient == null)
{
ConnectSseClient(null);
}
// Success here!
ConnectStreamClient();
}
else
{
if (resp != null)
Debug.Console(1, this, "Response from server: {0}\n{1}", resp.Code, err);
else
Debug.Console(1, this, "Null response received from server.");
if (resp != null)
Debug.Console(1, this, "Response from server: {0}\n{1}", resp.Code, err);
else
{
Debug.Console(1, this, "Null response received from server.");
}
StartReconnectTimer();
}
}
catch (Exception e)
{
Debug.Console(1, this, "Error Initializing SSE Client: {0}", e);
Debug.Console(1, this, "Error Initializing Stream Client: {0}", e);
StartReconnectTimer();
}
RegisterLockEvent.Set();
}
/// <summary>
/// Executes when we don't get a heartbeat message in time. Triggers reconnect.
/// </summary>
/// <param name="o"></param>
void HeartbeatExpired(object o)
/// <param name="o">For CTimer callback. Not used</param>
void HeartbeatExpiredTimerCallback(object o)
{
if (ServerHeartbeat != null)
Debug.Console(1, this, "Heartbeat Timer Expired.");
if (ServerHeartbeatCheckTimer != null)
{
Debug.Console(1, this, "Heartbeat Timer Expired.");
ServerHeartbeat.Stop();
ServerHeartbeat = null;
ServerHeartbeatCheckTimer.Stop();
ServerHeartbeatCheckTimer = null;
}
StartReconnectTimer(ServerReconnectInterval, ServerReconnectInterval);
StartReconnectTimer();
}
void StartReconnectTimer(long dueTime, long repeatTime)
/// <summary>
///
/// </summary>
/// <param name="dueTime"></param>
/// <param name="repeatTime"></param>
void StartReconnectTimer()
{
// Start the reconnect timer
ServerReconnect = new CTimer(ReconnectToServer, null, dueTime, repeatTime);
ServerReconnect.Reset(dueTime, repeatTime);
if (ServerReconnectTimer == null)
{
ServerReconnectTimer = new CTimer(ReconnectToServerTimerCallback, null, ServerReconnectInterval, ServerReconnectInterval);
Debug.Console(1, this, "Reconnect Timer Started.");
}
ServerReconnectTimer.Reset(ServerReconnectInterval, ServerReconnectInterval);
}
void StartHearbeatTimer(long dueTime, long repeatTime)
/// <summary>
///
/// </summary>
/// <param name="dueTime"></param>
/// <param name="repeatTime"></param>
void ResetOrStartHearbeatTimer()
{
if (ServerHeartbeat == null)
if (ServerHeartbeatCheckTimer == null)
{
ServerHeartbeat = new CTimer(HeartbeatExpired, null, dueTime, repeatTime);
ServerHeartbeatCheckTimer = new CTimer(HeartbeatExpiredTimerCallback, null, ServerHeartbeatInterval, ServerHeartbeatInterval);
Debug.Console(2, this, "Heartbeat Timer Started.");
Debug.Console(1, this, "Heartbeat Timer Started.");
}
ServerHeartbeat.Reset(dueTime, repeatTime);
Debug.Console(2, this, "Heartbeat Timer Reset.");
ServerHeartbeatCheckTimer.Reset(ServerHeartbeatInterval, ServerHeartbeatInterval);
}
@@ -290,156 +428,263 @@ namespace PepperDash.Essentials
/// Connects the SSE Client
/// </summary>
/// <param name="o"></param>
void ConnectSseClient(object o)
void ConnectStreamClient()
{
Debug.Console(0, this, "Initializing SSE Client.");
Debug.Console(0, this, "Initializing Stream client to server.");
if (SseClient == null)
{
SseClient = new GenericHttpSseClient(string.Format("{0}-SseClient", Key), Name);
CommunicationGather LineGathered = new CommunicationGather(SseClient, "\x0d\x0a");
LineGathered.LineReceived += new EventHandler<GenericCommMethodReceiveTextArgs>(LineGathered_LineReceived);
}
else
{
if (SseClient.IsConnected)
{
SseClient.Disconnect();
}
}
string uuid = Essentials.ConfigReader.ConfigObject.SystemUuid;
SseClient.Url = string.Format("http://{0}/api/system/stream/{1}", Config.serverUrl, uuid);
SseClient.Connect();
if (WSClient == null)
{
WSClient = new WebSocketClient();
}
WSClient.URL = string.Format("wss://{0}/system/join/{1}", Config.ServerUrl, this.SystemUuid);
WSClient.Connect();
Debug.Console(0, this, "Websocket connected");
WSClient.ReceiveCallBack = WebsocketReceiveCallback;
//WSClient.SendCallBack = WebsocketSendCallback;
WSClient.ReceiveAsync();
}
void LineGathered_LineReceived(object sender, GenericCommMethodReceiveTextArgs e)
/// <summary>
/// Resets reconnect timer and updates usercode
/// </summary>
/// <param name="content"></param>
void HandleHeartBeat(JToken content)
{
var code = content["userCode"];
if(code != null)
{
foreach (var b in RoomBridges)
{
b.SetUserCode(code.Value<string>());
}
}
ResetOrStartHearbeatTimer();
}
/// <summary>
/// Outputs debug info when enabled
/// </summary>
/// <param name="req"></param>
/// <param name="r"></param>
/// <param name="e"></param>
void CheckHttpDebug(HttpClientResponse r, HTTP_CALLBACK_ERROR e)
{
if (HttpDebugEnabled)
{
Debug.Console(0, this, "------ Begin HTTP Debug ---------------------------------------");
Debug.Console(0, this, "HTTP Response URL: {0}", r.ResponseUrl.ToString());
Debug.Console(0, this, "HTTP Response 'error' {0}", e);
Debug.Console(0, this, "HTTP Response code: {0}", r.Code);
Debug.Console(0, this, "HTTP Response content: \r{0}", r.ContentString);
Debug.Console(0, this, "------ End HTTP Debug -----------------------------------------");
}
}
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <param name="opcode"></param>
/// <param name="err"></param>
int WebsocketReceiveCallback(byte[] data, uint length, WebSocketClient.WEBSOCKET_PACKET_TYPES opcode,
WebSocketClient.WEBSOCKET_RESULT_CODES err)
{
var rx = System.Text.Encoding.UTF8.GetString(data, 0, (int)length);
if(rx.Length > 0)
ParseStreamRx(rx);
WSClient.ReceiveAsync();
return 1;
}
/// <summary>
/// Callback to catch possible errors in sending via the websocket
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
int WebsocketSendCallback(Crestron.SimplSharp.CrestronWebSocketClient.WebSocketClient.WEBSOCKET_RESULT_CODES result)
{
//Debug.Console(1, this, "Received from Server: '{0}'", e.Text);
Debug.Console(1, this, "SendCallback result: {0}", result);
if(e.Text.IndexOf("data:") > -1)
return 1;
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void ParseStreamRx(string message)
{
if(string.IsNullOrEmpty(message))
return;
Debug.Console(1, this, "Message RX: '{0}'", message);
try
{
var message = e.Text.Substring(6);
var messageObj = JObject.Parse(message);
Debug.Console(1, this, "Message: '{0}'", message);
var type = messageObj["type"].Value<string>();
try
if (type == "hello")
{
var messageObj = JObject.Parse(message);
var type = messageObj["type"].Value<string>();
if (type == "hello")
{
StartHearbeatTimer(ServerHeartbeatInterval, ServerHeartbeatInterval);
}
else if (type == "/system/heartbeat")
{
StartHearbeatTimer(ServerHeartbeatInterval, ServerHeartbeatInterval);
}
else if (type == "close")
{
SseClient.Disconnect();
// Start the reconnect timer
ServerReconnect = new CTimer(ConnectSseClient, null, ServerReconnectInterval, ServerReconnectInterval);
ServerReconnect.Reset(ServerReconnectInterval, ServerReconnectInterval);
}
else
{
// Check path against Action dictionary
if (ActionDictionary.ContainsKey(type))
{
var action = ActionDictionary[type];
if (action is Action)
{
(action as Action)();
}
else if (action is PressAndHoldAction)
{
var stateString = messageObj["content"]["state"].Value<string>();
// Look for a button press event
if (!string.IsNullOrEmpty(stateString))
{
switch (stateString)
{
case "true":
{
if (!PushedActions.ContainsKey(type))
{
PushedActions.Add(type, new CTimer(o =>
{
(action as PressAndHoldAction)(false);
PushedActions.Remove(type);
}, null, ButtonHeartbeatInterval, ButtonHeartbeatInterval));
}
// Maybe add an else to reset the timer
break;
}
case "held":
{
if (!PushedActions.ContainsKey(type))
{
PushedActions[type].Reset(ButtonHeartbeatInterval, ButtonHeartbeatInterval);
}
return;
}
case "false":
{
if (PushedActions.ContainsKey(type))
{
PushedActions[type].Stop();
PushedActions.Remove(type);
}
break;
}
}
(action as PressAndHoldAction)(stateString == "true");
}
}
else if (action is Action<bool>)
{
var stateString = messageObj["content"]["state"].Value<string>();
if (!string.IsNullOrEmpty(stateString))
{
(action as Action<bool>)(stateString == "true");
}
}
else if (action is Action<ushort>)
{
(action as Action<ushort>)(messageObj["content"]["value"].Value<ushort>());
}
else if (action is Action<string>)
{
(action as Action<string>)(messageObj["content"]["value"].Value<string>());
}
else if (action is Action<SourceSelectMessageContent>)
{
(action as Action<SourceSelectMessageContent>)(messageObj["content"]
.ToObject<SourceSelectMessageContent>());
}
}
}
}
catch (Exception err)
{
Debug.Console(1, this, "Unable to parse message: {0}", err);
ResetOrStartHearbeatTimer();
}
else if (type == "/system/heartbeat")
{
HandleHeartBeat(messageObj["content"]);
}
else if (type == "close")
{
WSClient.Disconnect();
ServerHeartbeatCheckTimer.Stop();
// Start the reconnect timer
StartReconnectTimer();
}
else
{
// Check path against Action dictionary
if (ActionDictionary.ContainsKey(type))
{
var action = ActionDictionary[type];
if (action is Action)
{
(action as Action)();
}
else if (action is PressAndHoldAction)
{
var stateString = messageObj["content"]["state"].Value<string>();
// Look for a button press event
if (!string.IsNullOrEmpty(stateString))
{
switch (stateString)
{
case "true":
{
if (!PushedActions.ContainsKey(type))
{
PushedActions.Add(type, new CTimer(o =>
{
(action as PressAndHoldAction)(false);
PushedActions.Remove(type);
}, null, ButtonHeartbeatInterval, ButtonHeartbeatInterval));
}
// Maybe add an else to reset the timer
break;
}
case "held":
{
if (!PushedActions.ContainsKey(type))
{
PushedActions[type].Reset(ButtonHeartbeatInterval, ButtonHeartbeatInterval);
}
return;
}
case "false":
{
if (PushedActions.ContainsKey(type))
{
PushedActions[type].Stop();
PushedActions.Remove(type);
}
break;
}
}
(action as PressAndHoldAction)(stateString == "true");
}
}
else if (action is Action<bool>)
{
var stateString = messageObj["content"]["state"].Value<string>();
if (!string.IsNullOrEmpty(stateString))
{
(action as Action<bool>)(stateString == "true");
}
}
else if (action is Action<ushort>)
{
(action as Action<ushort>)(messageObj["content"]["value"].Value<ushort>());
}
else if (action is Action<string>)
{
(action as Action<string>)(messageObj["content"]["value"].Value<string>());
}
else if (action is Action<SourceSelectMessageContent>)
{
(action as Action<SourceSelectMessageContent>)(messageObj["content"]
.ToObject<SourceSelectMessageContent>());
}
}
else
{
Debug.Console(1, this, "-- Warning: Incoming message has no registered handler");
}
}
}
catch (Exception err)
{
//Debug.Console(1, "SseMessageLengthBeforeFailureCount: {0}", SseMessageLengthBeforeFailureCount);
//SseMessageLengthBeforeFailureCount = 0;
Debug.Console(1, this, "Unable to parse message: {0}", err);
}
}
void TestHttpRequest(string s)
{
{
s = s.Trim();
if (string.IsNullOrEmpty(s))
{
PrintTestHttpRequestUsage();
return;
}
var tokens = s.Split(' ');
if (tokens.Length < 2)
{
CrestronConsole.ConsoleCommandResponse("Too few paramaters\r");
PrintTestHttpRequestUsage();
return;
}
try
{
var url = tokens[1];
if (tokens[0].ToLower() == "get")
{
var resp = new HttpClient().Get(url);
CrestronConsole.ConsoleCommandResponse("RESPONSE:\r{0}\r\r", resp);
}
else if (tokens[0].ToLower() == "post")
{
var resp = new HttpClient().Post(url, new byte[] { });
CrestronConsole.ConsoleCommandResponse("RESPONSE:\r{0}\r\r", resp);
}
else
{
CrestronConsole.ConsoleCommandResponse("Only get or post supported\r");
PrintTestHttpRequestUsage();
}
}
catch (HttpException e)
{
CrestronConsole.ConsoleCommandResponse("Exception in request:\r");
CrestronConsole.ConsoleCommandResponse("Response URL: {0}\r", e.Response.ResponseUrl);
CrestronConsole.ConsoleCommandResponse("Response Error Code: {0}\r", e.Response.Code);
CrestronConsole.ConsoleCommandResponse("Response body: {0}\r", e.Response.ContentString);
}
}
}
void PrintTestHttpRequestUsage()
{
CrestronConsole.ConsoleCommandResponse("Usage: mobilehttprequest:N get/post url\r");
}
}
}

View File

@@ -0,0 +1,694 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.Reflection;
using Crestron.SimplSharpPro.CrestronThread;
using Crestron.SimplSharp.CrestronWebSocketClient;
using Crestron.SimplSharpPro;
using Crestron.SimplSharp.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Room.Cotija;
namespace PepperDash.Essentials
{
public class CotijaSystemController : Device
{
WebSocketClient WSClient;
/// <summary>
/// Prevents post operations from stomping on each other and getting lost
/// </summary>
CEvent PostLockEvent = new CEvent(true, true);
CEvent RegisterLockEvent = new CEvent(true, true);
public CotijaConfig Config { get; private set; }
Dictionary<string, Object> ActionDictionary = new Dictionary<string, Object>(StringComparer.InvariantCultureIgnoreCase);
Dictionary<string, CTimer> PushedActions = new Dictionary<string, CTimer>();
CTimer ServerHeartbeatCheckTimer;
long ServerHeartbeatInterval = 20000;
CTimer ServerReconnectTimer;
long ServerReconnectInterval = 5000;
string SystemUuid;
List<CotijaBridgeBase> RoomBridges = new List<CotijaBridgeBase>();
long ButtonHeartbeatInterval = 1000;
/// <summary>
/// Used for tracking HTTP debugging
/// </summary>
bool HttpDebugEnabled;
/// <summary>
///
/// </summary>
/// <param name="key"></param>
/// <param name="name"></param>
/// <param name="config"></param>
public CotijaSystemController(string key, string name, CotijaConfig config) : base(key, name)
{
Config = config;
Debug.Console(0, this, "Mobile UI controller initializing for server:{0}", config.ServerUrl);
CrestronConsole.AddNewConsoleCommand(AuthorizeSystem,
"mobileauth", "Authorizes system to talk to cotija server", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => ShowInfo(),
"mobileinfo", "Shows information for current mobile control session", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => {
s = s.Trim();
if(!string.IsNullOrEmpty(s))
{
HttpDebugEnabled = (s.Trim() != "0");
}
CrestronConsole.ConsoleCommandResponse("HTTP Debug {0}", HttpDebugEnabled ? "Enabled" : "Disabled");
},
"mobilehttpdebug", "1 enables more verbose HTTP response debugging", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(TestHttpRequest,
"mobilehttprequest", "Tests an HTTP get to URL given", ConsoleAccessLevelEnum.AccessOperator);
}
/// <summary>
/// Adds an action to the dictionary
/// </summary>
/// <param name="key">The path of the API command</param>
/// <param name="action">The action to be triggered by the commmand</param>
public void AddAction(string key, object action)
{
if (!ActionDictionary.ContainsKey(key))
{
ActionDictionary.Add(key, action);
}
else
{
Debug.Console(1, this, "Cannot add action with key '{0}' because key already exists in ActionDictionary.", key);
}
}
/// <summary>
/// Removes an action from the dictionary
/// </summary>
/// <param name="key"></param>
public void RemoveAction(string key)
{
if (ActionDictionary.ContainsKey(key))
ActionDictionary.Remove(key);
}
/// <summary>
///
/// </summary>
/// <param name="bridge"></param>
public void AddBridge(CotijaBridgeBase bridge)
{
RoomBridges.Add(bridge);
var b = bridge as IDelayedConfiguration;
if (b != null)
{
Debug.Console(0, this, "Adding room bridge with delayed configuration");
b.ConfigurationIsReady += new EventHandler<EventArgs>(bridge_ConfigurationIsReady);
}
else
{
Debug.Console(0, this, "Adding room bridge and sending configuration");
RegisterSystemToServer();
}
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void bridge_ConfigurationIsReady(object sender, EventArgs e)
{
Debug.Console(1, this, "Bridge ready. Registering");
// send the configuration object to the server
RegisterSystemToServer();
}
/// <summary>
///
/// </summary>
/// <param name="o"></param>
void ReconnectToServerTimerCallback(object o)
{
RegisterSystemToServer();
}
/// <summary>
/// Verifies system connection with servers
/// </summary>
/// <param name="command"></param>
void AuthorizeSystem(string code)
{
if (string.IsNullOrEmpty(code))
{
CrestronConsole.ConsoleCommandResponse("Please enter a user code to authorize a system");
return;
}
var req = new HttpClientRequest();
string url = string.Format("http://{0}/api/system/grantcode/{1}/{2}", Config.ServerUrl, code, SystemUuid);
Debug.Console(0, this, "Authorizing to: {0}", url);
if (string.IsNullOrEmpty(Config.ServerUrl))
{
CrestronConsole.ConsoleCommandResponse("Config URL address is not set. Check portal configuration");
return;
}
try
{
req.Url.Parse(url);
new HttpClient().DispatchAsync(req, (r, e) =>
{
CheckHttpDebug(r, e);
if (e == HTTP_CALLBACK_ERROR.COMPLETED)
{
if (r.Code == 200)
{
Debug.Console(0, "System authorized, sending config.");
RegisterSystemToServer();
}
else if (r.Code == 404)
{
if (r.ContentString.Contains("codeNotFound"))
{
Debug.Console(0, "Authorization failed, code not found for system UUID {0}", SystemUuid);
}
else if (r.ContentString.Contains("uuidNotFound"))
{
Debug.Console(0, "Authorization failed, uuid {0} not found. Check Essentials configuration is correct",
SystemUuid);
}
}
}
else
Debug.Console(0, this, "Error {0} in authorizing system", e);
});
}
catch (Exception e)
{
Debug.Console(0, this, "Error in authorizing: {0}", e);
}
}
/// <summary>
/// Dumps info in response to console command.
/// </summary>
void ShowInfo()
{
var url = Config != null ? Config.ServerUrl : "No config";
string name;
string code;
if (RoomBridges != null && RoomBridges.Count > 0)
{
name = RoomBridges[0].RoomName;
code = RoomBridges[0].UserCode;
}
else
{
name = "No config";
code = "Not available";
}
var conn = WSClient == null ? "No client" : (WSClient.Connected ? "Yes" : "No");
CrestronConsole.ConsoleCommandResponse(@"Mobile Control Information:
Server address: {0}
System Name: {1}
System UUID: {2}
System User code: {3}
Connected?: {4}", url, name, SystemUuid,
code, conn);
}
/// <summary>
/// Registers the room with the server
/// </summary>
/// <param name="url">URL of the server, including the port number, if not 80. Format: "serverUrlOrIp:port"</param>
void RegisterSystemToServer()
{
var ready = RegisterLockEvent.Wait(20000);
if (!ready)
{
Debug.Console(1, this, "RegisterSystemToServer failed to enter after 20 seconds. Ignoring");
return;
}
RegisterLockEvent.Reset();
try
{
var confObject = ConfigReader.ConfigObject;
confObject.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name;
var version = Assembly.GetExecutingAssembly().GetName().Version;
confObject.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
confObject.Info.RuntimeInfo.OsVersion = Crestron.SimplSharp.CrestronEnvironment.OSVersion.Firmware;
string postBody = JsonConvert.SerializeObject(confObject);
SystemUuid = confObject.SystemUuid;
if (string.IsNullOrEmpty(postBody))
{
Debug.Console(1, this, "ERROR: Config body is empty. Cannot register with server.");
}
else
{
var regClient = new HttpClient();
regClient.Verbose = true;
regClient.KeepAlive = true;
string url = string.Format("http://{0}/api/system/join/{1}", Config.ServerUrl, SystemUuid);
Debug.Console(1, this, "Joining server at {0}", url);
HttpClientRequest request = new HttpClientRequest();
request.Url.Parse(url);
request.RequestType = RequestType.Post;
request.Header.SetHeaderValue("Content-Type", "application/json");
request.ContentString = postBody;
var err = regClient.DispatchAsync(request, RegistrationConnectionCallback);
}
}
catch (Exception e)
{
Debug.Console(0, this, "ERROR: Initilizing Room: {0}", e);
RegisterLockEvent.Set();
StartReconnectTimer();
}
}
/// <summary>
/// Sends a message to the server from a room
/// </summary>
/// <param name="room">room from which the message originates</param>
/// <param name="o">object to be serialized and sent in post body</param>
public void SendMessageToServer(JObject o)
{
if (WSClient != null && WSClient.Connected)
{
string message = JsonConvert.SerializeObject(o, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
Debug.Console(1, this, "Message TX: {0}", message);
var messageBytes = System.Text.Encoding.UTF8.GetBytes(message);
WSClient.Send(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME);
//WSClient.SendAsync(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME);
}
<<<<<<< HEAD
=======
>>>>>>> feature/ecs-684
}
/// <summary>
/// Disconnects the SSE Client and stops the heartbeat timer
/// </summary>
/// <param name="command"></param>
void DisconnectStreamClient(string command)
{
//if(SseClient != null)
// SseClient.Disconnect();
if (WSClient != null && WSClient.Connected)
WSClient.Disconnect();
if (ServerHeartbeatCheckTimer != null)
{
ServerHeartbeatCheckTimer.Stop();
ServerHeartbeatCheckTimer = null;
}
}
/// <summary>
/// The callback that fires when we get a response from our registration attempt
/// </summary>
/// <param name="resp"></param>
/// <param name="err"></param>
void RegistrationConnectionCallback(HttpClientResponse resp, HTTP_CALLBACK_ERROR err)
{
CheckHttpDebug(resp, err);
Debug.Console(1, this, "RegistrationConnectionCallback: {0}", err);
try
{
if (resp != null && resp.Code == 200)
{
if(ServerReconnectTimer != null)
{
ServerReconnectTimer.Stop();
ServerReconnectTimer = null;
}
// Success here!
ConnectStreamClient();
}
else
{
if (resp != null)
Debug.Console(1, this, "Response from server: {0}\n{1}", resp.Code, err);
else
{
Debug.Console(1, this, "Null response received from server.");
}
StartReconnectTimer();
}
}
catch (Exception e)
{
Debug.Console(1, this, "Error Initializing Stream Client: {0}", e);
StartReconnectTimer();
}
RegisterLockEvent.Set();
}
/// <summary>
/// Executes when we don't get a heartbeat message in time. Triggers reconnect.
/// </summary>
/// <param name="o">For CTimer callback. Not used</param>
void HeartbeatExpiredTimerCallback(object o)
{
Debug.Console(1, this, "Heartbeat Timer Expired.");
if (ServerHeartbeatCheckTimer != null)
{
ServerHeartbeatCheckTimer.Stop();
ServerHeartbeatCheckTimer = null;
}
StartReconnectTimer();
}
/// <summary>
///
/// </summary>
/// <param name="dueTime"></param>
/// <param name="repeatTime"></param>
void StartReconnectTimer()
{
// Start the reconnect timer
if (ServerReconnectTimer == null)
{
ServerReconnectTimer = new CTimer(ReconnectToServerTimerCallback, null, ServerReconnectInterval, ServerReconnectInterval);
Debug.Console(1, this, "Reconnect Timer Started.");
}
ServerReconnectTimer.Reset(ServerReconnectInterval, ServerReconnectInterval);
}
/// <summary>
///
/// </summary>
/// <param name="dueTime"></param>
/// <param name="repeatTime"></param>
void ResetOrStartHearbeatTimer()
{
if (ServerHeartbeatCheckTimer == null)
{
ServerHeartbeatCheckTimer = new CTimer(HeartbeatExpiredTimerCallback, null, ServerHeartbeatInterval, ServerHeartbeatInterval);
Debug.Console(1, this, "Heartbeat Timer Started.");
}
ServerHeartbeatCheckTimer.Reset(ServerHeartbeatInterval, ServerHeartbeatInterval);
}
/// <summary>
/// Connects the SSE Client
/// </summary>
/// <param name="o"></param>
void ConnectStreamClient()
{
Debug.Console(0, this, "Initializing Stream client to server.");
if (WSClient == null)
{
WSClient = new WebSocketClient();
}
WSClient.URL = string.Format("wss://{0}/system/join/{1}", Config.ServerUrl, this.SystemUuid);
WSClient.Connect();
Debug.Console(0, this, "Websocket connected");
WSClient.ReceiveCallBack = WebsocketReceiveCallback;
//WSClient.SendCallBack = WebsocketSendCallback;
WSClient.ReceiveAsync();
}
/// <summary>
/// Resets reconnect timer and updates usercode
/// </summary>
/// <param name="content"></param>
void HandleHeartBeat(JToken content)
{
var code = content["userCode"];
if(code != null)
{
foreach (var b in RoomBridges)
{
b.SetUserCode(code.Value<string>());
}
}
ResetOrStartHearbeatTimer();
}
/// <summary>
/// Outputs debug info when enabled
/// </summary>
/// <param name="req"></param>
/// <param name="r"></param>
/// <param name="e"></param>
void CheckHttpDebug(HttpClientResponse r, HTTP_CALLBACK_ERROR e)
{
if (HttpDebugEnabled)
{
Debug.Console(0, this, "------ Begin HTTP Debug ---------------------------------------");
Debug.Console(0, this, "HTTP Response URL: {0}", r.ResponseUrl.ToString());
Debug.Console(0, this, "HTTP Response 'error' {0}", e);
Debug.Console(0, this, "HTTP Response code: {0}", r.Code);
Debug.Console(0, this, "HTTP Response content: \r{0}", r.ContentString);
Debug.Console(0, this, "------ End HTTP Debug -----------------------------------------");
}
}
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <param name="opcode"></param>
/// <param name="err"></param>
int WebsocketReceiveCallback(byte[] data, uint length, WebSocketClient.WEBSOCKET_PACKET_TYPES opcode,
WebSocketClient.WEBSOCKET_RESULT_CODES err)
{
var rx = System.Text.Encoding.UTF8.GetString(data, 0, (int)length);
if(rx.Length > 0)
ParseStreamRx(rx);
WSClient.ReceiveAsync();
return 1;
}
/// <summary>
/// Callback to catch possible errors in sending via the websocket
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
int WebsocketSendCallback(Crestron.SimplSharp.CrestronWebSocketClient.WebSocketClient.WEBSOCKET_RESULT_CODES result)
{
Debug.Console(1, this, "SendCallback result: {0}", result);
return 1;
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void ParseStreamRx(string message)
{
if(string.IsNullOrEmpty(message))
return;
Debug.Console(1, this, "Message RX: '{0}'", message);
try
{
var messageObj = JObject.Parse(message);
var type = messageObj["type"].Value<string>();
if (type == "hello")
{
ResetOrStartHearbeatTimer();
}
else if (type == "/system/heartbeat")
{
HandleHeartBeat(messageObj["content"]);
}
else if (type == "close")
{
WSClient.Disconnect();
ServerHeartbeatCheckTimer.Stop();
// Start the reconnect timer
StartReconnectTimer();
}
else
{
// Check path against Action dictionary
if (ActionDictionary.ContainsKey(type))
{
var action = ActionDictionary[type];
if (action is Action)
{
(action as Action)();
}
else if (action is PressAndHoldAction)
{
var stateString = messageObj["content"]["state"].Value<string>();
// Look for a button press event
if (!string.IsNullOrEmpty(stateString))
{
switch (stateString)
{
case "true":
{
if (!PushedActions.ContainsKey(type))
{
PushedActions.Add(type, new CTimer(o =>
{
(action as PressAndHoldAction)(false);
PushedActions.Remove(type);
}, null, ButtonHeartbeatInterval, ButtonHeartbeatInterval));
}
// Maybe add an else to reset the timer
break;
}
case "held":
{
if (!PushedActions.ContainsKey(type))
{
PushedActions[type].Reset(ButtonHeartbeatInterval, ButtonHeartbeatInterval);
}
return;
}
case "false":
{
if (PushedActions.ContainsKey(type))
{
PushedActions[type].Stop();
PushedActions.Remove(type);
}
break;
}
}
(action as PressAndHoldAction)(stateString == "true");
}
}
else if (action is Action<bool>)
{
var stateString = messageObj["content"]["state"].Value<string>();
if (!string.IsNullOrEmpty(stateString))
{
(action as Action<bool>)(stateString == "true");
}
}
else if (action is Action<ushort>)
{
(action as Action<ushort>)(messageObj["content"]["value"].Value<ushort>());
}
else if (action is Action<string>)
{
(action as Action<string>)(messageObj["content"]["value"].Value<string>());
}
else if (action is Action<SourceSelectMessageContent>)
{
(action as Action<SourceSelectMessageContent>)(messageObj["content"]
.ToObject<SourceSelectMessageContent>());
}
}
else
{
Debug.Console(1, this, "-- Warning: Incoming message has no registered handler");
}
}
}
catch (Exception err)
{
//Debug.Console(1, "SseMessageLengthBeforeFailureCount: {0}", SseMessageLengthBeforeFailureCount);
//SseMessageLengthBeforeFailureCount = 0;
Debug.Console(1, this, "Unable to parse message: {0}", err);
}
}
void TestHttpRequest(string s)
{
{
s = s.Trim();
if (string.IsNullOrEmpty(s))
{
PrintTestHttpRequestUsage();
return;
}
var tokens = s.Split(' ');
if (tokens.Length < 2)
{
CrestronConsole.ConsoleCommandResponse("Too few paramaters\r");
PrintTestHttpRequestUsage();
return;
}
try
{
var url = tokens[1];
if (tokens[0].ToLower() == "get")
{
var resp = new HttpClient().Get(url);
CrestronConsole.ConsoleCommandResponse("RESPONSE:\r{0}\r\r", resp);
}
else if (tokens[0].ToLower() == "post")
{
var resp = new HttpClient().Post(url, new byte[] { });
CrestronConsole.ConsoleCommandResponse("RESPONSE:\r{0}\r\r", resp);
}
else
{
CrestronConsole.ConsoleCommandResponse("Only get or post supported\r");
PrintTestHttpRequestUsage();
}
}
catch (HttpException e)
{
CrestronConsole.ConsoleCommandResponse("Exception in request:\r");
CrestronConsole.ConsoleCommandResponse("Response URL: {0}\r", e.Response.ResponseUrl);
CrestronConsole.ConsoleCommandResponse("Response Error Code: {0}\r", e.Response.Code);
CrestronConsole.ConsoleCommandResponse("Response body: {0}\r", e.Response.ContentString);
}
}
}
void PrintTestHttpRequestUsage()
{
CrestronConsole.ConsoleCommandResponse("Usage: mobilehttprequest:N get/post url\r");
}
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Room.Cotija
{
public interface IDelayedConfiguration
{
event EventHandler<EventArgs> ConfigurationIsReady;
}
}

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials
{
/// <summary>
///
/// </summary>
public abstract class CotijaBridgeBase: Device
{
public CotijaSystemController Parent { get; private set; }
public string UserCode { get; private set; }
public abstract string RoomName { get; }
public CotijaBridgeBase(string key, string name)
: base(key, name)
{
}
/// <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(CotijaSystemController parent)
{
Parent = parent;
}
/// <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>
/// Empty method in base class. Override this to add functionality
/// when code changes
/// </summary>
protected virtual void UserCodeChange()
{
}
}
}

View File

@@ -0,0 +1,607 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.EthernetCommunication;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Room.Config;
namespace PepperDash.Essentials.Room.Cotija
{
public class CotijaDdvc01RoomBridge : CotijaBridgeBase, IDelayedConfiguration
{
public class BoolJoin
{
/// <summary>
/// 301
/// </summary>
public const uint RoomIsOn = 301;
/// <summary>
/// 51
/// </summary>
public const uint ActivitySharePress = 51;
/// <summary>
/// 52
/// </summary>
public const uint ActivityPhoneCallPress = 52;
/// <summary>
/// 53
/// </summary>
public const uint ActivityVideoCallPress = 53;
/// <summary>
/// 1
/// </summary>
public const uint MasterVolumeIsMuted = 1;
/// <summary>
/// 1
/// </summary>
public const uint MasterVolumeMuteToggle = 1;
/// <summary>
/// 61
/// </summary>
public const uint ShutdownCancel = 61;
/// <summary>
/// 62
/// </summary>
public const uint ShutdownEnd = 62;
/// <summary>
/// 63
/// </summary>
public const uint ShutdownStart = 63;
/// <summary>
/// 72
/// </summary>
public const uint SourceHasChanged = 72;
/// <summary>
/// 501
/// </summary>
public const uint ConfigIsReady = 501;
}
public class UshortJoin
{
/// <summary>
/// 1
/// </summary>
public const uint MasterVolumeLevel = 1;
/// <summary>
/// 61
/// </summary>
public const uint ShutdownPromptDuration = 61;
}
public class StringJoin
{
/// <summary>
/// 71
/// </summary>
public const uint SelectedSourceKey = 71;
/// <summary>
/// 501
/// </summary>
public const uint ConfigRoomName = 501;
/// <summary>
/// 502
/// </summary>
public const uint ConfigHelpMessage = 502;
/// <summary>
/// 503
/// </summary>
public const uint ConfigHelpNumber = 503;
/// <summary>
/// 504
/// </summary>
public const uint ConfigRoomPhoneNumber = 504;
/// <summary>
/// 505
/// </summary>
public const uint ConfigRoomURI = 505;
/// <summary>
/// 401
/// </summary>
public const uint UserCodeToSystem = 401;
/// <summary>
/// 402
/// </summary>
public const uint ServerUrl = 402;
}
/// <summary>
/// Fires when config is ready to go
/// </summary>
public event EventHandler<EventArgs> ConfigurationIsReady;
public ThreeSeriesTcpIpEthernetIntersystemCommunications EISC { get; private set; }
/// <summary>
///
/// </summary>
public bool ConfigIsLoaded { get; private set; }
public override string RoomName
{
get {
var name = EISC.StringOutput[StringJoin.ConfigRoomName].StringValue;
return string.IsNullOrEmpty(name) ? "Not Loaded" : name;
}
}
CotijaDdvc01DeviceBridge SourceBridge;
/// <summary>
///
/// </summary>
/// <param name="key"></param>
/// <param name="name"></param>
/// <param name="ipId"></param>
public CotijaDdvc01RoomBridge(string key, string name, uint ipId)
: base(key, name)
{
try
{
EISC = new ThreeSeriesTcpIpEthernetIntersystemCommunications(ipId, "127.0.0.2", Global.ControlSystem);
var reg = EISC.Register();
if (reg != Crestron.SimplSharpPro.eDeviceRegistrationUnRegistrationResponse.Success)
Debug.Console(0, this, "Cannot connect EISC at IPID {0}: \r{1}", ipId, reg);
SourceBridge = new CotijaDdvc01DeviceBridge(key + "-sourceBridge", "DDVC01 source bridge", EISC);
DeviceManager.AddDevice(SourceBridge);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Finish wiring up everything after all devices are created. The base class will hunt down the related
/// parent controller and link them up.
/// </summary>
/// <returns></returns>
public override bool CustomActivate()
{
Debug.Console(0, this, "Final activation. Setting up actions and feedbacks");
SetupFunctions();
SetupFeedbacks();
EISC.SigChange += EISC_SigChange;
EISC.OnlineStatusChange += (o, a) =>
{
Debug.Console(1, this, "DDVC EISC online={0}. Config is ready={1}", a.DeviceOnLine, EISC.BooleanOutput[BoolJoin.ConfigIsReady].BoolValue);
if (a.DeviceOnLine && EISC.BooleanOutput[BoolJoin.ConfigIsReady].BoolValue)
LoadConfigValues();
};
// load config if it's already there
if (EISC.IsOnline && EISC.BooleanOutput[BoolJoin.ConfigIsReady].BoolValue) // || EISC.BooleanInput[BoolJoin.ConfigIsReady].BoolValue)
LoadConfigValues();
CrestronConsole.AddNewConsoleCommand(s =>
{
for (uint i = 1; i < 1000; i++)
{
if (s.ToLower().Equals("b"))
{
CrestronConsole.ConsoleCommandResponse("D{0,6} {1} - ", i, EISC.BooleanOutput[i].BoolValue);
}
else if (s.ToLower().Equals("u"))
{
CrestronConsole.ConsoleCommandResponse("U{0,6} {1,8} - ", i, EISC.UShortOutput[i].UShortValue);
}
else if (s.ToLower().Equals("s"))
{
var val = EISC.StringOutput[i].StringValue;
if(!string.IsNullOrEmpty(val))
CrestronConsole.ConsoleCommandResponse("S{0,6} {1}\r", i, EISC.StringOutput[i].StringValue);
}
}
}, "mobilebridgedump", "Dumps DDVC01 bridge EISC data b,u,s", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => LoadConfigValues(), "loadddvc", "", ConsoleAccessLevelEnum.AccessOperator);
return base.CustomActivate();
}
/// <summary>
/// Setup the actions to take place on various incoming API calls
/// </summary>
void SetupFunctions()
{
Parent.AddAction(@"/room/room1/status", new Action(SendFullStatus));
Parent.AddAction(@"/room/room1/source", new Action<SourceSelectMessageContent>(c =>
{
EISC.SetString(StringJoin.SelectedSourceKey, c.SourceListItem);
EISC.PulseBool(BoolJoin.SourceHasChanged);
}));
#warning CHANGE to activityshare. Perhaps
Parent.AddAction(@"/room/room1/defaultsource", new Action(() =>
EISC.PulseBool(BoolJoin.ActivitySharePress)));
Parent.AddAction(@"/room/room1/masterVolumeLevel", new Action<ushort>(u =>
EISC.SetUshort(UshortJoin.MasterVolumeLevel, u)));
Parent.AddAction(@"/room/room1/masterVolumeMuteToggle", new Action(() =>
EISC.PulseBool(BoolJoin.MasterVolumeIsMuted)));
Parent.AddAction(@"/room/room1/shutdownStart", new Action(() =>
EISC.PulseBool(BoolJoin.ShutdownStart)));
Parent.AddAction(@"/room/room1/shutdownEnd", new Action(() =>
EISC.PulseBool(BoolJoin.ShutdownEnd)));
Parent.AddAction(@"/room/room1/shutdownCancel", new Action(() =>
EISC.PulseBool(BoolJoin.ShutdownCancel)));
}
/// <summary>
/// Links feedbacks to whatever is gonna happen!
/// </summary>
void SetupFeedbacks()
{
// Power
EISC.SetBoolSigAction(BoolJoin.RoomIsOn, b =>
PostStatusMessage(new
{
isOn = b
}));
// Source change things
EISC.SetSigTrueAction(BoolJoin.SourceHasChanged, () =>
PostStatusMessage(new
{
selectedSourceKey = EISC.StringOutput[StringJoin.SelectedSourceKey].StringValue
}));
// Volume things
EISC.SetUShortSigAction(UshortJoin.MasterVolumeLevel, u =>
PostStatusMessage(new
{
masterVolumeLevel = u
}));
EISC.SetBoolSigAction(BoolJoin.MasterVolumeIsMuted, b =>
PostStatusMessage(new
{
masterVolumeMuteState = b
}));
// shutdown things
EISC.SetSigTrueAction(BoolJoin.ShutdownCancel, new Action(() =>
PostMessage("/room/shutdown/", new
{
state = "wasCancelled"
})));
EISC.SetSigTrueAction(BoolJoin.ShutdownEnd, new Action(() =>
PostMessage("/room/shutdown/", new
{
state = "hasFinished"
})));
EISC.SetSigTrueAction(BoolJoin.ShutdownStart, new Action(() =>
PostMessage("/room/shutdown/", new
{
state = "hasStarted",
duration = EISC.UShortOutput[UshortJoin.ShutdownPromptDuration].UShortValue
})));
// Config things
EISC.SetSigTrueAction(BoolJoin.ConfigIsReady, LoadConfigValues);
}
/// <summary>
/// Reads in config values when the Simpl program is ready
/// </summary>
void LoadConfigValues()
{
Debug.Console(1, this, "Loading configuration from DDVC01 EISC bridge");
ConfigIsLoaded = false;
var co = ConfigReader.ConfigObject;
//Room
if (co.Rooms == null)
co.Rooms = new List<EssentialsRoomConfig>();
var rm = new EssentialsRoomConfig();
if (co.Rooms.Count == 0)
{
Debug.Console(0, this, "Adding room to config");
co.Rooms.Add(rm);
}
rm.Name = EISC.StringOutput[501].StringValue;
rm.Key = "room1";
rm.Type = "ddvc01";
DDVC01RoomPropertiesConfig rmProps;
if (rm.Properties == null)
rmProps = new DDVC01RoomPropertiesConfig();
else
rmProps = JsonConvert.DeserializeObject<DDVC01RoomPropertiesConfig>(rm.Properties.ToString());
rmProps.Help = new EssentialsHelpPropertiesConfig();
rmProps.Help.CallButtonText = EISC.StringOutput[503].StringValue;
rmProps.Help.Message = EISC.StringOutput[502].StringValue;
rmProps.Environment = new EssentialsEnvironmentPropertiesConfig(); // enabled defaults to false
rmProps.RoomPhoneNumber = EISC.StringOutput[504].StringValue;
rmProps.RoomURI = EISC.StringOutput[505].StringValue;
rmProps.SpeedDials = new List<DDVC01SpeedDial>();
// add speed dials as long as there are more - up to 4
for (uint i = 512; i <= 519; i = i + 2)
{
var num = EISC.StringOutput[i].StringValue;
if (string.IsNullOrEmpty(num))
break;
var name = EISC.StringOutput[i + 1].StringValue;
rmProps.SpeedDials.Add(new DDVC01SpeedDial { Number = num, Name = name});
}
// volume control names
var volCount = EISC.UShortOutput[701].UShortValue;
// use Volumes object or?
rmProps.VolumeSliderNames = new List<string>();
for(uint i = 701; i <= 700 + volCount; i++)
{
rmProps.VolumeSliderNames.Add(EISC.StringInput[i].StringValue);
}
// There should be cotija devices in here, I think...
if(co.Devices == null)
co.Devices = new List<DeviceConfig>();
// clear out previous DDVC devices
co.Devices.RemoveAll(d => d.Key.StartsWith("source-", StringComparison.OrdinalIgnoreCase));
rmProps.SourceListKey = "default";
rm.Properties = JToken.FromObject(rmProps);
// Source list! This might be brutal!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
var groupMap = GetSourceGroupDictionary();
co.SourceLists = new Dictionary<string,Dictionary<string,SourceListItem>>();
var newSl = new Dictionary<string, SourceListItem>();
// add sources...
for (uint i = 0; i<= 19; i++)
{
var name = EISC.StringOutput[601 + i].StringValue;
if(string.IsNullOrEmpty(name))
break;
var icon = EISC.StringOutput[651 + i].StringValue;
var key = EISC.StringOutput[671 + i].StringValue;
var type = EISC.StringOutput[701 + i].StringValue;
Debug.Console(0, this, "Adding source {0} '{1}'", key, name);
var newSLI = new SourceListItem{
Icon = icon,
Name = name,
Order = (int)i + 1,
SourceKey = key,
};
newSl.Add(key, newSLI);
string group = "genericsource";
if (groupMap.ContainsKey(type))
{
group = groupMap[type];
}
// add dev to devices list
var devConf = new DeviceConfig {
Group = group,
Key = key,
Name = name,
Type = type
};
co.Devices.Add(devConf);
}
co.SourceLists.Add("default", newSl);
Debug.Console(0, this, "******* CONFIG FROM DDVC: \r{0}", JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented));
var handler = ConfigurationIsReady;
if (handler != null)
{
handler(this, new EventArgs());
}
ConfigIsLoaded = true;
}
void SendFullStatus()
{
if (ConfigIsLoaded)
{
PostStatusMessage(new
{
isOn = EISC.BooleanOutput[BoolJoin.RoomIsOn].BoolValue,
selectedSourceKey = EISC.StringOutput[StringJoin.SelectedSourceKey].StringValue,
masterVolumeLevel = EISC.UShortOutput[UshortJoin.MasterVolumeLevel].UShortValue,
masterVolumeMuteState = EISC.BooleanOutput[BoolJoin.MasterVolumeIsMuted].BoolValue
});
}
else
{
PostStatusMessage(new
{
error = "systemNotReady"
});
}
}
/// <summary>
/// Helper for posting status message
/// </summary>
/// <param name="contentObject">The contents of the content object</param>
void PostStatusMessage(object contentObject)
{
Parent.SendMessageToServer(JObject.FromObject(new
{
type = "/room/status/",
content = contentObject
}));
}
/// <summary>
///
/// </summary>
/// <param name="messageType"></param>
/// <param name="contentObject"></param>
void PostMessage(string messageType, object contentObject)
{
Parent.SendMessageToServer(JObject.FromObject(new
{
type = messageType,
content = contentObject
}));
}
/// <summary>
///
/// </summary>
/// <param name="currentDevice"></param>
/// <param name="args"></param>
void EISC_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args)
{
if (Debug.Level >= 1)
Debug.Console(1, this, "DDVC EISC change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);
var uo = args.Sig.UserObject;
if (uo is Action<bool>)
(uo as Action<bool>)(args.Sig.BoolValue);
else if (uo is Action<ushort>)
(uo as Action<ushort>)(args.Sig.UShortValue);
else if (uo is Action<string>)
(uo as Action<string>)(args.Sig.StringValue);
}
/// <summary>
/// Returns the mapping of types to groups, for setting up devices.
/// </summary>
/// <returns></returns>
Dictionary<string, string> GetSourceGroupDictionary()
{
//type, group
var d = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "laptop", "pc" },
{ "wireless", "genericsource" },
{ "iptv", "settopbox" }
};
return d;
}
/// <summary>
/// updates the usercode from server
/// </summary>
protected override void UserCodeChange()
{
Debug.Console(1, this, "Server user code changed: {0}", UserCode);
EISC.StringInput[StringJoin.UserCodeToSystem].StringValue = UserCode;
EISC.StringInput[StringJoin.ServerUrl].StringValue = Parent.Config.ClientAppUrl;
}
/// <summary>
///
/// </summary>
/// <param name="oldKey"></param>
/// <param name="newKey"></param>
void SourceChange(string oldKey, string newKey)
{
/* Example message
* {
"type":"/room/status",
"content": {
"selectedSourceKey": "off",
}
}
*/
//if (type == ChangeType.WillChange)
//{
// // Disconnect from previous source
// if (info != null)
// {
// var previousDev = info.SourceDevice;
// // device type interfaces
// if (previousDev is ISetTopBoxControls)
// (previousDev as ISetTopBoxControls).UnlinkActions(Parent);
// // common interfaces
// if (previousDev is IChannel)
// (previousDev as IChannel).UnlinkActions(Parent);
// if (previousDev is IColor)
// (previousDev as IColor).UnlinkActions(Parent);
// if (previousDev is IDPad)
// (previousDev as IDPad).UnlinkActions(Parent);
// if (previousDev is IDvr)
// (previousDev as IDvr).UnlinkActions(Parent);
// if (previousDev is INumericKeypad)
// (previousDev as INumericKeypad).UnlinkActions(Parent);
// if (previousDev is IPower)
// (previousDev as IPower).UnlinkActions(Parent);
// if (previousDev is ITransport)
// (previousDev as ITransport).UnlinkActions(Parent);
// }
// var huddleRoom = room as EssentialsHuddleSpaceRoom;
// JObject roomStatus = new JObject();
// roomStatus.Add("selectedSourceKey", huddleRoom.CurrentSourceInfoKey);
// JObject message = new JObject();
// message.Add("type", "/room/status/");
// message.Add("content", roomStatus);
// Parent.PostToServer(message);
//}
//else
//{
// if (info != null)
// {
// var dev = info.SourceDevice;
// if (dev is ISetTopBoxControls)
// (dev as ISetTopBoxControls).LinkActions(Parent);
// if (dev is IChannel)
// (dev as IChannel).LinkActions(Parent);
// if (dev is IColor)
// (dev as IColor).LinkActions(Parent);
// if (dev is IDPad)
// (dev as IDPad).LinkActions(Parent);
// if (dev is IDvr)
// (dev as IDvr).LinkActions(Parent);
// if (dev is INumericKeypad)
// (dev as INumericKeypad).LinkActions(Parent);
// if (dev is IPower)
// (dev as IPower).LinkActions(Parent);
// if (dev is ITransport)
// (dev as ITransport).LinkActions(Parent);
// }
//}
}
}
}

View File

@@ -1,256 +1,379 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Room.Cotija;
namespace PepperDash.Essentials
{
public class CotijaEssentialsHuddleSpaceRoomBridge
{
CotijaSystemController Parent;
public EssentialsHuddleSpaceRoom Room { get; private set; }
public CotijaEssentialsHuddleSpaceRoomBridge(CotijaSystemController parent, EssentialsHuddleSpaceRoom room)
{
Parent = parent;
Room = room;
// Source Changes and room off
Parent.AddAction(string.Format(@"/room/{0}/status",Room.Key), new Action(() => Room_RoomFullStatus(Room)));
Parent.AddAction(string.Format(@"/room/{0}/source", Room.Key), new Action<SourceSelectMessageContent>(c => room.RunRouteAction(c.SourceSelect)));
Parent.AddAction(string.Format(@"/room/{0}/event/masterVolumeUpBtn", Room.Key), new PressAndHoldAction(b => room.CurrentVolumeControls.VolumeUp(b)));
Parent.AddAction(string.Format(@"/room/{0}/event/masterVolumeDownBtn", Room.Key), new PressAndHoldAction(b => room.CurrentVolumeControls.VolumeDown(b)));
Parent.AddAction(string.Format(@"/room/{0}/event/muteToggle", Room.Key), new Action(() => room.CurrentVolumeControls.MuteToggle()));
Room.CurrentSingleSourceChange += new SourceInfoChangeHandler(Room_CurrentSingleSourceChange);
Room.CurrentVolumeDeviceChange += new EventHandler<VolumeDeviceChangeEventArgs>(Room_CurrentVolumeDeviceChange);
Room.OnFeedback.OutputChange += new EventHandler<EventArgs>(OnFeedback_OutputChange);
// Registers for initial volume events, if possible
var currentVolumeDevice = Room.CurrentVolumeControls;
if (currentVolumeDevice != null)
{
if (currentVolumeDevice is IBasicVolumeWithFeedback)
{
var newDev = currentVolumeDevice as IBasicVolumeWithFeedback;
newDev.MuteFeedback.OutputChange += new EventHandler<EventArgs>(VolumeLevelFeedback_OutputChange);
newDev.VolumeLevelFeedback.OutputChange += new EventHandler<EventArgs>(VolumeLevelFeedback_OutputChange);
}
}
}
void OnFeedback_OutputChange(object sender, EventArgs e)
{
/* Example message
* {
"type":"/room/status",
"content": {
"isOn": false
}
}
*/
JObject roomStatus = new JObject();
roomStatus.Add("isOn", (sender as BoolFeedback).BoolValue);
JObject message = new JObject();
message.Add("type", "/room/status/");
message.Add("content", roomStatus);
Parent.PostToServer(Room, message);
}
void Room_CurrentVolumeDeviceChange(object sender, VolumeDeviceChangeEventArgs e)
{
if (e.OldDev is IBasicVolumeWithFeedback)
{
var oldDev = e.OldDev as IBasicVolumeWithFeedback;
oldDev.MuteFeedback.OutputChange -= VolumeLevelFeedback_OutputChange;
oldDev.VolumeLevelFeedback.OutputChange -= VolumeLevelFeedback_OutputChange;
}
if (e.NewDev is IBasicVolumeWithFeedback)
{
var newDev = e.NewDev as IBasicVolumeWithFeedback;
newDev.MuteFeedback.OutputChange += new EventHandler<EventArgs>(VolumeLevelFeedback_OutputChange);
newDev.VolumeLevelFeedback.OutputChange += new EventHandler<EventArgs>(VolumeLevelFeedback_OutputChange);
}
}
void VolumeLevelFeedback_OutputChange(object sender, EventArgs e)
{
/* Example message
* {
"type":"/room/status",
"content": {
"masterVolumeLevel": 12345,
"masterVolumeMuteState": false
}
}
*/
var huddleRoom = Room as EssentialsHuddleSpaceRoom;
if(huddleRoom.CurrentVolumeControls is IBasicVolumeWithFeedback)
{
JObject roomStatus = new JObject();
if (huddleRoom.CurrentVolumeControls is IBasicVolumeWithFeedback)
{
var currentVolumeConstrols = huddleRoom.CurrentVolumeControls as IBasicVolumeWithFeedback;
roomStatus.Add("masterVolumeLevel", currentVolumeConstrols.VolumeLevelFeedback.IntValue);
roomStatus.Add("masterVolumeMuteState", currentVolumeConstrols.MuteFeedback.BoolValue);
}
JObject message = new JObject();
message.Add("type", "/room/status/");
message.Add("content", roomStatus);
Parent.PostToServer(Room, message);
}
}
void Room_CurrentSingleSourceChange(EssentialsRoomBase room, PepperDash.Essentials.Core.SourceListItem info, ChangeType type)
{
/* Example message
* {
"type":"/room/status",
"content": {
"selectedSourceKey": "off",
}
}
*/
if (type == ChangeType.WillChange)
{
// Disconnect from previous source
if (info != null)
{
var previousDev = info.SourceDevice;
// device type interfaces
if (previousDev is ISetTopBoxControls)
(previousDev as ISetTopBoxControls).UnlinkActions(Parent);
// common interfaces
if (previousDev is IChannel)
(previousDev as IChannel).UnlinkActions(Parent);
if (previousDev is IColor)
(previousDev as IColor).UnlinkActions(Parent);
if (previousDev is IDPad)
(previousDev as IDPad).UnlinkActions(Parent);
if (previousDev is IDvr)
(previousDev as IDvr).UnlinkActions(Parent);
if (previousDev is INumericKeypad)
(previousDev as INumericKeypad).UnlinkActions(Parent);
if (previousDev is IPower)
(previousDev as IPower).UnlinkActions(Parent);
if (previousDev is ITransport)
(previousDev as ITransport).UnlinkActions(Parent);
}
JObject roomStatus = new JObject();
var huddleRoom = room as EssentialsHuddleSpaceRoom;
roomStatus.Add("selectedSourceKey", huddleRoom.CurrentSourceInfoKey);
JObject message = new JObject();
message.Add("type", "/room/status/");
message.Add("content", roomStatus);
Parent.PostToServer(Room, message);
}
else
{
if (info != null)
{
var dev = info.SourceDevice;
if (dev is ISetTopBoxControls)
(dev as ISetTopBoxControls).LinkActions(Parent);
if (dev is IChannel)
(dev as IChannel).LinkActions(Parent);
if (dev is IColor)
(dev as IColor).LinkActions(Parent);
if (dev is IDPad)
(dev as IDPad).LinkActions(Parent);
if (dev is IDvr)
(dev as IDvr).LinkActions(Parent);
if (dev is INumericKeypad)
(dev as INumericKeypad).LinkActions(Parent);
if (dev is IPower)
(dev as IPower).LinkActions(Parent);
if (dev is ITransport)
(dev as ITransport).LinkActions(Parent);
}
}
}
/// <summary>
/// Posts the full status of the room to the server
/// </summary>
/// <param name="room"></param>
void Room_RoomFullStatus(EssentialsRoomBase room)
{
/* Example message
* {
"type":"/room/status",
"content": {
"selectedSourceKey": "off",
"isOn": false,
"masterVolumeLevel": 50,
"masterVolumeMuteState": false
}
}
*/
JObject roomStatus = new JObject();
var huddleRoom = room as EssentialsHuddleSpaceRoom;
roomStatus.Add("isOn", huddleRoom.OnFeedback.BoolValue);
roomStatus.Add("selectedSourceKey", huddleRoom.CurrentSourceInfoKey);
if(huddleRoom.CurrentVolumeControls is IBasicVolumeWithFeedback)
{
var currentVolumeConstrols = huddleRoom.CurrentVolumeControls as IBasicVolumeWithFeedback;
roomStatus.Add("masterVolumeLevel", currentVolumeConstrols.VolumeLevelFeedback.IntValue);
roomStatus.Add("masterVolumeMuteState", currentVolumeConstrols.MuteFeedback.BoolValue);
}
JObject message = new JObject();
message.Add("type", "/room/status/");
message.Add("content", roomStatus);
Parent.PostToServer(Room, message);
}
}
public class SourceSelectMessageContent
{
public string Destination { get; set; }
public string SourceSelect { get; set; }
}
public delegate void PressAndHoldAction(bool b);
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Room.Cotija;
namespace PepperDash.Essentials
{
public class CotijaEssentialsHuddleSpaceRoomBridge : CotijaBridgeBase
{
public EssentialsHuddleSpaceRoom Room { get; private set; }
/// <summary>
///
/// </summary>
public override string RoomName
{
get
{
return Room.Name;
}
}
/// <summary>
///
/// </summary>
/// <param name="parent"></param>
/// <param name="room"></param>
public CotijaEssentialsHuddleSpaceRoomBridge(EssentialsHuddleSpaceRoom room):
base("mobileControlBridge-essentialsHuddle", "Essentials Mobile Control Bridge-Huddle")
{
Room = room;
}
/// <summary>
/// Override of base: calls base to add parent and then registers actions and events.
/// </summary>
/// <param name="parent"></param>
public override void AddParent(CotijaSystemController parent)
{
base.AddParent(parent);
// 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.
// Source Changes and room off
Parent.AddAction(string.Format(@"/room/{0}/status", Room.Key), new Action(() => Room_RoomFullStatus(Room)));
Parent.AddAction(string.Format(@"/room/{0}/source", Room.Key), new Action<SourceSelectMessageContent>(c => Room.RunRouteAction(c.SourceListItem)));
Parent.AddAction(string.Format(@"/room/{0}/defaultsource", Room.Key), new Action(Room.RunDefaultRoute));
Parent.AddAction(string.Format(@"/room/{0}/masterVolumeLevel", Room.Key), new Action<ushort>(u =>
(Room.CurrentVolumeControls as IBasicVolumeWithFeedback).SetVolume(u)));
Parent.AddAction(string.Format(@"/room/{0}/masterVolumeMuteToggle", Room.Key), new Action(() => Room.CurrentVolumeControls.MuteToggle()));
Parent.AddAction(string.Format(@"/room/{0}/shutdownStart", Room.Key), new Action(() => Room.StartShutdown(eShutdownType.Manual)));
Parent.AddAction(string.Format(@"/room/{0}/shutdownEnd", Room.Key), new Action(() => Room.ShutdownPromptTimer.Finish()));
Parent.AddAction(string.Format(@"/room/{0}/shutdownCancel", Room.Key), new Action(() => Room.ShutdownPromptTimer.Cancel()));
Room.CurrentSingleSourceChange += new SourceInfoChangeHandler(Room_CurrentSingleSourceChange);
Room.CurrentVolumeDeviceChange += new EventHandler<VolumeDeviceChangeEventArgs>(Room_CurrentVolumeDeviceChange);
Room.OnFeedback.OutputChange += new EventHandler<EventArgs>(OnFeedback_OutputChange);
Room.IsCoolingDownFeedback.OutputChange += new EventHandler<EventArgs>(IsCoolingDownFeedback_OutputChange);
Room.IsWarmingUpFeedback.OutputChange += new EventHandler<EventArgs>(IsWarmingUpFeedback_OutputChange);
Room.ShutdownPromptTimer.HasStarted += new EventHandler<EventArgs>(ShutdownPromptTimer_HasStarted);
Room.ShutdownPromptTimer.HasFinished += new EventHandler<EventArgs>(ShutdownPromptTimer_HasFinished);
Room.ShutdownPromptTimer.WasCancelled += new EventHandler<EventArgs>(ShutdownPromptTimer_WasCancelled);
// Registers for initial volume events, if possible
var currentVolumeDevice = Room.CurrentVolumeControls;
if (currentVolumeDevice != null)
{
if (currentVolumeDevice is IBasicVolumeWithFeedback)
{
var newDev = currentVolumeDevice as IBasicVolumeWithFeedback;
newDev.MuteFeedback.OutputChange += new EventHandler<EventArgs>(VolumeLevelFeedback_OutputChange);
newDev.VolumeLevelFeedback.OutputChange += new EventHandler<EventArgs>(VolumeLevelFeedback_OutputChange);
}
}
}
/// <summary>
/// Handler for cancelled shutdown
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void ShutdownPromptTimer_WasCancelled(object sender, EventArgs e)
{
JObject roomStatus = new JObject();
roomStatus.Add("state", "wasCancelled");
JObject message = new JObject();
message.Add("type", "/room/shutdown/");
message.Add("content", roomStatus);
Parent.SendMessageToServer(message);
}
/// <summary>
/// Handler for when shutdown finishes
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void ShutdownPromptTimer_HasFinished(object sender, EventArgs e)
{
JObject roomStatus = new JObject();
roomStatus.Add("state", "hasFinished");
JObject message = new JObject();
message.Add("type", "/room/shutdown/");
message.Add("content", roomStatus);
Parent.SendMessageToServer(message);
}
/// <summary>
/// Handler for when shutdown starts
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void ShutdownPromptTimer_HasStarted(object sender, EventArgs e)
{
JObject roomStatus = new JObject();
roomStatus.Add("state", "hasStarted");
roomStatus.Add("duration", Room.ShutdownPromptTimer.SecondsToCount);
JObject message = new JObject();
message.Add("type", "/room/shutdown/");
message.Add("content", roomStatus);
Parent.SendMessageToServer(message);
// equivalent JS message:
// Post( { type: '/room/status/', content: { shutdown: 'hasStarted', duration: Room.ShutdownPromptTimer.SecondsToCount })
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void IsWarmingUpFeedback_OutputChange(object sender, EventArgs e)
{
JObject roomStatus = new JObject();
roomStatus.Add("isWarmingUp", (sender as BoolFeedback).BoolValue);
JObject message = new JObject();
message.Add("type", "/room/status/");
message.Add("content", roomStatus);
Parent.SendMessageToServer(message);
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void IsCoolingDownFeedback_OutputChange(object sender, EventArgs e)
{
JObject roomStatus = new JObject();
roomStatus.Add("isCoolingDown", (sender as BoolFeedback).BoolValue);
JObject message = new JObject();
message.Add("type", "/room/status/");
message.Add("content", roomStatus);
Parent.SendMessageToServer(message);
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void OnFeedback_OutputChange(object sender, EventArgs e)
{
/* Example message
* {
"type":"/room/status",
"content": {
"isOn": false
}
}
*/
JObject roomStatus = new JObject();
roomStatus.Add("isOn", (sender as BoolFeedback).BoolValue);
JObject message = new JObject();
message.Add("type", "/room/status/");
message.Add("content", roomStatus);
Parent.SendMessageToServer(message);
}
void Room_CurrentVolumeDeviceChange(object sender, VolumeDeviceChangeEventArgs e)
{
if (e.OldDev is IBasicVolumeWithFeedback)
{
var oldDev = e.OldDev as IBasicVolumeWithFeedback;
oldDev.MuteFeedback.OutputChange -= VolumeLevelFeedback_OutputChange;
oldDev.VolumeLevelFeedback.OutputChange -= VolumeLevelFeedback_OutputChange;
}
if (e.NewDev is IBasicVolumeWithFeedback)
{
var newDev = e.NewDev as IBasicVolumeWithFeedback;
newDev.MuteFeedback.OutputChange += new EventHandler<EventArgs>(VolumeLevelFeedback_OutputChange);
newDev.VolumeLevelFeedback.OutputChange += new EventHandler<EventArgs>(VolumeLevelFeedback_OutputChange);
}
}
void VolumeLevelFeedback_OutputChange(object sender, EventArgs e)
{
/* Example message
* {
"type":"/room/status",
"content": {
"masterVolumeLevel": 12345,
"masterVolumeMuteState": false
}
}
*/
var huddleRoom = Room as EssentialsHuddleSpaceRoom;
if(huddleRoom.CurrentVolumeControls is IBasicVolumeWithFeedback)
{
JObject roomStatus = new JObject();
if (huddleRoom.CurrentVolumeControls is IBasicVolumeWithFeedback)
{
var currentVolumeConstrols = huddleRoom.CurrentVolumeControls as IBasicVolumeWithFeedback;
roomStatus.Add("masterVolumeLevel", currentVolumeConstrols.VolumeLevelFeedback.IntValue);
roomStatus.Add("masterVolumeMuteState", currentVolumeConstrols.MuteFeedback.BoolValue);
}
JObject message = new JObject();
message.Add("type", "/room/status/");
message.Add("content", roomStatus);
Parent.SendMessageToServer(message);
}
}
void Room_CurrentSingleSourceChange(EssentialsRoomBase room, PepperDash.Essentials.Core.SourceListItem info, ChangeType type)
{
/* Example message
* {
"type":"/room/status",
"content": {
"selectedSourceKey": "off",
}
}
*/
if (type == ChangeType.WillChange)
{
// Disconnect from previous source
if (info != null)
{
var previousDev = info.SourceDevice;
// device type interfaces
if (previousDev is ISetTopBoxControls)
(previousDev as ISetTopBoxControls).UnlinkActions(Parent);
// common interfaces
if (previousDev is IChannel)
(previousDev as IChannel).UnlinkActions(Parent);
if (previousDev is IColor)
(previousDev as IColor).UnlinkActions(Parent);
if (previousDev is IDPad)
(previousDev as IDPad).UnlinkActions(Parent);
if (previousDev is IDvr)
(previousDev as IDvr).UnlinkActions(Parent);
if (previousDev is INumericKeypad)
(previousDev as INumericKeypad).UnlinkActions(Parent);
if (previousDev is IPower)
(previousDev as IPower).UnlinkActions(Parent);
if (previousDev is ITransport)
(previousDev as ITransport).UnlinkActions(Parent);
}
}
else // did change
{
if (info != null)
{
var dev = info.SourceDevice;
if (dev is ISetTopBoxControls)
(dev as ISetTopBoxControls).LinkActions(Parent);
if (dev is IChannel)
(dev as IChannel).LinkActions(Parent);
if (dev is IColor)
(dev as IColor).LinkActions(Parent);
if (dev is IDPad)
(dev as IDPad).LinkActions(Parent);
if (dev is IDvr)
(dev as IDvr).LinkActions(Parent);
if (dev is INumericKeypad)
(dev as INumericKeypad).LinkActions(Parent);
if (dev is IPower)
(dev as IPower).LinkActions(Parent);
if (dev is ITransport)
(dev as ITransport).LinkActions(Parent);
var huddleRoom = room as EssentialsHuddleSpaceRoom;
JObject roomStatus = new JObject();
roomStatus.Add("selectedSourceKey", huddleRoom.CurrentSourceInfoKey);
JObject message = new JObject();
message.Add("type", "/room/status/");
message.Add("content", roomStatus);
Parent.SendMessageToServer(message);
}
}
}
/// <summary>
/// Posts the full status of the room to the server
/// </summary>
/// <param name="room"></param>
void Room_RoomFullStatus(EssentialsRoomBase room)
{
/* Example message
* {
"type":"/room/status",
"content": {
"selectedSourceKey": "off",
"isOn": false,
"masterVolumeLevel": 50,
"masterVolumeMuteState": false
}
}
*/
JObject roomStatus = new JObject();
var huddleRoom = room as EssentialsHuddleSpaceRoom;
roomStatus.Add("isOn", huddleRoom.OnFeedback.BoolValue);
roomStatus.Add("selectedSourceKey", huddleRoom.CurrentSourceInfoKey);
if(huddleRoom.CurrentVolumeControls is IBasicVolumeWithFeedback)
{
var currentVolumeConstrols = huddleRoom.CurrentVolumeControls as IBasicVolumeWithFeedback;
roomStatus.Add("masterVolumeLevel", currentVolumeConstrols.VolumeLevelFeedback.IntValue);
roomStatus.Add("masterVolumeMuteState", currentVolumeConstrols.MuteFeedback.BoolValue);
}
JObject message = new JObject();
message.Add("type", "/room/status/");
message.Add("content", roomStatus);
Parent.SendMessageToServer(message);
}
}
public class SourceSelectMessageContent
{
public string SourceListItem { get; set; }
//public string Destination { get; set; }
//public string SourceSelect { get; set; }
}
public delegate void PressAndHoldAction(bool b);
}

View File

@@ -187,8 +187,6 @@ namespace PepperDash.Essentials
disp.IsCoolingDownFeedback.OutputChange += (o, a) =>
{
IsCoolingDownFeedback.FireUpdate();
if (IsCoolingDownFeedback.BoolValue)
(DefaultDisplay as IBasicVolumeWithFeedback).SetVolume(DefaultVolume);
};
}
@@ -202,6 +200,12 @@ namespace PepperDash.Essentials
/// </summary>
protected override void EndShutdown()
{
SetDefaultLevels();
RunDefaultRoute();
CrestronEnvironment.Sleep(1000);
RunRouteAction("roomOff");
}
@@ -210,8 +214,8 @@ namespace PepperDash.Essentials
/// </summary>
public void RunDefaultRoute()
{
if (DefaultSourceItem != null)
RunRouteAction(DefaultSourceItem);
//if (DefaultSourceItem != null && !OnFeedback.BoolValue)
RunRouteAction(DefaultSourceItem);
}
/// <summary>
@@ -313,7 +317,9 @@ namespace PepperDash.Essentials
}
// Set volume control on room, using default if non provided
// Set volume control, using default if non provided
IBasicVolumeControls volDev = null;
// Handle special cases for volume control
if (string.IsNullOrEmpty(item.VolumeControlKey)
@@ -330,7 +336,27 @@ namespace PepperDash.Essentials
else if (dev is IHasVolumeDevice)
volDev = (dev as IHasVolumeDevice).VolumeDevice;
}
CurrentVolumeControls = volDev;
if (volDev != CurrentVolumeControls)
{
// zero the volume on the device we are leaving.
// Set the volume to default on device we are entering
if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback)
{
var vd = CurrentVolumeControls as IBasicVolumeWithFeedback;
SavedVolumeLevels[vd] = (uint)vd.VolumeLevelFeedback.IntValue;
vd.SetVolume(0);
}
CurrentVolumeControls = volDev;
if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback)
{
var vd = CurrentVolumeControls as IBasicVolumeWithFeedback;
ushort vol = (SavedVolumeLevels.ContainsKey(vd) ? (ushort)SavedVolumeLevels[vd] : DefaultVolume);
vd.SetVolume(vol);
}
}
// store the name and UI info for routes
if (item.SourceKey == "$off")
@@ -370,7 +396,10 @@ namespace PepperDash.Essentials
/// </summary>
public override void SetDefaultLevels()
{
Debug.Console(0, this, "SetDefaultLevels not implemented");
Debug.Console(1, this, "Restoring default levels");
var vc = CurrentVolumeControls as IBasicVolumeWithFeedback;
if (vc != null)
vc.SetVolume(DefaultVolume);
}
/// <summary>

View File

@@ -12,7 +12,7 @@ using PepperDash.Essentials.Devices.Common.VideoCodec;
namespace PepperDash.Essentials
{
public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IHasCurrentSourceInfoChange, IPrivacy
public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IHasCurrentSourceInfoChange, IPrivacy, IHasCurrentVolumeControls
{
public event EventHandler<VolumeDeviceChangeEventArgs> CurrentVolumeDeviceChange;
public event SourceInfoChangeHandler CurrentSingleSourceChange;
@@ -122,7 +122,8 @@ namespace PepperDash.Essentials
string LastSourceKey;
/// <summary>
///
/// Sets the volume control device, and attaches/removes InUseTrackers with "audio"
/// tag to device.
/// </summary>
public IBasicVolumeControls CurrentVolumeControls
{
@@ -190,6 +191,8 @@ namespace PepperDash.Essentials
/// </summary>
public IHasScheduleAwareness ScheduleSource { get { return VideoCodec as IHasScheduleAwareness; } }
CCriticalSection SourceSelectLock = new CCriticalSection();
/// <summary>
///
/// </summary>
@@ -211,6 +214,7 @@ namespace PepperDash.Essentials
else if (defaultAudio is IHasVolumeDevice)
DefaultVolumeControls = (defaultAudio as IHasVolumeDevice).VolumeDevice;
CurrentVolumeControls = DefaultVolumeControls;
var disp = DefaultDisplay as DisplayBase;
if (disp != null)
@@ -243,7 +247,10 @@ namespace PepperDash.Essentials
}
InCallFeedback = new BoolFeedback(() => VideoCodec.IsInCall);
IsSharingFeedback = new BoolFeedback(() => VideoCodec.SharingSourceFeedback.StringValue != null);
VideoCodec.CallStatusChange += (o, a) => this.InCallFeedback.FireUpdate();
IsSharingFeedback = new BoolFeedback(() => VideoCodec.SharingContentIsOnFeedback.BoolValue);
VideoCodec.SharingContentIsOnFeedback.OutputChange += (o, a) => this.IsSharingFeedback.FireUpdate();
// link privacy to VC (for now?)
PrivacyModeIsOnFeedback = new BoolFeedback(() => VideoCodec.PrivacyModeIsOnFeedback.BoolValue);
@@ -262,6 +269,13 @@ namespace PepperDash.Essentials
protected override void EndShutdown()
{
VideoCodec.EndAllCalls();
SetDefaultLevels();
RunDefaultPresentRoute();
CrestronEnvironment.Sleep(1000);
RunRouteAction("roomOff");
}
@@ -270,7 +284,7 @@ namespace PepperDash.Essentials
/// </summary>
public bool RunDefaultPresentRoute()
{
if (DefaultSourceItem != null)
//if (DefaultSourceItem != null)
RunRouteAction(DefaultSourceItem);
return DefaultSourceItem != null;
}
@@ -304,6 +318,9 @@ namespace PepperDash.Essentials
// Run this on a separate thread
new CTimer(o =>
{
// try to prevent multiple simultaneous selections
SourceSelectLock.TryEnter();
try
{
@@ -362,6 +379,50 @@ namespace PepperDash.Essentials
(item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage();
}
// See if this can be moved into common, base-class method -------------
// Set volume control, using default if non provided
IBasicVolumeControls volDev = null;
// Handle special cases for volume control
if (string.IsNullOrEmpty(item.VolumeControlKey)
|| item.VolumeControlKey.Equals("$defaultAudio", StringComparison.OrdinalIgnoreCase))
volDev = DefaultVolumeControls;
else if (item.VolumeControlKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase))
volDev = DefaultDisplay as IBasicVolumeControls;
// Or a specific device, probably rarely used.
else
{
var dev = DeviceManager.GetDeviceForKey(item.VolumeControlKey);
if (dev is IBasicVolumeControls)
volDev = dev as IBasicVolumeControls;
else if (dev is IHasVolumeDevice)
volDev = (dev as IHasVolumeDevice).VolumeDevice;
}
if (volDev != CurrentVolumeControls)
{
// zero the volume on the device we are leaving.
// Set the volume to default on device we are entering
if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback)
{
var vd = CurrentVolumeControls as IBasicVolumeWithFeedback;
SavedVolumeLevels[vd] = (uint)vd.VolumeLevelFeedback.IntValue;
vd.SetVolume(0);
}
CurrentVolumeControls = volDev;
if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback)
{
var vd = CurrentVolumeControls as IBasicVolumeWithFeedback;
ushort vol = (SavedVolumeLevels.ContainsKey(vd) ? (ushort)SavedVolumeLevels[vd] : DefaultVolume);
vd.SetVolume(vol);
}
}
// -----------------------------------------------------------------------
// store the name and UI info for routes
if (item.SourceKey == "$off")
{
@@ -385,6 +446,7 @@ namespace PepperDash.Essentials
Debug.Console(1, this, "ERROR in routing: {0}", e);
}
SourceSelectLock.Leave();
}, 0); // end of CTimer
}

View File

@@ -51,6 +51,8 @@ namespace PepperDash.Essentials
public PepperDash.Essentials.Room.EssentialsRoomEmergencyBase Emergency { get; set; }
public PepperDash.Essentials.Devices.Common.Microphones.MicrophonePrivacyController MicrophonePrivacy { get; set; }
public string LogoUrl { get; set; }
protected SecondsCountdownTimer RoomVacancyShutdownTimer { get; private set; }
@@ -72,6 +74,13 @@ namespace PepperDash.Essentials
/// </summary>
protected abstract Func<bool> OnFeedbackFunc { get; }
protected Dictionary<IBasicVolumeWithFeedback, uint> SavedVolumeLevels = new Dictionary<IBasicVolumeWithFeedback, uint>();
/// <summary>
/// When volume control devices change, should we zero the one that we are leaving?
/// </summary>
public bool ZeroVolumeWhenSwtichingVolumeDevices { get; private set; }
/// <summary>
///
/// </summary>
@@ -121,8 +130,11 @@ namespace PepperDash.Essentials
StartRoomVacancyTimer(eVacancyMode.InShutdownWarning);
break;
case eVacancyMode.InShutdownWarning:
StartShutdown(eShutdownType.Vacancy);
break;
{
StartShutdown(eShutdownType.Vacancy);
Debug.Console(0, this, "Shutting Down due to vacancy.");
break;
}
default:
break;
}
@@ -152,6 +164,8 @@ namespace PepperDash.Essentials
RoomVacancyShutdownTimer.SecondsToCount = RoomVacancyShutdownSeconds;
VacancyMode = mode;
RoomVacancyShutdownTimer.Start();
Debug.Console(0, this, "Vacancy Timer Started.");
}
/// <summary>
@@ -179,12 +193,21 @@ namespace PepperDash.Essentials
/// Sets the object to be used as the IOccupancyStatusProvider for the room. Can be an Occupancy Aggregator or a specific device
/// </summary>
/// <param name="statusProvider"></param>
public void SetRoomOccupancy(IOccupancyStatusProvider statusProvider)
public void SetRoomOccupancy(IOccupancyStatusProvider statusProvider, int timeoutMinutes)
{
if (statusProvider == null)
{
Debug.Console(0, this, "ERROR: Occupancy sensor device is null");
return;
}
// If status provider is fusion, set flag to remote
if (statusProvider is PepperDash.Essentials.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase)
OccupancyStatusProviderIsRemote = true;
if(timeoutMinutes > 0)
RoomVacancyShutdownSeconds = timeoutMinutes * 60;
RoomOccupancy = statusProvider;
RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += new EventHandler<EventArgs>(RoomIsOccupiedFeedback_OutputChange);
@@ -192,19 +215,26 @@ namespace PepperDash.Essentials
void RoomIsOccupiedFeedback_OutputChange(object sender, EventArgs e)
{
if ((sender as IOccupancyStatusProvider).RoomIsOccupiedFeedback.BoolValue == false)
if (RoomOccupancy.RoomIsOccupiedFeedback.BoolValue == false)
{
Debug.Console(1, this, "Notice: Vacancy Detected");
// Trigger the timer when the room is vacant
StartRoomVacancyTimer(eVacancyMode.InInitialVacancy);
}
else
{
Debug.Console(1, this, "Notice: Occupancy Detected");
// Reset the timer when the room is occupied
if(RoomVacancyShutdownTimer.IsRunningFeedback.BoolValue)
RoomVacancyShutdownTimer.Cancel();
}
}
//void SwapVolumeDevices(IBasicVolumeControls currentDevice, IBasicVolumeControls newDevice)
//{
//}
/// <summary>
/// Executes when RoomVacancyShutdownTimer expires. Used to trigger specific room actions as needed. Must nullify the timer object when executed
/// </summary>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,9 @@
public bool ShowDate { get; set; }
public bool ShowTime { get; set; }
public UiSetupPropertiesConfig Setup { get; set; }
public string HeaderStyle { get; set; }
public string HeaderStyle { get; set; }
public bool IncludeInFusionRoomHealth { get; set; }
/// <summary>
/// The count of sources that will trigger the "additional" arrows to show on the SRL.

View File

@@ -36,149 +36,74 @@ namespace PepperDash.Essentials
public EssentialsTouchpanelController(string key, string name, string type, CrestronTouchpanelPropertiesConfig props, uint id)
: base(key, name)
{
AddPostActivationAction(() =>
{
Debug.Console(0, this, "Creating hardware...");
type = type.ToLower();
try
{
if (type == "crestronapp")
{
var app = new CrestronApp(id, Global.ControlSystem);
app.ParameterProjectName.Value = props.ProjectName;
Panel = app;
}
else if (type == "tsw550")
Panel = new Tsw550(id, Global.ControlSystem);
else if (type == "tsw552")
Panel = new Tsw552(id, Global.ControlSystem);
else if (type == "tsw560")
Panel = new Tsw560(id, Global.ControlSystem);
else if (type == "tsw750")
Panel = new Tsw750(id, Global.ControlSystem);
else if (type == "tsw752")
Panel = new Tsw752(id, Global.ControlSystem);
else if (type == "tsw760")
Panel = new Tsw760(id, Global.ControlSystem);
else if (type == "tsw1050")
Panel = new Tsw1050(id, Global.ControlSystem);
else if (type == "tsw1052")
Panel = new Tsw1052(id, Global.ControlSystem);
else if (type == "tsw1060")
Panel = new Tsw1060(id, Global.ControlSystem);
else
{
Debug.Console(0, this, "WARNING: Cannot create TSW controller with type '{0}'", type);
return;
}
}
catch (Exception e)
{
Debug.Console(0, this, "WARNING: Cannot create TSW base class. Panel will not function: {0}", e.Message);
return;
}
// Reserved sigs
if (Panel is TswFt5ButtonSystem)
{
var tsw = Panel as TswFt5ButtonSystem;
tsw.ExtenderSystemReservedSigs.Use();
tsw.ExtenderSystemReservedSigs.DeviceExtenderSigChange
+= ExtenderSystemReservedSigs_DeviceExtenderSigChange;
}
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating touchpanel hardware...");
type = type.ToLower();
try
{
if (type == "crestronapp")
{
var app = new CrestronApp(id, Global.ControlSystem);
app.ParameterProjectName.Value = props.ProjectName;
Panel = app;
}
else if (type == "tsw550")
Panel = new Tsw550(id, Global.ControlSystem);
else if (type == "tsw552")
Panel = new Tsw552(id, Global.ControlSystem);
else if (type == "tsw560")
Panel = new Tsw560(id, Global.ControlSystem);
else if (type == "tsw750")
Panel = new Tsw750(id, Global.ControlSystem);
else if (type == "tsw752")
Panel = new Tsw752(id, Global.ControlSystem);
else if (type == "tsw760")
Panel = new Tsw760(id, Global.ControlSystem);
else if (type == "tsw1050")
Panel = new Tsw1050(id, Global.ControlSystem);
else if (type == "tsw1052")
Panel = new Tsw1052(id, Global.ControlSystem);
else if (type == "tsw1060")
Panel = new Tsw1060(id, Global.ControlSystem);
else
{
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "WARNING: Cannot create TSW controller with type '{0}'", type);
return;
}
}
catch (Exception e)
{
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "WARNING: Cannot create TSW base class. Panel will not function: {0}", e.Message);
return;
}
new CTimer(o =>
{
var regSuccess = Panel.Register();
if (regSuccess != eDeviceRegistrationUnRegistrationResponse.Success)
Debug.Console(0, this, "WARNING: Registration failed. Continuing, but panel may not function: {0}", regSuccess);
// Reserved sigs
if (Panel is TswFt5ButtonSystem)
{
var tsw = Panel as TswFt5ButtonSystem;
tsw.ExtenderSystemReservedSigs.Use();
tsw.ExtenderSystemReservedSigs.DeviceExtenderSigChange
+= ExtenderSystemReservedSigs_DeviceExtenderSigChange;
// Give up cleanly if SGD is not present.
var sgdName = @"\NVRAM\Program" + InitialParametersClass.ApplicationNumber
+ @"\sgd\" + props.SgdFile;
if (!File.Exists(sgdName))
{
Debug.Console(0, this, "WARNING: Smart object file '{0}' not present. Exiting TSW load", sgdName);
return;
}
tsw.ButtonStateChange += new ButtonEventHandler(Tsw_ButtonStateChange);
Panel.LoadSmartObjects(sgdName);
Panel.SigChange += Tsw_SigChange;
}
var mainDriver = new EssentialsPanelMainInterfaceDriver(Panel, props);
// Then the AV driver
if (Panel.Register() != eDeviceRegistrationUnRegistrationResponse.Success)
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "WARNING: Registration failed. Continuing, but panel may not function: {0}", Panel.RegistrationFailureReason);
// spin up different room drivers depending on room type
var room = DeviceManager.GetDeviceForKey(props.DefaultRoomKey);
if (room is EssentialsHuddleSpaceRoom)
{
Debug.Console(0, this, "Adding huddle space driver");
var avDriver = new EssentialsHuddlePanelAvFunctionsDriver(mainDriver, props);
avDriver.CurrentRoom = room as EssentialsHuddleSpaceRoom;
avDriver.DefaultRoomKey = props.DefaultRoomKey;
mainDriver.AvDriver = avDriver;
LoadAndShowDriver(mainDriver); // This is a little convoluted.
// Give up cleanly if SGD is not present.
var sgdName = Global.FilePathPrefix
+ Global.DirectorySeparator + "sgd" + Global.DirectorySeparator + props.SgdFile;
if (!File.Exists(sgdName))
{
Debug.Console(0, this, "ERROR: Smart object file '{0}' not present. Exiting TSW load", sgdName);
return;
}
if (Panel is TswFt5ButtonSystem)
{
var tsw = Panel as TswFt5ButtonSystem;
// Wire up hard keys
tsw.Power.UserObject = new Action<bool>(b => { if (!b) avDriver.PowerButtonPressed(); });
//tsw.Home.UserObject = new Action<bool>(b => { if (!b) HomePressed(); });
tsw.Up.UserObject = new Action<bool>(avDriver.VolumeUpPress);
tsw.Down.UserObject = new Action<bool>(avDriver.VolumeDownPress);
tsw.ButtonStateChange += new ButtonEventHandler(Tsw_ButtonStateChange);
}
}
else if (room is EssentialsPresentationRoom)
{
Debug.Console(0, this, "Adding presentation room driver");
var avDriver = new EssentialsPresentationPanelAvFunctionsDriver(mainDriver, props);
avDriver.CurrentRoom = room as EssentialsPresentationRoom;
avDriver.DefaultRoomKey = props.DefaultRoomKey;
mainDriver.AvDriver = avDriver;
LoadAndShowDriver(mainDriver);
if (Panel is TswFt5ButtonSystem)
{
var tsw = Panel as TswFt5ButtonSystem;
// Wire up hard keys
tsw.Power.UserObject = new Action<bool>(b => { if (!b) avDriver.PowerButtonPressed(); });
//tsw.Home.UserObject = new Action<bool>(b => { if (!b) HomePressed(); });
tsw.Up.UserObject = new Action<bool>(avDriver.VolumeUpPress);
tsw.Down.UserObject = new Action<bool>(avDriver.VolumeDownPress);
tsw.ButtonStateChange += new ButtonEventHandler(Tsw_ButtonStateChange);
}
}
else if (room is EssentialsHuddleVtc1Room)
{
Debug.Console(0, this, "Adding huddle space driver");
var avDriver = new EssentialsHuddleVtc1PanelAvFunctionsDriver(mainDriver, props);
var codecDriver = new PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver(Panel, avDriver,
(room as EssentialsHuddleVtc1Room).VideoCodec);
avDriver.SetVideoCodecDriver(codecDriver);
avDriver.CurrentRoom = room as EssentialsHuddleVtc1Room;
avDriver.DefaultRoomKey = props.DefaultRoomKey;
mainDriver.AvDriver = avDriver;
LoadAndShowDriver(mainDriver); // This is a little convoluted.
Panel.LoadSmartObjects(sgdName);
Panel.SigChange += Tsw_SigChange;
if (Panel is TswFt5ButtonSystem)
{
var tsw = Panel as TswFt5ButtonSystem;
// Wire up hard keys
tsw.Power.UserObject = new Action<bool>(b => { if (!b) avDriver.EndMeetingPress(); });
//tsw.Home.UserObject = new Action<bool>(b => { if (!b) HomePressed(); });
tsw.Up.UserObject = new Action<bool>(avDriver.VolumeUpPress);
tsw.Down.UserObject = new Action<bool>(avDriver.VolumeDownPress);
tsw.ButtonStateChange += new ButtonEventHandler(Tsw_ButtonStateChange);
}
}
else
{
Debug.Console(0, this, "ERROR: Cannot load AvFunctionsDriver for room '{0}'", props.DefaultRoomKey);
}
}, 0);
});
}
public void LoadAndShowDriver(PanelDriverBase driver)

View File

@@ -207,16 +207,132 @@ namespace PepperDash.Essentials
/// <summary>
/// 1252
/// </summary>
public const uint VCRemoteViewTogglePress = 1252;
public const uint VCLayoutTogglePress = 1252;
/// <summary>
/// 1253
/// </summary>
public const uint VCSelfViewPipTogglePress = 1253;
/// <summary>
/// 1254
/// </summary>
public const uint VCLayoutToggleEnable = 1254;
/// <summary>
/// 1255
/// </summary>
public const uint VCMinMaxPress = 1255;
/// <summary>
/// 1256
/// </summary>
public const uint VCMinMaxEnable = 1256;
// Letter joins start at 2921;
//******************************************************
// Letter joins start at 2921;
// Environment Joins
// Popup Container
/// <summary>
/// 2001 - 2004
/// </summary>
public const uint EnvironmentBackgroundSubpageVisibleBase = 2000;
// ColumnOne
/// <summary>
/// 2011 - 2015
/// </summary>
public const uint EnvironmentColumnOneLightingTypeVisibleBase = 2010;
/// <summary>
/// 2016 - 2020
/// </summary>
public const uint EnvironmentColumnOneShadingTypeVisibleBase = 2015;
// ColumnTwo
/// <summary>
/// 2021 - 2025
/// </summary>
public const uint EnvironmentColumnTwoLightingTypeVisibleBase = 2020;
/// <summary>
/// 2026 - 2030
/// </summary>
public const uint EnvironmentColumnTwoShadingTypeVisibleBase = 2025;
// ColumnThree
/// <summary>
/// 2031 - 2035
/// </summary>
public const uint EnvironmentColumnThreeLightingTypeVisibleBase = 2030;
/// <summary>
/// 2036 - 2040
/// </summary>
public const uint EnvironmentColumnThreeShadingTypeVisibleBase = 2035;
// ColumnFour
/// <summary>
/// 2041 - 2045
/// </summary>
public const uint EnvironmentColumnFourLightingTypeVisibleBase = 2040;
/// <summary>
/// 2046 - 2050
/// </summary>
public const uint EnvironmentColumnFourShadingTypeVisibleBase = 2045;
// Button press
/// <summary>
/// 2051 - 2060
/// </summary>
public const uint EnvironmentColumnOneButtonPressBase = 2050;
/// <summary>
/// 2061 - 2070
/// </summary>
public const uint EnvironmentColumnTwoButtonPressBase = 2060;
/// <summary>
/// 2071 - 2080
/// </summary>
public const uint EnvironmentColumnThreeButtonPressBase = 2070;
/// <summary>
/// 2081 - 2090
/// </summary>
public const uint EnvironmentColumnFourButtonPressBase = 2080;
// Button visibility
/// <summary>
/// 2151 - 2160
/// </summary>
public const uint EnvironmentColumnOneButtonVisibleBase = 2150;
/// <summary>
/// 2161 - 2170
/// </summary>
public const uint EnvironmentColumnTwoButtonVisibleBase = 2160;
/// <summary>
/// 2171 - 2180
/// </summary>
public const uint EnvironmentColumnThreeButtonVisibleBase = 2170;
/// <summary>
/// 2181 - 2190
/// </summary>
public const uint EnvironmentColumnFourButtonVisibleBase = 2180;
//******************************************************
/// <summary>
/// 3101
@@ -251,7 +367,7 @@ namespace PepperDash.Essentials
/// </summary>
public const uint TechSchedulerVisible = 3112;
//******************************************************
//*****************************************************
/// <summary>
/// 3811
/// </summary>

View File

@@ -28,6 +28,10 @@
/// 1204
/// </summary>
public const uint VCFavoritesList = 1204;
/// <summary>
/// 1205 Layout buttons dynamic list
/// </summary>
public const uint VCLayoutsList = 1205;
//******************************************************

View File

@@ -68,6 +68,34 @@ namespace PepperDash.Essentials
/// </summary>
//public const uint KeypadText = 2901;
//******************************************************
// Environment Joins
/// <summary>
/// 2001 - 2010
/// </summary>
public const uint EnvironmentColumnOneLabelBase = 2000;
/// <summary>
/// 2011 - 2020
/// </summary>
public const uint EnvironmentColumnTwoLabelBase = 2010;
/// <summary>
/// 2021 - 2030
/// </summary>
public const uint EnvironmentColumnThreeLabelBase = 2020;
/// <summary>
/// 2031 - 2040
/// </summary>
public const uint EnvironmentColumnFourLabelBase = 2030;
// 2050, 2060, 2070 and 2080 reserved for column device name labels
//******************************************************
/// <summary>
/// 3101 - This is the start of the range 3101 - 3120
/// </summary>
@@ -121,6 +149,10 @@ namespace PepperDash.Essentials
/// 3906 - The separator for verbose-header text on addresses
/// </summary>
public const uint RoomAddressPipeText = 3906;
/// <summary>
/// 3907 - The user code for mobile control
/// </summary>
public const uint RoomUserCode = 3907;
/// <summary>
/// 3911
/// </summary>

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