Working 2-way communication with Cotija Node server for Essentials Huddle Room Type

This commit is contained in:
Neil Dorin
2017-06-26 10:11:12 -06:00
parent e6281b7048
commit 615880a3d8
12 changed files with 572 additions and 265 deletions

View File

@@ -138,12 +138,22 @@ namespace PepperDash.Essentials.Core
public void VolumeUp(bool pressRelease) public void VolumeUp(bool pressRelease)
{ {
Debug.Console(0, this, "Volume Down {0}", pressRelease); Debug.Console(2, this, "Volume Down {0}", pressRelease);
if (pressRelease)
{
var newLevel = _FakeVolumeLevel + 655;
SetVolume((ushort)newLevel);
}
} }
public void VolumeDown(bool pressRelease) public void VolumeDown(bool pressRelease)
{ {
Debug.Console(0, this, "Volume Up {0}", pressRelease); Debug.Console(2, this, "Volume Up {0}", pressRelease);
if (pressRelease)
{
var newLevel = _FakeVolumeLevel - 655;
SetVolume((ushort)newLevel);
}
} }
public void MuteToggle() public void MuteToggle()

View File

@@ -338,15 +338,15 @@ namespace PepperDash.Essentials.Devices.Displays
public void VolumeDown(bool pressRelease) public void VolumeDown(bool pressRelease)
{ {
throw new NotImplementedException(); //throw new NotImplementedException();
#warning need incrementer for these //#warning need incrementer for these
//Send(VolumeDownCmd); SetVolume(_VolumeLevel++);
} }
public void VolumeUp(bool pressRelease) public void VolumeUp(bool pressRelease)
{ {
throw new NotImplementedException(); //throw new NotImplementedException();
//Send(VolumeUpCmd); SetVolume(_VolumeLevel--);
} }
#endregion #endregion

View File

