# Buggy code
async def ws_handler(message_obj):
    """Handle websocket messages.

    This allows push notifications from Alexa to update last_called
    and media state.
    """

    command = (
        message_obj.json_payload["command"]
        if isinstance(message_obj.json_payload, dict)
        and "command" in message_obj.json_payload
        else None
    )
    json_payload = (
        message_obj.json_payload["payload"]
        if isinstance(message_obj.json_payload, dict)
        and "payload" in message_obj.json_payload
        else None
    )
    existing_serials = _existing_serials(hass, login_obj)
    seen_commands = hass.data[DATA_ALEXAMEDIA]["accounts"][email]["websocket_commands"]
    if command and json_payload:
        _LOGGER.debug(
            "%s: Received websocket command: %s : %s",
            hide_email(email),
            command,
            hide_serial(json_payload),
        )
        serial = None
        command_time = time.time()
        if command not in seen_commands:
            _LOGGER.debug("Adding %s to seen_commands: %s", command, seen_commands)
        seen_commands[command] = command_time

        if (
            "dopplerId" in json_payload
            and "deviceSerialNumber" in json_payload["dopplerId"]
        ):
            serial = json_payload["dopplerId"]["deviceSerialNumber"]
        elif (
            "key" in json_payload
            and "entryId" in json_payload["key"]
            and json_payload["key"]["entryId"].find("#") != -1
        ):
            serial = (json_payload["key"]["entryId"]).split("#")[2]
            json_payload["key"]["serialNumber"] = serial
        else:
            serial = None
        if command == "PUSH_ACTIVITY":
            #  Last_Alexa Updated
            last_called = {
                "serialNumber": serial,
                "timestamp": json_payload["timestamp"],
            }
            if serial and serial in existing_serials:
                await update_last_called(login_obj, last_called)
            async_dispatcher_send(
                hass,
                f"{DOMAIN}_{hide_email(email)}"[0:32],
                {"push_activity": json_payload},
            )
        elif command in (
            "PUSH_AUDIO_PLAYER_STATE",
            "PUSH_MEDIA_CHANGE",
            "PUSH_MEDIA_PROGRESS_CHANGE",
        ):
            # Player update/ Push_media from tune_in
            if serial and serial in existing_serials:
                _LOGGER.debug("Updating media_player: %s", hide_serial(json_payload))
                async_dispatcher_send(
                    hass,
                    f"{DOMAIN}_{hide_email(email)}"[0:32],
                    {"player_state": json_payload},
                )
        elif command == "PUSH_VOLUME_CHANGE":
            # Player volume update
            if serial and serial in existing_serials:
                _LOGGER.debug(
                    "Updating media_player volume: %s", hide_serial(json_payload)
                )
                async_dispatcher_send(
                    hass,
                    f"{DOMAIN}_{hide_email(email)}"[0:32],
                    {"player_state": json_payload},
                )
        elif command in (
            "PUSH_DOPPLER_CONNECTION_CHANGE",
            "PUSH_EQUALIZER_STATE_CHANGE",
        ):
            # Player availability update
            if serial and serial in existing_serials:
                _LOGGER.debug(
                    "Updating media_player availability %s",
                    hide_serial(json_payload),
                )
                async_dispatcher_send(
                    hass,
                    f"{DOMAIN}_{hide_email(email)}"[0:32],
                    {"player_state": json_payload},
                )
        elif command == "PUSH_BLUETOOTH_STATE_CHANGE":
            # Player bluetooth update
            bt_event = json_payload["bluetoothEvent"]
            bt_success = json_payload["bluetoothEventSuccess"]
            if (
                serial
                and serial in existing_serials
                and bt_success
                and bt_event
                and bt_event in ["DEVICE_CONNECTED", "DEVICE_DISCONNECTED"]
            ):
                _LOGGER.debug(
                    "Updating media_player bluetooth %s", hide_serial(json_payload)
                )
                bluetooth_state = await update_bluetooth_state(login_obj, serial)
                # _LOGGER.debug("bluetooth_state %s",
                #               hide_serial(bluetooth_state))
                if bluetooth_state:
                    async_dispatcher_send(
                        hass,
                        f"{DOMAIN}_{hide_email(email)}"[0:32],
                        {"bluetooth_change": bluetooth_state},
                    )
        elif command == "PUSH_MEDIA_QUEUE_CHANGE":
            # Player availability update
            if serial and serial in existing_serials:
                _LOGGER.debug(
                    "Updating media_player queue %s", hide_serial(json_payload)
                )
                async_dispatcher_send(
                    hass,
                    f"{DOMAIN}_{hide_email(email)}"[0:32],
                    {"queue_state": json_payload},
                )
        elif command == "PUSH_NOTIFICATION_CHANGE":
            # Player update
            await process_notifications(login_obj)
            if serial and serial in existing_serials:
                _LOGGER.debug(
                    "Updating mediaplayer notifications: %s",
                    hide_serial(json_payload),
                )
                async_dispatcher_send(
                    hass,
                    f"{DOMAIN}_{hide_email(email)}"[0:32],
                    {"notification_update": json_payload},
                )
        elif command in [
            "PUSH_DELETE_DOPPLER_ACTIVITIES",  # delete Alexa history
            "PUSH_LIST_ITEM_CHANGE",  # update shopping list
            "PUSH_CONTENT_FOCUS_CHANGE",  # likely prime related refocus
            "PUSH_DEVICE_SETUP_STATE_CHANGE",  # likely device changes mid setup
        ]:
            pass
        else:
            _LOGGER.warning(
                "Unhandled command: %s with data %s. Please report at %s",
                command,
                hide_serial(json_payload),
                ISSUE_URL,
            )
        if serial in existing_serials:
            history = hass.data[DATA_ALEXAMEDIA]["accounts"][email][
                "websocket_activity"
            ]["serials"].get(serial)
            if history is None or (
                history and command_time - history[len(history) - 1][1] > 2
            ):
                history = [(command, command_time)]
            else:
                history.append([command, command_time])
            hass.data[DATA_ALEXAMEDIA]["accounts"][email]["websocket_activity"][
                "serials"
            ][serial] = history
            events = []
            for old_command, old_command_time in history:
                if (
                    old_command in {"PUSH_VOLUME_CHANGE", "PUSH_EQUALIZER_STATE_CHANGE"}
                    and command_time - old_command_time < 0.25
                ):
                    events.append(
                        (old_command, round(command_time - old_command_time, 2))
                    )
                elif old_command in {"PUSH_AUDIO_PLAYER_STATE"}:
                    # There is a potential false positive generated during this event
                    events = []
            if len(events) >= 4:
                _LOGGER.debug(
                    "%s: Detected potential DND websocket change with %s events %s",
                    hide_serial(serial),
                    len(events),
                    events,
                )
                await update_dnd_state(login_obj)
        if (
            serial
            and serial not in existing_serials
            and serial
            not in (hass.data[DATA_ALEXAMEDIA]["accounts"][email]["excluded"].keys())
        ):
            _LOGGER.debug("Discovered new media_player %s", serial)
            (hass.data[DATA_ALEXAMEDIA]["accounts"][email]["new_devices"]) = True
            coordinator = hass.data[DATA_ALEXAMEDIA]["accounts"][email].get(
                "coordinator"
            )
            if coordinator:
                await coordinator.async_request_refresh()


