<s> """<STR_LIT>""" <EOL> import logging <EOL> import select <EOL> import socket <EOL> import ssl <EOL> import json <EOL> import threading <EOL> import time <EOL> from collections import namedtuple <EOL> from struct import pack , unpack <EOL> import sys <EOL> from . import cast_channel_pb2 <EOL> from . dial import CAST_TYPE_CHROMECAST , CAST_TYPE_AUDIO , CAST_TYPE_GROUP <EOL> from . controllers import BaseController <EOL> from . controllers . media import MediaController <EOL> from . error import ( <EOL> ChromecastConnectionError , <EOL> UnsupportedNamespace , <EOL> NotConnected , <EOL> PyChromecastStopped , <EOL> LaunchError , <EOL> ) <EOL> NS_CONNECTION = '<STR_LIT>' <EOL> NS_RECEIVER = '<STR_LIT>' <EOL> NS_HEARTBEAT = '<STR_LIT>' <EOL> PLATFORM_DESTINATION_ID = "<STR_LIT>" <EOL> MESSAGE_TYPE = '<STR_LIT:type>' <EOL> TYPE_PING = "<STR_LIT>" <EOL> TYPE_RECEIVER_STATUS = "<STR_LIT>" <EOL> TYPE_PONG = "<STR_LIT>" <EOL> TYPE_CONNECT = "<STR_LIT>" <EOL> TYPE_CLOSE = "<STR_LIT>" <EOL> TYPE_GET_STATUS = "<STR_LIT>" <EOL> TYPE_LAUNCH = "<STR_LIT>" <EOL> TYPE_LAUNCH_ERROR = "<STR_LIT>" <EOL> TYPE_LOAD = "<STR_LIT>" <EOL> CONNECTION_STATUS_CONNECTING = "<STR_LIT>" <EOL> CONNECTION_STATUS_CONNECTED = "<STR_LIT>" <EOL> CONNECTION_STATUS_DISCONNECTED = "<STR_LIT>" <EOL> CONNECTION_STATUS_FAILED = "<STR_LIT>" <EOL> CONNECTION_STATUS_LOST = "<STR_LIT>" <EOL> APP_ID = '<STR_LIT>' <EOL> REQUEST_ID = "<STR_LIT>" <EOL> SESSION_ID = "<STR_LIT>" <EOL> ERROR_REASON = '<STR_LIT>' <EOL> HB_PING_TIME = <NUM_LIT:10> <EOL> HB_PONG_TIME = <NUM_LIT:10> <EOL> POLL_TIME = <NUM_LIT:5> <EOL> TIMEOUT_TIME = <NUM_LIT:30> <EOL> RETRY_TIME = <NUM_LIT:5> <EOL> class InterruptLoop ( Exception ) : <EOL> """<STR_LIT>""" <EOL> pass <EOL> def _json_from_message ( message ) : <EOL> """<STR_LIT>""" <EOL> return json . loads ( message . payload_utf8 ) <EOL> def _message_to_string ( message , data = None ) : <EOL> """<STR_LIT>""" <EOL> if data is None : <EOL> data = _json_from_message ( message ) <EOL> return "<STR_LIT>" . format ( <EOL> message . namespace , message . source_id , message . destination_id , data ) <EOL> if sys . version_info >= ( <NUM_LIT:3> , <NUM_LIT:0> ) : <EOL> def _json_to_payload ( data ) : <EOL> """<STR_LIT>""" <EOL> return json . dumps ( data , ensure_ascii = False ) . encode ( "<STR_LIT:utf8>" ) <EOL> else : <EOL> def _json_to_payload ( data ) : <EOL> """<STR_LIT>""" <EOL> return json . dumps ( data , ensure_ascii = False ) <EOL> def _is_ssl_timeout ( exc ) : <EOL> """<STR_LIT>""" <EOL> return exc . message in ( "<STR_LIT>" , <EOL> "<STR_LIT>" , <EOL> "<STR_LIT>" ) <EOL> NetworkAddress = namedtuple ( '<STR_LIT>' , <EOL> [ '<STR_LIT:address>' , '<STR_LIT:port>' ] ) <EOL> ConnectionStatus = namedtuple ( '<STR_LIT>' , <EOL> [ '<STR_LIT:status>' , '<STR_LIT:address>' ] ) <EOL> CastStatus = namedtuple ( '<STR_LIT>' , <EOL> [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , <EOL> '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , <EOL> '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , <EOL> '<STR_LIT>' ] ) <EOL> LaunchFailure = namedtuple ( '<STR_LIT>' , <EOL> [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ] ) <EOL> class SocketClient ( threading . Thread ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , host , port = None , cast_type = CAST_TYPE_CHROMECAST , <EOL> ** kwargs ) : <EOL> tries = kwargs . pop ( '<STR_LIT>' , None ) <EOL> timeout = kwargs . pop ( '<STR_LIT>' , None ) <EOL> retry_wait = kwargs . pop ( '<STR_LIT>' , None ) <EOL> super ( SocketClient , self ) . __init__ ( ) <EOL> self . daemon = True <EOL> self . logger = logging . getLogger ( __name__ ) <EOL> self . _force_recon = False <EOL> self . cast_type = cast_type <EOL> self . tries = tries <EOL> self . timeout = timeout or TIMEOUT_TIME <EOL> self . retry_wait = retry_wait or RETRY_TIME <EOL> self . host = host <EOL> self . port = port or <NUM_LIT> <EOL> self . source_id = "<STR_LIT>" <EOL> self . stop = threading . Event ( ) <EOL> self . app_namespaces = [ ] <EOL> self . destination_id = None <EOL> self . session_id = None <EOL> self . _request_id = <NUM_LIT:0> <EOL> self . _request_callbacks = { } <EOL> self . _open_channels = [ ] <EOL> self . connecting = True <EOL> self . socket = None <EOL> self . _handlers = { } <EOL> self . _connection_listeners = [ ] <EOL> self . receiver_controller = ReceiverController ( cast_type ) <EOL> self . media_controller = MediaController ( ) <EOL> self . heartbeat_controller = HeartbeatController ( ) <EOL> self . register_handler ( self . heartbeat_controller ) <EOL> self . register_handler ( ConnectionController ( ) ) <EOL> self . register_handler ( self . receiver_controller ) <EOL> self . register_handler ( self . media_controller ) <EOL> self . receiver_controller . register_status_listener ( self ) <EOL> try : <EOL> self . initialize_connection ( ) <EOL> except ChromecastConnectionError : <EOL> self . _report_connection_status ( <EOL> ConnectionStatus ( CONNECTION_STATUS_DISCONNECTED , <EOL> NetworkAddress ( self . host , self . port ) ) ) <EOL> raise <EOL> def initialize_connection ( self ) : <EOL> """<STR_LIT>""" <EOL> tries = self . tries <EOL> if self . socket is not None : <EOL> self . socket . close ( ) <EOL> self . socket = None <EOL> for callback in self . _request_callbacks . values ( ) : <EOL> callback [ '<STR_LIT>' ] . set ( ) <EOL> self . app_namespaces = [ ] <EOL> self . destination_id = None <EOL> self . session_id = None <EOL> self . _request_id = <NUM_LIT:0> <EOL> self . _request_callbacks = { } <EOL> self . _open_channels = [ ] <EOL> self . connecting = True <EOL> retry_log_fun = self . logger . exception <EOL> while not self . stop . is_set ( ) and ( tries is None or tries > <NUM_LIT:0> ) : <EOL> try : <EOL> self . socket = ssl . wrap_socket ( socket . socket ( ) ) <EOL> self . socket . settimeout ( self . timeout ) <EOL> self . _report_connection_status ( <EOL> ConnectionStatus ( CONNECTION_STATUS_CONNECTING , <EOL> NetworkAddress ( self . host , self . port ) ) ) <EOL> self . socket . connect ( ( self . host , self . port ) ) <EOL> self . connecting = False <EOL> self . _force_recon = False <EOL> self . _report_connection_status ( <EOL> ConnectionStatus ( CONNECTION_STATUS_CONNECTED , <EOL> NetworkAddress ( self . host , self . port ) ) ) <EOL> self . receiver_controller . update_status ( ) <EOL> self . heartbeat_controller . ping ( ) <EOL> self . heartbeat_controller . reset ( ) <EOL> self . logger . debug ( "<STR_LIT>" ) <EOL> break <EOL> except socket . error : <EOL> self . connecting = True <EOL> if self . stop . is_set ( ) : <EOL> self . logger . exception ( <EOL> "<STR_LIT>" ) <EOL> raise ChromecastConnectionError ( "<STR_LIT>" ) <EOL> self . _report_connection_status ( <EOL> ConnectionStatus ( CONNECTION_STATUS_FAILED , <EOL> NetworkAddress ( self . host , self . port ) ) ) <EOL> retry_log_fun ( "<STR_LIT>" , <EOL> self . retry_wait ) <EOL> retry_log_fun = self . logger . debug <EOL> time . sleep ( self . retry_wait ) <EOL> if tries : <EOL> tries -= <NUM_LIT:1> <EOL> else : <EOL> self . stop . set ( ) <EOL> self . logger . error ( "<STR_LIT>" ) <EOL> raise ChromecastConnectionError ( "<STR_LIT>" ) <EOL> def disconnect ( self ) : <EOL> """<STR_LIT>""" <EOL> self . stop . set ( ) <EOL> def register_handler ( self , handler ) : <EOL> """<STR_LIT>""" <EOL> self . _handlers [ handler . namespace ] = handler <EOL> handler . registered ( self ) <EOL> def new_cast_status ( self , cast_status ) : <EOL> """<STR_LIT>""" <EOL> new_channel = self . destination_id != cast_status . transport_id <EOL> if new_channel : <EOL> self . _disconnect_channel ( self . destination_id ) <EOL> self . app_namespaces = cast_status . namespaces <EOL> self . destination_id = cast_status . transport_id <EOL> self . session_id = cast_status . session_id <EOL> if new_channel : <EOL> for namespace in self . app_namespaces : <EOL> if namespace in self . _handlers : <EOL> self . _ensure_channel_connected ( self . destination_id ) <EOL> self . _handlers [ namespace ] . channel_connected ( ) <EOL> def _gen_request_id ( self ) : <EOL> """<STR_LIT>""" <EOL> self . _request_id += <NUM_LIT:1> <EOL> return self . _request_id <EOL> @ property <EOL> def is_connected ( self ) : <EOL> """<STR_LIT>""" <EOL> return not self . connecting <EOL> @ property <EOL> def is_stopped ( self ) : <EOL> """<STR_LIT>""" <EOL> return self . stop . is_set ( ) <EOL> def run ( self ) : <EOL> """<STR_LIT>""" <EOL> self . heartbeat_controller . reset ( ) <EOL> self . _force_recon = False <EOL> while not self . stop . is_set ( ) : <EOL> try : <EOL> if not self . _check_connection ( ) : <EOL> continue <EOL> except ChromecastConnectionError : <EOL> break <EOL> can_read , _ , _ = select . select ( [ self . socket ] , [ ] , [ ] , POLL_TIME ) <EOL> message = data = None <EOL> if self . socket in can_read and not self . _force_recon : <EOL> try : <EOL> message = self . _read_message ( ) <EOL> except InterruptLoop as exc : <EOL> if self . stop . is_set ( ) : <EOL> self . logger . info ( <EOL> "<STR_LIT>" ) <EOL> break <EOL> else : <EOL> self . logger . exception ( <EOL> "<STR_LIT>" , <EOL> exc ) <EOL> break <EOL> except ssl . SSLError as exc : <EOL> if exc . errno == ssl . SSL_ERROR_EOF : <EOL> if self . stop . is_set ( ) : <EOL> break <EOL> raise <EOL> except socket . error : <EOL> self . _force_recon = True <EOL> self . logger . info ( '<STR_LIT>' ) <EOL> else : <EOL> data = _json_from_message ( message ) <EOL> if not message : <EOL> continue <EOL> if self . stop . is_set ( ) : <EOL> break <EOL> self . _route_message ( message , data ) <EOL> if REQUEST_ID in data : <EOL> callback = self . _request_callbacks . pop ( data [ REQUEST_ID ] , None ) <EOL> if callback is not None : <EOL> event = callback [ '<STR_LIT>' ] <EOL> callback [ '<STR_LIT>' ] = data <EOL> event . set ( ) <EOL> self . _cleanup ( ) <EOL> def _check_connection ( self ) : <EOL> """<STR_LIT>""" <EOL> reset = False <EOL> if self . _force_recon : <EOL> self . logger . warning ( <EOL> "<STR_LIT>" ) <EOL> reset = True <EOL> elif self . heartbeat_controller . is_expired ( ) : <EOL> self . logger . warning ( "<STR_LIT>" ) <EOL> reset = True <EOL> if reset : <EOL> self . _report_connection_status ( <EOL> ConnectionStatus ( CONNECTION_STATUS_LOST , <EOL> NetworkAddress ( self . host , self . port ) ) ) <EOL> try : <EOL> self . initialize_connection ( ) <EOL> except ChromecastConnectionError : <EOL> self . stop . set ( ) <EOL> return False <EOL> return True <EOL> def _route_message ( self , message , data ) : <EOL> """<STR_LIT>""" <EOL> if message . namespace in self . _handlers : <EOL> if message . namespace != NS_HEARTBEAT : <EOL> self . logger . debug ( <EOL> "<STR_LIT>" , _message_to_string ( message , data ) ) <EOL> try : <EOL> handled = self . _handlers [ message . namespace ] . receive_message ( <EOL> message , data ) <EOL> if not handled : <EOL> if data . get ( REQUEST_ID ) not in self . _request_callbacks : <EOL> self . logger . debug ( <EOL> "<STR_LIT>" , <EOL> _message_to_string ( message , data ) ) <EOL> except Exception : <EOL> self . logger . exception ( <EOL> ( u"<STR_LIT>" <EOL> u"<STR_LIT>" ) , <EOL> type ( self . _handlers [ message . namespace ] ) . __name__ , <EOL> _message_to_string ( message , data ) ) <EOL> else : <EOL> self . logger . debug ( <EOL> "<STR_LIT>" , <EOL> _message_to_string ( message , data ) ) <EOL> def _cleanup ( self ) : <EOL> """<STR_LIT>""" <EOL> for channel in self . _open_channels : <EOL> try : <EOL> self . _disconnect_channel ( channel ) <EOL> except Exception : <EOL> pass <EOL> for handler in self . _handlers . values ( ) : <EOL> try : <EOL> handler . tear_down ( ) <EOL> except Exception : <EOL> pass <EOL> self . socket . close ( ) <EOL> self . _report_connection_status ( <EOL> ConnectionStatus ( CONNECTION_STATUS_DISCONNECTED , <EOL> NetworkAddress ( self . host , self . port ) ) ) <EOL> self . connecting = True <EOL> def _report_connection_status ( self , status ) : <EOL> """<STR_LIT>""" <EOL> for listener in self . _connection_listeners : <EOL> try : <EOL> self . logger . debug ( "<STR_LIT>" , <EOL> id ( listener ) , type ( listener ) . __name__ ) <EOL> listener . new_connection_status ( status ) <EOL> except Exception : <EOL> pass <EOL> def _read_bytes_from_socket ( self , msglen ) : <EOL> """<STR_LIT>""" <EOL> chunks = [ ] <EOL> bytes_recd = <NUM_LIT:0> <EOL> while bytes_recd < msglen : <EOL> if self . stop . is_set ( ) : <EOL> raise InterruptLoop ( "<STR_LIT>" ) <EOL> try : <EOL> chunk = self . socket . recv ( min ( msglen - bytes_recd , <NUM_LIT> ) ) <EOL> if chunk == b'<STR_LIT>' : <EOL> raise socket . error ( "<STR_LIT>" ) <EOL> chunks . append ( chunk ) <EOL> bytes_recd += len ( chunk ) <EOL> except socket . timeout : <EOL> continue <EOL> except ssl . SSLError as exc : <EOL> if _is_ssl_timeout ( exc ) : <EOL> continue <EOL> raise <EOL> return b'<STR_LIT>' . join ( chunks ) <EOL> def _read_message ( self ) : <EOL> """<STR_LIT>""" <EOL> payload_info = self . _read_bytes_from_socket ( <NUM_LIT:4> ) <EOL> read_len = unpack ( "<STR_LIT>" , payload_info ) [ <NUM_LIT:0> ] <EOL> payload = self . _read_bytes_from_socket ( read_len ) <EOL> message = cast_channel_pb2 . CastMessage ( ) <EOL> message . ParseFromString ( payload ) <EOL> return message <EOL> def send_message ( self , destination_id , namespace , data , <EOL> inc_session_id = False , wait_for_response = False , <EOL> no_add_request_id = False , force = False ) : <EOL> """<STR_LIT>""" <EOL> self . _ensure_channel_connected ( destination_id ) <EOL> request_id = None <EOL> if not no_add_request_id : <EOL> request_id = self . _gen_request_id ( ) <EOL> data [ REQUEST_ID ] = request_id <EOL> if inc_session_id : <EOL> data [ SESSION_ID ] = self . session_id <EOL> msg = cast_channel_pb2 . CastMessage ( ) <EOL> msg . protocol_version = msg . CASTV2_1_0 <EOL> msg . source_id = self . source_id <EOL> msg . destination_id = destination_id <EOL> msg . payload_type = cast_channel_pb2 . CastMessage . STRING <EOL> msg . namespace = namespace <EOL> msg . payload_utf8 = _json_to_payload ( data ) <EOL> be_size = pack ( "<STR_LIT>" , msg . ByteSize ( ) ) <EOL> if msg . namespace != NS_HEARTBEAT : <EOL> self . logger . debug ( "<STR_LIT>" , _message_to_string ( msg , data ) ) <EOL> if not force and self . stop . is_set ( ) : <EOL> raise PyChromecastStopped ( "<STR_LIT>" ) <EOL> if not self . connecting and not self . _force_recon : <EOL> try : <EOL> self . socket . sendall ( be_size + msg . SerializeToString ( ) ) <EOL> except socket . error : <EOL> self . _force_recon = True <EOL> self . logger . info ( '<STR_LIT>' ) <EOL> else : <EOL> raise NotConnected ( "<STR_LIT>" ) <EOL> response = None <EOL> if not no_add_request_id and wait_for_response : <EOL> callback = self . _request_callbacks [ request_id ] = { <EOL> '<STR_LIT>' : threading . Event ( ) , <EOL> '<STR_LIT>' : None , <EOL> } <EOL> callback [ '<STR_LIT>' ] . wait ( ) <EOL> response = callback . get ( '<STR_LIT>' ) <EOL> return response <EOL> def send_platform_message ( self , namespace , message , inc_session_id = False , <EOL> wait_for_response = False ) : <EOL> """<STR_LIT>""" <EOL> return self . send_message ( PLATFORM_DESTINATION_ID , namespace , message , <EOL> inc_session_id , wait_for_response ) <EOL> def send_app_message ( self , namespace , message , inc_session_id = False , <EOL> wait_for_response = False ) : <EOL> """<STR_LIT>""" <EOL> if namespace not in self . app_namespaces : <EOL> raise UnsupportedNamespace ( <EOL> ( "<STR_LIT>" <EOL> "<STR_LIT>" ) . format ( namespace , <EOL> "<STR_LIT:U+002CU+0020>" . join ( self . app_namespaces ) ) ) <EOL> return self . send_message ( self . destination_id , namespace , message , <EOL> inc_session_id , wait_for_response ) <EOL> def register_connection_listener ( self , listener ) : <EOL> """<STR_LIT>""" <EOL> self . _connection_listeners . append ( listener ) <EOL> def _ensure_channel_connected ( self , destination_id ) : <EOL> """<STR_LIT>""" <EOL> if destination_id not in self . _open_channels : <EOL> self . _open_channels . append ( destination_id ) <EOL> self . send_message ( <EOL> destination_id , NS_CONNECTION , <EOL> { MESSAGE_TYPE : TYPE_CONNECT , <EOL> '<STR_LIT>' : { } , <EOL> '<STR_LIT>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT>' : <NUM_LIT:2> , <EOL> '<STR_LIT:version>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : "<STR_LIT>" , <EOL> '<STR_LIT>' : <NUM_LIT:4> , <EOL> '<STR_LIT>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : <NUM_LIT:1> } } , <EOL> no_add_request_id = True ) <EOL> def _disconnect_channel ( self , destination_id ) : <EOL> """<STR_LIT>""" <EOL> if destination_id in self . _open_channels : <EOL> self . send_message ( <EOL> destination_id , NS_CONNECTION , <EOL> { MESSAGE_TYPE : TYPE_CLOSE , '<STR_LIT>' : { } } , <EOL> no_add_request_id = True , force = True ) <EOL> self . _open_channels . remove ( destination_id ) <EOL> self . handle_channel_disconnected ( ) <EOL> def handle_channel_disconnected ( self ) : <EOL> """<STR_LIT>""" <EOL> for namespace in self . app_namespaces : <EOL> if namespace in self . _handlers : <EOL> self . _handlers [ namespace ] . channel_disconnected ( ) <EOL> self . app_namespaces = [ ] <EOL> self . destination_id = None <EOL> self . session_id = None <EOL> class ConnectionController ( BaseController ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self ) : <EOL> super ( ConnectionController , self ) . __init__ ( NS_CONNECTION ) <EOL> def receive_message ( self , message , data ) : <EOL> """<STR_LIT>""" <EOL> if self . _socket_client . is_stopped : <EOL> return True <EOL> if data [ MESSAGE_TYPE ] == TYPE_CLOSE : <EOL> self . _socket_client . handle_channel_disconnected ( ) <EOL> return True <EOL> else : <EOL> return False <EOL> class HeartbeatController ( BaseController ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self ) : <EOL> super ( HeartbeatController , self ) . __init__ ( <EOL> NS_HEARTBEAT , target_platform = True ) <EOL> self . last_ping = <NUM_LIT:0> <EOL> self . last_pong = time . time ( ) <EOL> def receive_message ( self , message , data ) : <EOL> """<STR_LIT>""" <EOL> if self . _socket_client . is_stopped : <EOL> return True <EOL> if data [ MESSAGE_TYPE ] == TYPE_PING : <EOL> try : <EOL> self . _socket_client . send_message ( <EOL> PLATFORM_DESTINATION_ID , self . namespace , <EOL> { MESSAGE_TYPE : TYPE_PONG } , no_add_request_id = True ) <EOL> except PyChromecastStopped : <EOL> self . _socket_client . logger . exception ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" ) <EOL> return True <EOL> elif data [ MESSAGE_TYPE ] == TYPE_PONG : <EOL> self . reset ( ) <EOL> return True <EOL> else : <EOL> return False <EOL> def ping ( self ) : <EOL> """<STR_LIT>""" <EOL> self . last_ping = time . time ( ) <EOL> try : <EOL> self . send_message ( { MESSAGE_TYPE : TYPE_PING } ) <EOL> except NotConnected : <EOL> self . _socket_client . logger . error ( "<STR_LIT>" + <EOL> "<STR_LIT>" ) <EOL> def reset ( self ) : <EOL> """<STR_LIT>""" <EOL> self . last_pong = time . time ( ) <EOL> def is_expired ( self ) : <EOL> """<STR_LIT>""" <EOL> if time . time ( ) - self . last_ping > HB_PING_TIME : <EOL> self . ping ( ) <EOL> return ( time . time ( ) - self . last_pong ) > HB_PING_TIME + HB_PONG_TIME <EOL> class ReceiverController ( BaseController ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , cast_type = CAST_TYPE_CHROMECAST ) : <EOL> super ( ReceiverController , self ) . __init__ ( <EOL> NS_RECEIVER , target_platform = True ) <EOL> self . status = None <EOL> self . launch_failure = None <EOL> self . app_to_launch = None <EOL> self . cast_type = cast_type <EOL> self . app_launch_event = threading . Event ( ) <EOL> self . _status_listeners = [ ] <EOL> self . _launch_error_listeners = [ ] <EOL> @ property <EOL> def app_id ( self ) : <EOL> """<STR_LIT>""" <EOL> return self . status . app_id if self . status else None <EOL> def receive_message ( self , message , data ) : <EOL> """<STR_LIT>""" <EOL> if data [ MESSAGE_TYPE ] == TYPE_RECEIVER_STATUS : <EOL> self . _process_get_status ( data ) <EOL> return True <EOL> elif data [ MESSAGE_TYPE ] == TYPE_LAUNCH_ERROR : <EOL> self . _process_launch_error ( data ) <EOL> return True <EOL> else : <EOL> return False <EOL> def register_status_listener ( self , listener ) : <EOL> """<STR_LIT>""" <EOL> self . _status_listeners . append ( listener ) <EOL> def register_launch_error_listener ( self , listener ) : <EOL> """<STR_LIT>""" <EOL> self . _launch_error_listeners . append ( listener ) <EOL> def update_status ( self , blocking = False ) : <EOL> """<STR_LIT>""" <EOL> self . logger . debug ( "<STR_LIT>" ) <EOL> self . send_message ( { MESSAGE_TYPE : TYPE_GET_STATUS } , <EOL> wait_for_response = blocking ) <EOL> def launch_app ( self , app_id , force_launch = False , block_till_launched = True ) : <EOL> """<STR_LIT>""" <EOL> if not force_launch and self . app_id is None : <EOL> self . update_status ( True ) <EOL> if force_launch or self . app_id != app_id : <EOL> self . logger . info ( "<STR_LIT>" , app_id ) <EOL> self . launch_failure = None <EOL> if block_till_launched : <EOL> self . app_to_launch = app_id <EOL> self . app_launch_event . clear ( ) <EOL> response = self . send_message ( { MESSAGE_TYPE : TYPE_LAUNCH , <EOL> APP_ID : app_id } , <EOL> wait_for_response = block_till_launched ) <EOL> if block_till_launched : <EOL> is_app_started = False <EOL> if response : <EOL> response_type = response [ MESSAGE_TYPE ] <EOL> if response_type == TYPE_RECEIVER_STATUS : <EOL> new_status = self . _parse_status ( response , <EOL> self . cast_type ) <EOL> new_app_id = new_status . app_id <EOL> if new_app_id == app_id : <EOL> is_app_started = True <EOL> self . app_to_launch = None <EOL> self . app_launch_event . clear ( ) <EOL> elif response_type == TYPE_LAUNCH_ERROR : <EOL> self . app_to_launch = None <EOL> self . app_launch_event . clear ( ) <EOL> launch_error = self . _parse_launch_error ( response ) <EOL> raise LaunchError ( <EOL> "<STR_LIT>" . format ( <EOL> app_id , launch_error . reason ) ) <EOL> if not is_app_started : <EOL> self . app_launch_event . wait ( ) <EOL> if self . launch_failure : <EOL> raise LaunchError ( <EOL> "<STR_LIT>" . format ( <EOL> app_id , self . launch_failure . reason ) ) <EOL> else : <EOL> self . logger . info ( <EOL> "<STR_LIT>" , app_id ) <EOL> def stop_app ( self , block_till_stopped = True ) : <EOL> """<STR_LIT>""" <EOL> self . logger . info ( "<STR_LIT>" , self . app_id ) <EOL> return self . send_message ( <EOL> { MESSAGE_TYPE : '<STR_LIT>' } , <EOL> inc_session_id = True , wait_for_response = block_till_stopped ) <EOL> def set_volume ( self , volume ) : <EOL> """<STR_LIT>""" <EOL> volume = min ( max ( <NUM_LIT:0> , volume ) , <NUM_LIT:1> ) <EOL> self . logger . info ( "<STR_LIT>" , volume ) <EOL> self . send_message ( { MESSAGE_TYPE : '<STR_LIT>' , <EOL> '<STR_LIT>' : { '<STR_LIT>' : volume } } ) <EOL> return volume <EOL> def set_volume_muted ( self , muted ) : <EOL> """<STR_LIT>""" <EOL> self . send_message ( <EOL> { MESSAGE_TYPE : '<STR_LIT>' , <EOL> '<STR_LIT>' : { '<STR_LIT>' : muted } } ) <EOL> @ staticmethod <EOL> def _parse_status ( data , cast_type ) : <EOL> """<STR_LIT>""" <EOL> data = data . get ( '<STR_LIT:status>' , { } ) <EOL> volume_data = data . get ( '<STR_LIT>' , { } ) <EOL> try : <EOL> app_data = data [ '<STR_LIT>' ] [ <NUM_LIT:0> ] <EOL> except KeyError : <EOL> app_data = { } <EOL> is_audio = cast_type in ( CAST_TYPE_AUDIO , CAST_TYPE_GROUP ) <EOL> status = CastStatus ( <EOL> data . get ( '<STR_LIT>' , None if is_audio else False ) , <EOL> data . get ( '<STR_LIT>' , None if is_audio else True ) , <EOL> volume_data . get ( '<STR_LIT>' , <NUM_LIT:1.0> ) , <EOL> volume_data . get ( '<STR_LIT>' , False ) , <EOL> app_data . get ( APP_ID ) , <EOL> app_data . get ( '<STR_LIT>' ) , <EOL> [ item [ '<STR_LIT:name>' ] for item in app_data . get ( '<STR_LIT>' , [ ] ) ] , <EOL> app_data . get ( SESSION_ID ) , <EOL> app_data . get ( '<STR_LIT>' ) , <EOL> app_data . get ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> ) <EOL> return status <EOL> def _process_get_status ( self , data ) : <EOL> """<STR_LIT>""" <EOL> status = self . _parse_status ( data , self . cast_type ) <EOL> is_new_app = self . app_id != status . app_id and self . app_to_launch <EOL> self . status = status <EOL> if is_new_app and self . app_to_launch == self . app_id : <EOL> self . app_to_launch = None <EOL> self . app_launch_event . set ( ) <EOL> self . logger . debug ( "<STR_LIT>" , self . status ) <EOL> self . _report_status ( ) <EOL> def _report_status ( self ) : <EOL> """<STR_LIT>""" <EOL> for listener in self . _status_listeners : <EOL> try : <EOL> listener . new_cast_status ( self . status ) <EOL> except Exception : <EOL> pass <EOL> @ staticmethod <EOL> def _parse_launch_error ( data ) : <EOL> """<STR_LIT>""" <EOL> return LaunchFailure ( <EOL> data . get ( ERROR_REASON , None ) , <EOL> data . get ( APP_ID ) , <EOL> data . get ( REQUEST_ID ) , <EOL> ) <EOL> def _process_launch_error ( self , data ) : <EOL> """<STR_LIT>""" <EOL> launch_failure = self . _parse_launch_error ( data ) <EOL> self . launch_failure = launch_failure <EOL> if self . app_to_launch : <EOL> self . app_to_launch = None <EOL> self . app_launch_event . set ( ) <EOL> self . logger . debug ( "<STR_LIT>" , launch_failure ) <EOL> for listener in self . _launch_error_listeners : <EOL> try : <EOL> listener . new_launch_error ( launch_failure ) <EOL> except Exception : <EOL> pass <EOL> def tear_down ( self ) : <EOL> """<STR_LIT>""" <EOL> super ( ReceiverController , self ) . tear_down ( ) <EOL> self . status = None <EOL> self . launch_failure = None <EOL> self . app_to_launch = None <EOL> self . app_launch_event . clear ( ) <EOL> self . _report_status ( ) <EOL> self . _status_listeners [ : ] = [ ] </s>