@@ -40,6 +40,11 @@ namespace PepperDash.Essentials
return new MockDisplay(key, name); return new MockDisplay(key, name);
} }
else if (typeName == "generic")
{
return new Device(key, name);
}
// MOVE into something else??? // MOVE into something else???
else if (typeName == "basicirdisplay") else if (typeName == "basicirdisplay")
{ {

View File

@@ -162,6 +162,8 @@ namespace PepperDash.Essentials
{ {
if (room is EssentialsHuddleSpaceRoom) if (room is EssentialsHuddleSpaceRoom)
{ {
DeviceManager.AddDevice(room);
Debug.Console(1, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion"); Debug.Console(1, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion");
DeviceManager.AddDevice(new EssentialsHuddleSpaceFusionSystemController((EssentialsHuddleSpaceRoom)room, 0xf1)); DeviceManager.AddDevice(new EssentialsHuddleSpaceFusionSystemController((EssentialsHuddleSpaceRoom)room, 0xf1));

View File

@@ -0,0 +1,226 @@
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;
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 Action<bool>(b => room.CurrentVolumeControls.VolumeUp(b)));
Parent.AddAction(string.Format(@"/room/{0}/event/masterVolumeDownBtn", Room.Key), new Action<bool>(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.DidChange)
return;
JObject roomStatus = new JObject();
var huddleRoom = room as EssentialsHuddleSpaceRoom;
//roomStatus.Add("isOn", huddleRoom.OnFeedback.BoolValue);
roomStatus.Add("selectedSourceKey", huddleRoom.CurrentSourceInfoKey);
JObject message = new JObject();
message.Add("type", "/room/status/");
message.Add("content", roomStatus);
Parent.PostToServer(Room, 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.PostToServer(Room, message);
}
}
public class SourceSelectMessageContent
{
public string Destination { get; set; }
public string SourceSelect { get; set; }
}
//public class PostMessage
//{
// [JsonProperty("type")]
// public string Type { get; set; }
// [JsonProperty("content")]
// public JToken Content { get; set; }
//}
//public class RoomStatusMessageContent
//{
// [JsonProperty("selectedSourceKey")]
// public string SelectedSourceKey { get; set; }
// [JsonProperty("isOn")]
// public bool? IsOn { get; set; }
// [JsonProperty("masterVolumeLevel")]
// public int? MasterVolumeLevel { get; set; }
// [JsonProperty("masterVolumeMuteState")]
// public bool? MasterVolumeMuteState { get; set; }
//}
}

View File

@@ -42,18 +42,33 @@ namespace PepperDash.Essentials
CotijaRooms = new List<CotijaEssentialsHuddleSpaceRoomBridge>(); CotijaRooms = new List<CotijaEssentialsHuddleSpaceRoomBridge>();
CrestronConsole.AddNewConsoleCommand(ConnectSseClient, "InitializeHttpClient", "Initializes a new HTTP client connection to a specified URL", 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); CrestronConsole.AddNewConsoleCommand(DisconnectSseClient, "CloseHttpClient", "Closes the active HTTP client", ConsoleAccessLevelEnum.AccessOperator);
AddPostActivationAction(() => RegisterSystemToServer(null)); AddPostActivationAction(() => RegisterSystemToServer(null));
} }
/// <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) public void AddAction(string key, object action)
{ {
// This might blow up if an action with that key already exists if (!ActionDictionary.ContainsKey(key))
{
ActionDictionary.Add(key, action); ActionDictionary.Add(key, action);
} }
else
{
Debug.Console(1, this, "Cannot add action with key '{0}' because key already exists in ActionDictionary.");
}
}
/// <summary>
/// Removes and action from the dictionary
/// </summary>
/// <param name="key"></param>
public void RemoveAction(string key) public void RemoveAction(string key)
{ {
if (ActionDictionary.ContainsKey(key)) if (ActionDictionary.ContainsKey(key))
@@ -135,22 +150,39 @@ namespace PepperDash.Essentials
/// <param name="o">object to be serialized and sent in post body</param> /// <param name="o">object to be serialized and sent in post body</param>
public void PostToServer(EssentialsRoomBase room, JObject o) public void PostToServer(EssentialsRoomBase room, JObject o)
{ {
if(Client == null) try
{
if (Client == null)
Client = new HttpClient(); Client = new HttpClient();
//HttpClient client = new HttpClient();
HttpClientRequest request = new HttpClientRequest(); HttpClientRequest request = new HttpClientRequest();
Client.Verbose = true; Client.Verbose = true;
Client.KeepAlive = true; Client.KeepAlive = true;
string url = string.Format("http://{0}/api/room/{1}", Config.serverUrl, string.Format("{0}-{1}", SystemUuid, room.Key)); string url = string.Format("http://{0}/api/room/{1}/status", Config.serverUrl, string.Format("{0}--{1}", SystemUuid, room.Key));
request.Url.Parse(url); request.Url.Parse(url);
request.RequestType = RequestType.Post; request.RequestType = RequestType.Post;
request.Header.SetHeaderValue("Content-Type", "application/json"); request.Header.SetHeaderValue("Content-Type", "application/json");
request.ContentString = o.ToString(); request.KeepAlive = true;
Client.DispatchAsync(request, PostConnectionCallback); // 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(5000, 5000);
}
catch(Exception e)
{
Debug.Console(1, this, "Error Posting to Server: {0}", e);
}
} }
/// <summary> /// <summary>
@@ -181,6 +213,13 @@ namespace PepperDash.Essentials
{ {
if (resp != null && resp.Code == 200) if (resp != null && resp.Code == 200)
{ {
if(Reconnect != null)
{
Reconnect.Stop();
Reconnect = null;
}
if (SseClient == null) if (SseClient == null)
{ {
ConnectSseClient(null); ConnectSseClient(null);
@@ -188,12 +227,12 @@ namespace PepperDash.Essentials
} }
else else
{ {
Debug.Console(0, this, "Unable to initialize SSE Client"); Debug.Console(0, this, "Response from server: {0}\n{1}", resp.Code, err);
} }
} }
catch (Exception e) catch (Exception e)
{ {
Debug.Console(1, this, "Error Initializeing SSE Client: {0}", e); Debug.Console(1, this, "Error Initializing SSE Client: {0}", e);
} }
} }
@@ -205,15 +244,22 @@ namespace PepperDash.Essentials
{ {
if (Heartbeat != null) if (Heartbeat != null)
{ {
Debug.Console(1, this, "Heartbeat Timer Expired.");
Heartbeat.Stop(); Heartbeat.Stop();
Heartbeat = null; Heartbeat = null;
} }
// Start the reconnect timer StartReconnectTimer(5000, 5000);
Reconnect = new CTimer(ReconnectToServer, null, 5000, 5000); }
Reconnect.Reset(5000, 5000); void StartReconnectTimer(long dueTime, long repeatTime)
{
// Start the reconnect timer
Reconnect = new CTimer(ReconnectToServer, null, dueTime, repeatTime);
Reconnect.Reset(dueTime, repeatTime);
} }
@@ -246,12 +292,9 @@ namespace PepperDash.Essentials
SseClient.Url = string.Format("http://{0}/api/system/stream/{1}", Config.serverUrl, uuid); SseClient.Url = string.Format("http://{0}/api/system/stream/{1}", Config.serverUrl, uuid);
SseClient.Connect(); SseClient.Connect();
//Heartbeat = new CTimer(HeartbeatExpired, null, 20000, 20000);
//Heartbeat.Reset(20000, 20000);
} }
void LineGathered_LineReceived(object sender, GenericCommMethodReceiveTextArgs e) void LineGathered_LineReceived(object sender, GenericCommMethodReceiveTextArgs e)
{ {
//Debug.Console(1, this, "Received from Server: '{0}'", e.Text); //Debug.Console(1, this, "Received from Server: '{0}'", e.Text);
@@ -260,8 +303,6 @@ namespace PepperDash.Essentials
{ {
var message = e.Text.Substring(6); var message = e.Text.Substring(6);
string roomId = null;
Debug.Console(1, this, "Message: '{0}'", message); Debug.Console(1, this, "Message: '{0}'", message);
try try
@@ -270,11 +311,21 @@ namespace PepperDash.Essentials
var type = messageObj["type"].Value<string>(); var type = messageObj["type"].Value<string>();
if(type == "/system/hearbeat") if (type == "hello")
{ {
//Heartbeat.Reset(20000, 20000); Heartbeat = new CTimer(HeartbeatExpired, null, 20000, 20000);
Debug.Console(2, this, "Heartbeat Timer Started.");
Heartbeat.Reset(20000, 20000);
} }
else if(type == "close") else if (type == "/system/heartbeat")
{
Heartbeat.Reset(20000, 20000);
Debug.Console(2, this, "Heartbeat Timer Reset.");
}
else if (type == "close")
{ {
SseClient.Disconnect(); SseClient.Disconnect();
@@ -285,19 +336,21 @@ namespace PepperDash.Essentials
} }
else else
{ {
// Check path against Action dictionary // Check path against Action dictionary
if (ActionDictionary.ContainsKey(type)) if (ActionDictionary.ContainsKey(type))
{ {
var action = ActionDictionary[type]; var action = ActionDictionary[type];
if (action is Action<bool>) if (action is Action)
{
(action as Action)();
}
else if (action is Action<bool>)
{ {
var stateString = messageObj["content"]["state"].Value<string>(); var stateString = messageObj["content"]["state"].Value<string>();
// Look for a button press event // Look for a button press event
if(!string.IsNullOrEmpty(stateString)) if (!string.IsNullOrEmpty(stateString))
{ {
#warning deal with held state later #warning deal with held state later
if (stateString == "held") if (stateString == "held")

View File

@@ -89,6 +89,8 @@ namespace PepperDash.Essentials
} }
SourceListItem _CurrentSourceInfo; SourceListItem _CurrentSourceInfo;
public string CurrentSourceInfoKey { get; private set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
@@ -156,7 +158,13 @@ namespace PepperDash.Essentials
// Let's run it // Let's run it
if (routeKey.ToLower() != "roomoff") if (routeKey.ToLower() != "roomoff")
{
LastSourceKey = routeKey; LastSourceKey = routeKey;
}
else
{
CurrentSourceInfoKey = null;
}
foreach (var route in item.RouteList) foreach (var route in item.RouteList)
{ {
@@ -205,7 +213,10 @@ namespace PepperDash.Essentials
// store the name and UI info for routes // store the name and UI info for routes
if (item.SourceKey != null) if (item.SourceKey != null)
{
CurrentSourceInfoKey = routeKey;
CurrentSourceInfo = item; CurrentSourceInfo = item;
}
// And finally, set the "control". This will trigger event // And finally, set the "control". This will trigger event
//CurrentControlDevice = DeviceManager.GetDeviceForKey(item.SourceKey) as Device; //CurrentControlDevice = DeviceManager.GetDeviceForKey(item.SourceKey) as Device;