private void OnWebsocketLog(LogData data, string arg2)
 {
     TraceOutput?.Invoke(this, new LogEventArgs {
         Text = data.Message + " [arg2]"
     });
 }
 private void LogTrace(string log) => TraceOutput?.Invoke(this, new LogEventArgs {
     Text = log
 });
        private void OnMessage(object sender, MessageEventArgs e)
        {
            if (!e.IsText && !e.Data.IsValidJson())
            {
                return;
            }

            var json = JToken.Parse(e.Data);

            if (json.IsAuthMessage())
            {
                InfoOutput?.Invoke(this, new LogEventArgs {
                    Text = $"Authorize message: {e.Data.ToPrettyJson()}"
                });
                return;
            }

            if (json.IsResult())
            {
                // Isn't an event, log and exit.
                DebugOutput?.Invoke(this, new LogEventArgs {
                    Text = $"Result message: {e.Data.ToPrettyJson()}"
                });
                return;
            }

            if (!json.IsEvent())
            {
                // Isn't an event! And event's are what we're working with.
                WarnOutput?.Invoke(this, new LogEventArgs {
                    Text = $"Unsupported message (not an 'event'): {e.Data.ToPrettyJson()}"
                });
                return;
            }

            var entId       = json.ExtractEntityId().ToLowerInvariant();
            var matchedApps = _apps.FindApps(entId);

            if (matchedApps.Count == 0)
            {
                // No matched apps, log and exit.
                if (EncounteredEntityIdsWithoutSubscription.Add(entId))
                {
                    TraceOutput?.Invoke(this, new LogEventArgs {
                        Text = $"First time encounter of message with an EntityId that we're not listening on: {entId}"
                    });
                }
                return;
            }

            // Found matched apps! Log and determine which type
            InfoOutput?.Invoke(this, new LogEventArgs {
                Text = e.Data.ToPrettyJson()
            });
            var eventData = new EventData {
                EntityId = entId
            };

            if (json.IsClickEvent())
            {
                eventData.ClickData = new Click {
                    ClickType = (string)json["event"]["data"]["click_type"]
                };
            }

            if (json.IsStateChangeEvent())
            {
                //entity_boolean doesn't have a "last_triggered" attribute.
                if (!entId.Contains("input_boolean."))
                {
                    if (!json.HasNewStateWithLastTriggered())
                    {
                        return; // Irrelevant event, we need new states that has "last time triggered" otherwise it might be an event provoked by reloading Hass. Unsure about this.
                    }
                }
                if (!json.IsTheMostRelevantStateChangeMessage())
                {
                    return; // Is most probably a 'duped' event, throw it away ..
                }
                if (!json.HasNewState())
                {
                    return; // Irrelevant event, we need new states only ..
                }
                var rawGraph    = JsonConvert.DeserializeObject <HassEventRawModel>(e.Data);
                var stateChange = new StateChanged();
                stateChange.NewState      = [email protected]_state?.state;
                stateChange.OldState      = [email protected]_state?.state;
                stateChange.Attributes    = JsonConvert.DeserializeObject <Dictionary <string, object> >(([email protected]_state ?? [email protected]_state ?? new StateRaw()).attributes.ToString());
                eventData.StateChangeData = stateChange;
            }

            foreach (var hassApp in matchedApps.Where(p => p.IsExecuting == false))
            {
                hassApp.IsExecuting = true;
                hassApp
                .ExecuteAsync(eventData, e.Data)
                .ContinueWith(p =>
                {
                    // Only on exception: Raise event..
                    var ex = p.Exception?.InnerExceptions?.FirstOrDefault() ?? p.Exception;
                    ErrorOutput?.Invoke(this, new LogEventArgs {
                        Text = ex?.Message, Exception = ex
                    });
                }, TaskContinuationOptions.OnlyOnFaulted)
                .ContinueWith(p =>
                {
                    hassApp.IsExecuting = false;
                }, TaskContinuationOptions.None);
            }
        }