private async Task OnMessage(string msg, CancellationToken ct)
        {
            if (!msg.IsValidJson(out var json))
            {
                return;
            }

            var message = new WebSocketMessage(json);

            switch (message.MessageType)
            {
            case "auth_required":
                LogInfo("Authorization requested. Sending access token...");
                await SendWs(new AuthCommand(_accessToken), ct);

                return;

            case "auth_ok":
                LogInfo("Authorization completed, subscribing to events...");
                await SubscribeToEvents(ct);

                return;

            case "result":
                // Isn't an event, log and exit.
                LogDebug($"Result message: {msg.ToPrettyJson()}");
                return;

            case "event":
                break;                         // continue processing

            default:
            {
                // Isn't an event! And event's are what we're working with.
                LogWarn($"Unsupported message (not an 'event'): {msg.ToPrettyJson()}");
                return;
            }
            }

            var entId       = message.EventEntityId;
            var matchedApps = _apps
                              .Where(a => a.MatchEntity(entId))
                              .ToArray();

            if (!matchedApps.Any())
            {
                // No matched apps, log and exit.
                if (EncounteredEntityIdsWithoutSubscription.Add(entId))
                {
                    LogTrace($"First time encounter of message with an EntityId that we're not listening on: {entId}");
                }

                return;
            }

            // Found matched apps! Log and determine which type
            LogInfo(msg.ToPrettyJson());
            Click        clickData        = null;
            StateChanged stateChangedData = null;

            var eventType = message.EventType;

            switch (eventType)
            {
            case "click":
            {
                clickData = new Click {
                    ClickType = (string)json["event"]["data"]["click_type"]
                };
                break;
            }

            case "state_changed":
            {
                //entity_boolean doesn't have a "last_triggered" attribute.
                //if (!entId.Contains("input_boolean."))
                //{
                //    if (!message.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 (!message.IsTheMostRelevantStateChangeEvent)
                {
                    return;                                     // Is most probably a 'duped' event, throw it away ..
                }

                if (!message.HasNewState)
                {
                    return;                                     // Irrelevant event, we need new states only ..
                }

                stateChangedData = message.DeserializeStateChanged();
                break;
            }
            }

            var eventData = new EventData(entId, stateChangedData, clickData, msg);

            foreach (var hassApp in matchedApps)
            {
                hassApp.DispatchEvent(eventData);
            }
        }