# Traceback (KeyError)
2021-01-20 20:43:46 WARNING (MainThread) [alexapy.helpers] alexaapi.get_activities((<alexapy.alexalogin.AlexaLogin object at 0x7f6929707c70>, 10), {}): A connection error occured: An exception of type KeyError occurred. Arguments:
('activities',)
2021-01-20 20:43:46 WARNING (MainThread) [alexapy.helpers] alexaapi.get_last_device_serial((<alexapy.alexalogin.AlexaLogin object at 0x7f6929707c70>,), {}): An error occured accessing AlexaAPI: An exception of type AlexapyConnectionError
occurred. Arguments:
()
2021-01-20 20:43:46 ERROR (MainThread) [homeassistant] Error doing job: Task exception was never retrieved
Traceback (most recent call last):
File "/usr/local/lib/python3.8/site-packages/alexapy/helpers.py", line 141, in wrapper
return await func(*args, **kwargs)
File "/usr/local/lib/python3.8/site-packages/alexapy/alexaapi.py", line 1197, in get_activities
(await response.json(content_type=None))["activities"] if response else None
KeyError: 'activities'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/usr/local/lib/python3.8/site-packages/alexapy/alexawebsocket.py", line 144, in process_messages
await self.on_message(cast(bytes, msg.data))
File "/usr/local/lib/python3.8/site-packages/alexapy/alexawebsocket.py", line 226, in on_message
await self.msg_callback(message_obj)
File "/config/custom_components/alexa_media/__init__.py", line 750, in ws_handler
await update_last_called(login_obj, last_called)
File "/config/custom_components/alexa_media/helpers.py", line 162, in _catch_login_errors
result = await func(*args, **kwargs)
File "/config/custom_components/alexa_media/__init__.py", line 597, in update_last_called
last_called = await AlexaAPI.get_last_device_serial(login_obj)
File "/usr/local/lib/python3.8/site-packages/alexapy/helpers.py", line 141, in wrapper
return await func(*args, **kwargs)
File "/usr/local/lib/python3.8/site-packages/alexapy/alexaapi.py", line 1230, in get_last_device_serial
response = await AlexaAPI.get_activities(login, items)
File "/usr/local/lib/python3.8/site-packages/alexapy/helpers.py", line 151, in wrapper
raise AlexapyConnectionError from ex
alexapy.errors.AlexapyConnectionError

