Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor listeners and controllers as ABCs #450

Merged
merged 2 commits into from
Jan 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion pychromecast/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"""
Provides controllers to handle specific namespaces in Chromecast communication.
"""
import abc
import logging

from ..error import UnsupportedNamespace, ControllerNotRegistered


class BaseController:
class BaseController(abc.ABC):
""" ABC for namespace controllers. """

def __init__(self, namespace, supporting_app_id=None, target_platform=False):
Expand Down
11 changes: 10 additions & 1 deletion pychromecast/controllers/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Provides a controller for controlling the default media players
on the Chromecast.
"""
import abc
from datetime import datetime
import logging

Expand Down Expand Up @@ -310,6 +311,14 @@ def __repr__(self):
return "<MediaStatus {}>".format(info)


class MediaStatusListener(abc.ABC):
"""Listener for receiving media status events."""

@abc.abstractmethod
async def new_media_status(self, status: MediaStatus):
"""Updated media status."""


# pylint: disable=too-many-public-methods
class MediaController(BaseController):
""" Controller to interact with Google media namespace. """
Expand Down Expand Up @@ -341,7 +350,7 @@ def receive_message(self, message, data: dict):

return False

def register_status_listener(self, listener):
def register_status_listener(self, listener: MediaStatusListener):
"""Register a listener for new media statuses. A new status will
call listener.new_media_status(status)"""
self._status_listeners.append(listener)
Expand Down
47 changes: 45 additions & 2 deletions pychromecast/controllers/multizone.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
"""
Controller to monitor audio group members.
"""
import abc
import logging

from . import BaseController
from .media import MediaStatus
from ..socket_client import (
CONNECTION_STATUS_CONNECTED,
CONNECTION_STATUS_DISCONNECTED,
CONNECTION_STATUS_LOST,
CastStatus,
)

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -90,6 +93,28 @@ def multizone_status_received(self):
"""Handle reception of audio group status."""


class MultiZoneManagerListener(abc.ABC):
"""Listener for receiving audio group events for a cast device."""

@abc.abstractmethod
async def added_to_multizone(self, group_uuid: str):
"""The cast has been added to group identified by group_uuid."""

@abc.abstractmethod
async def removed_from_multizone(self, group_uuid: str):
"""The cast has been removed from group identified by group_uuid."""

@abc.abstractmethod
async def multizone_new_media_status(
self, group_uuid: str, media_status: MediaStatus
):
"""The group identified by group_uuid, of which the cast is a member, has new media status."""

@abc.abstractmethod
async def multizone_new_cast_status(self, group_uuid: str, cast_status: CastStatus):
"""The group identified by group_uuid, of which the cast is a member, has new status."""


class MultizoneManager:
""" Manage audio groups. """

Expand Down Expand Up @@ -117,7 +142,7 @@ def remove_multizone(self, group_uuid):
for member in self._casts.values():
member["groups"].discard(group_uuid)

def register_listener(self, member_uuid, listener):
def register_listener(self, member_uuid, listener: MultiZoneManagerListener):
"""Register a listener for audio group changes of cast uuid.
On update will call:
listener.added_to_multizone(group_uuid)
Expand Down Expand Up @@ -147,6 +172,24 @@ def get_multizone_mediacontroller(self, group_uuid):
return self._groups[str(group_uuid)]["chromecast"].media_controller


class MultiZoneControllerListener(abc.ABC):
"""Listener for receiving audio group events."""

@abc.abstractmethod
async def multizone_member_added(self, group_uuid: str):
"""The cast has been added to group identified by group_uuid."""

@abc.abstractmethod
async def multizone_member_removed(self, group_uuid: str):
"""The cast has been removed from group identified by group_uuid."""

@abc.abstractmethod
async def multizone_status_received(
self, group_uuid: str, media_status: MediaStatus
):
"""The group identified by group_uuid, of which the cast is a member, has new status."""


class MultizoneController(BaseController):
""" Controller to monitor audio group members. """

Expand Down Expand Up @@ -183,7 +226,7 @@ def _remove_member(self, uuid):
for listener in list(self._status_listeners):
listener.multizone_member_removed(uuid)

def register_listener(self, listener):
def register_listener(self, listener: MultiZoneControllerListener):
"""Register a listener for audio group changes. On update will call:
listener.multizone_member_added(uuid)
listener.multizone_member_removed(uuid)
Expand Down
33 changes: 29 additions & 4 deletions pychromecast/socket_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
# Pylint does not understand the protobuf objects correctly
# pylint: disable=no-member, too-many-lines

import abc
import errno
import json
import logging
Expand Down Expand Up @@ -162,6 +163,30 @@ def _is_ssl_timeout(exc):
LaunchFailure = namedtuple("LaunchStatus", ["reason", "app_id", "request_id"])


class CastStatusListener(abc.ABC):
"""Listener for receiving cast status events."""

@abc.abstractmethod
async def new_cast_status(self, status: CastStatus):
"""Updated cast status."""


class ConnectionStatusListener(abc.ABC):
"""Listener for receiving connection status events."""

@abc.abstractmethod
async def new_connection_status(self, status: ConnectionStatus):
"""Updated connection status."""


class LaunchErrorListener(abc.ABC):
"""Listener for receiving launch error events."""

@abc.abstractmethod
async def new_launch_error(self, status: LaunchFailure):
"""Launch error."""


# pylint: disable=too-many-instance-attributes
class SocketClient(threading.Thread):
"""
Expand Down Expand Up @@ -476,7 +501,7 @@ def disconnect(self):
# The socketpair may already be closed during shutdown, ignore it
pass

def register_handler(self, handler):
def register_handler(self, handler: BaseController):
""" Register a new namespace handler. """
self._handlers[handler.namespace] = handler

Expand Down Expand Up @@ -950,7 +975,7 @@ def send_app_message(
callback_function_param,
)

def register_connection_listener(self, listener):
def register_connection_listener(self, listener: ConnectionStatusListener):
"""Register a connection listener for when the socket connection
changes. Listeners will be called with
listener.new_connection_status(status)"""
Expand Down Expand Up @@ -1149,13 +1174,13 @@ def receive_message(self, message, data: dict):

return False

def register_status_listener(self, listener):
def register_status_listener(self, listener: CastStatusListener):
"""Register a status listener for when a new Chromecast status
has been received. Listeners will be called with
listener.new_cast_status(status)"""
self._status_listeners.append(listener)

def register_launch_error_listener(self, listener):
def register_launch_error_listener(self, listener: LaunchErrorListener):
"""Register a listener for when a new launch error message
has been received. Listeners will be called with
listener.new_launch_error(launch_failure)"""
Expand Down
1 change: 1 addition & 0 deletions pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ reports=no
disable=
format,
locally-disabled,
too-few-public-methods,
too-many-arguments,
too-many-instance-attributes,
too-many-public-methods,
Expand Down