using System; using System.Collections.Generic; using F10.StreamDeckIntegration.Json; using JetBrains.Annotations; using WebSocketSharp; namespace F10.StreamDeckIntegration { /// /// Manages WebSocket connections and messages with the Stream Deck. /// public static class StreamDeckSocket { private const int WebSocketPort = 2245; #if UNITY_2017_1_OR_NEWER private static readonly Queue> _messagesQueue = new Queue>(); #else private static readonly Queue> _messagesQueue = new Queue>(); #endif private static readonly Queue _sendQueue = new Queue(); private static readonly object _queueLock = new object(); private static WebSocket _socket = null; private static event Action UpdateEvents; /// /// Is the WebSocket connected and alive. /// [PublicAPI] public static bool IsConnected { get { return _socket != null && _socket.IsAlive; } } /// /// Try to connect or re-connect to any available Stream Deck. /// [PublicAPI] public static void Connect() { if (IsConnected) { Log.Info("Disconnecting current link..."); _socket.OnClose += (sender, args) => ConnectAsync(); _socket.CloseAsync(); } else { ConnectAsync(); } } /// /// Disconnect from any connected Stream Deck. /// [PublicAPI] public static void Disconnect() { if (!IsConnected) return; Log.Info("Disconnecting..."); _socket.CloseAsync(); } /// /// Handle one queued received and / or sent message. /// [PublicAPI] public static void OnUpdate() { if (UpdateEvents == null) return; UpdateEvents.Invoke(); } private static void ConnectAsync() { _socket = new WebSocket("ws://127.0.0.1:" + WebSocketPort); _socket.OnOpen += OnOpen; _socket.OnMessage += OnMessage; _socket.OnClose += OnClose; _socket.ConnectAsync(); } private static void OnOpen(object sender, EventArgs args) { UpdateEvents -= HandleMessages; UpdateEvents += HandleMessages; UpdateEvents -= HandleSending; UpdateEvents += HandleSending; Log.Info("Connected"); StreamDeckHelper.GetConnectedDevices(); } private static void OnMessage(object sender, MessageEventArgs e) { Log.Debug("Received: " + e.Data); var data = JsonParser.Parse(e.Data); if (data == null) return; QueueMessage(data); } private static void OnClose(object sender, CloseEventArgs closeEventArgs) { UpdateEvents -= HandleSending; Log.Info(string.IsNullOrEmpty(closeEventArgs.Reason) ? "Disconnected" : "Disconnected - " + closeEventArgs.Reason); } internal static void Send(string message, bool immediate = false) { if (!IsConnected) { Connect(); } if (immediate) { SendToPlugin(message); } else { Log.Debug("Queued message to send: " + message); _sendQueue.Enqueue(message); } } #if UNITY_2017_1_OR_NEWER private static void QueueMessage(IReadOnlyDictionary data) { #else private static void QueueMessage(IDictionary data) { #endif lock (_queueLock) { _messagesQueue.Enqueue(data); } } private static void HandleSending() { if (!IsConnected) return; if (_sendQueue.Count <= 0) return; SendToPlugin(_sendQueue.Dequeue()); } private static void SendToPlugin(string message) { Log.Debug("Sent: " + message); _socket.Send(message); } private static void HandleMessages() { lock (_queueLock) { if (_messagesQueue.Count <= 0) return; var data = _messagesQueue.Dequeue(); var action = data["action"].AsString; Log.Debug("Handling action: \"" + action + "\" with state: " + data["state"].AsInt); switch (action) { case "invoke-method": StreamDeckReflection.InvokeMethod(data); break; case "set-field-property": StreamDeckReflection.SetFieldOrProperty(data); break; case "play-mode": StreamDeckHelper.SwitchPlayMode(); break; case "pause-mode": StreamDeckHelper.SwitchPauseMode(); break; case "execute-menu": JsonData value; var path = (data["settings"].AsData.TryGetValue("path", out value) ? value : new JsonData(string.Empty)).AsString; StreamDeckHelper.ExecuteMenu(path); break; case "dial-invoke": StreamDeckReflection.InvokeDial(data); break; case "scripted": StreamDeckReflection.InvokeMethod(data); break; case "connected-devices": var devices = data["settings"].AsData; foreach (var device in devices) { Log.Info("Device named \"" + device.Value.AsData["name"].AsString + "\" ready"); } break; default: Log.Debug("Unknown action: \"" + action + "\""); break; } } } } }