# Fixed code
async def ws_handler(message_obj):
    """Handle websocket messages.

    This allows push notifications from Alexa to update last_called
    and media state.
    """

    command = (
        message_obj.json_payload["command"]
        if isinstance(message_obj.json_payload, dict)
        and "command" in message_obj.json_payload
        else None
    )
    json_payload = (
        message_obj.json_payload["payload"]
        if isinstance(message_obj.json_payload, dict)
        and "payload" in message_obj.json_payload
        else None
    )
    existing_serials = _existing_serials(hass, login_obj)
    seen_commands = hass.data[DATA_ALEXAMEDIA]["accounts"][email]["websocket_commands"]
    if command and json_payload:
        _LOGGER.debug(
            "%s: Received websocket command: %s : %s",
            hide_email(email),
            command,
            hide_serial(json_payload),
        )
        serial = None
        command_time = time.time()
        if command not in seen_commands:
            _LOGGER.debug("Adding %s to seen_commands: %s", command, seen_commands)
        seen_commands[command] = command_time

        if (
            "dopplerId" in json_payload
            and "deviceSerialNumber" in json_payload["dopplerId"]
        ):
            serial = json_payload["dopplerId"]["deviceSerialNumber"]
        elif (
            "key" in json_payload
            and "entryId" in json_payload["key"]
            and json_payload["key"]["entryId"].find("#") != -1
        ):
            serial = (json_payload["key"]["entryId"]).split("#")[2]
            json_payload["key"]["serialNumber"] = serial
        else:
            serial = None
        if command == "PUSH_ACTIVITY":
            #  Last_Alexa Updated
            last_called = {
                "serialNumber": serial,
                "timestamp": json_payload["timestamp"],
            }
            try:
                if serial and serial in existing_serials:
                    await update_last_called(login_obj, last_called)
                async_dispatcher_send(
                    hass,
                    f"{DOMAIN}_{hide_email(email)}"[0:32],
                    {"push_activity": json_payload},
                )
            except AlexapyConnectionError:
                # Catch case where activities doesn't report valid json
                pass
        elif command in (
            "PUSH_AUDIO_PLAYER_STATE",
            "PUSH_MEDIA_CHANGE",
            "PUSH_MEDIA_PROGRESS_CHANGE",
        ):
            # Player update/ Push_media from tune_in
            if serial and serial in existing_serials:
                _LOGGER.debug("Updating media_player: %s", hide_serial(json_payload))
                async_dispatcher_send(
                    hass,
                    f"{DOMAIN}_{hide_email(email)}"[0:32],
                    {"player_state": json_payload},
                )
        elif command == "PUSH_VOLUME_CHANGE":
            # Player volume update
            if serial and serial in existing_serials:
                _LOGGER.debug(
                    "Updating media_player volume: %s", hide_serial(json_payload)
                )
                async_dispatcher_send(
                    hass,
                    f"{DOMAIN}_{hide_email(email)}"[0:32],
                    {"player_state": json_payload},
                )
        elif command in (
            "PUSH_DOPPLER_CONNECTION_CHANGE",
            "PUSH_EQUALIZER_STATE_CHANGE",
        ):
            # Player availability update
            if serial and serial in existing_serials:
                _LOGGER.debug(
                    "Updating media_player availability %s",
                    hide_serial(json_payload),
                )
                async_dispatcher_send(
                    hass,
                    f"{DOMAIN}_{hide_email(email)}"[0:32],
                    {"player_state": json_payload},
                )
        elif command == "PUSH_BLUETOOTH_STATE_CHANGE":
            # Player bluetooth update
            bt_event = json_payload["bluetoothEvent"]
            bt_success = json_payload["bluetoothEventSuccess"]
            if (
                serial
                and serial in existing_serials
                and bt_success
                and bt_event
                and bt_event in ["DEVICE_CONNECTED", "DEVICE_DISCONNECTED"]
            ):
                _LOGGER.debug(
                    "Updating media_player bluetooth %s", hide_serial(json_payload)
                )
                bluetooth_state = await update_bluetooth_state(login_obj, serial)
                # _LOGGER.debug("bluetooth_state %s",
                #               hide_serial(bluetooth_state))
                if bluetooth_state:
                    async_dispatcher_send(
                        hass,
                        f"{DOMAIN}_{hide_email(email)}"[0:32],
                        {"bluetooth_change": bluetooth_state},
                    )
        elif command == "PUSH_MEDIA_QUEUE_CHANGE":
            # Player availability update
            if serial and serial in existing_serials:
                _LOGGER.debug(
                    "Updating media_player queue %s", hide_serial(json_payload)
                )
                async_dispatcher_send(
                    hass,
                    f"{DOMAIN}_{hide_email(email)}"[0:32],
                    {"queue_state": json_payload},
                )
        elif command == "PUSH_NOTIFICATION_CHANGE":
            # Player update
            await process_notifications(login_obj)
            if serial and serial in existing_serials:
                _LOGGER.debug(
                    "Updating mediaplayer notifications: %s",
                    hide_serial(json_payload),
                )
                async_dispatcher_send(
                    hass,
                    f"{DOMAIN}_{hide_email(email)}"[0:32],
                    {"notification_update": json_payload},
                )
        elif command in [
            "PUSH_DELETE_DOPPLER_ACTIVITIES",  # delete Alexa history
            "PUSH_LIST_ITEM_CHANGE",  # update shopping list
            "PUSH_CONTENT_FOCUS_CHANGE",  # likely prime related refocus
            "PUSH_DEVICE_SETUP_STATE_CHANGE",  # likely device changes mid setup
        ]:
            pass
        else:
            _LOGGER.warning(
                "Unhandled command: %s with data %s. Please report at %s",
                command,
                hide_serial(json_payload),
                ISSUE_URL,
            )
        if serial in existing_serials:
            history = hass.data[DATA_ALEXAMEDIA]["accounts"][email][
                "websocket_activity"
            ]["serials"].get(serial)
            if history is None or (
                history and command_time - history[len(history) - 1][1] > 2
            ):
                history = [(command, command_time)]
            else:
                history.append([command, command_time])
            hass.data[DATA_ALEXAMEDIA]["accounts"][email]["websocket_activity"][
                "serials"
            ][serial] = history
            events = []
            for old_command, old_command_time in history:
                if (
                    old_command in {"PUSH_VOLUME_CHANGE", "PUSH_EQUALIZER_STATE_CHANGE"}
                    and command_time - old_command_time < 0.25
                ):
                    events.append(
                        (old_command, round(command_time - old_command_time, 2))
                    )
                elif old_command in {"PUSH_AUDIO_PLAYER_STATE"}:
                    # There is a potential false positive generated during this event
                    events = []
            if len(events) >= 4:
                _LOGGER.debug(
                    "%s: Detected potential DND websocket change with %s events %s",
                    hide_serial(serial),
                    len(events),
                    events,
                )
                await update_dnd_state(login_obj)
        if (
            serial
            and serial not in existing_serials
            and serial
            not in (hass.data[DATA_ALEXAMEDIA]["accounts"][email]["excluded"].keys())
        ):
            _LOGGER.debug("Discovered new media_player %s", serial)
            (hass.data[DATA_ALEXAMEDIA]["accounts"][email]["new_devices"]) = True
            coordinator = hass.data[DATA_ALEXAMEDIA]["accounts"][email].get(
                "coordinator"
            )
            if coordinator:
                await coordinator.async_request_refresh()