77
88_LOGGER = logging .getLogger (__name__ )
99
10+ SIGNAL_DATA = "data"
11+ SIGNAL_CONNECTION_STATE = "state"
12+
13+ ERROR_AUTH_FAILURE = "Authorization failure"
14+ ERROR_TOO_MANY_RETRIES = "Too many retries"
15+ ERROR_UNKNOWN = "Unknown"
16+
17+ MAX_FAILED_ATTEMPTS = 5
18+
1019STATE_CONNECTED = "connected"
1120STATE_DISCONNECTED = "disconnected"
1221STATE_STARTING = "starting"
@@ -47,7 +56,10 @@ def __init__(self, plex_server, callback, session=None, verify_ssl=True):
4756 plex_server (plexapi.server.PlexServer):
4857 A connected PlexServer instance.
4958 callback (Runnable):
50- Callback to issue when Plex player events occur.
59+ Called when interesting events occur. Provides arguments:
60+ signal (str): One of SIGNAL_* constants
61+ data (str): Current STATE_* or websocket payload contents
62+ error (str): Error message if any or None
5163 verify_ssl:
5264 Set to False to disable SSL certificate validation.
5365 session (aiohttp.ClientSession, optional):
@@ -61,6 +73,7 @@ def __init__(self, plex_server, callback, session=None, verify_ssl=True):
6173 self ._ssl = False if verify_ssl is False else None
6274 self ._state = None
6375 self .failed_attempts = 0
76+ self ._error_reason = None
6477
6578 @property
6679 def state (self ):
@@ -72,6 +85,8 @@ def state(self, value):
7285 """Set the state."""
7386 self ._state = value
7487 _LOGGER .debug ("Websocket %s" , value )
88+ self .callback (SIGNAL_CONNECTION_STATE , value , self ._error_reason )
89+ self ._error_reason = None
7590
7691 @staticmethod
7792 def _get_uri (plex_server ):
@@ -90,7 +105,6 @@ async def running(self):
90105 ) as ws_client :
91106 self .state = STATE_CONNECTED
92107 self .failed_attempts = 0
93- self .callback ()
94108
95109 async for message in ws_client :
96110 if self .state == STATE_STOPPED :
@@ -99,7 +113,7 @@ async def running(self):
99113 if message .type == aiohttp .WSMsgType .TEXT :
100114 msg = message .json ()["NotificationContainer" ]
101115 if self .player_event (msg ):
102- self .callback ()
116+ self .callback (SIGNAL_DATA , msg , None )
103117
104118 elif message .type == aiohttp .WSMsgType .CLOSED :
105119 _LOGGER .warning ("AIOHTTP websocket connection closed" )
@@ -109,8 +123,19 @@ async def running(self):
109123 _LOGGER .error ("AIOHTTP websocket error" )
110124 break
111125
126+ except aiohttp .ClientResponseError as error :
127+ if error .code == 401 :
128+ _LOGGER .error ("Credentials rejected: %s" , error )
129+ self ._error_reason = ERROR_AUTH_FAILURE
130+ else :
131+ _LOGGER .error ("Unexpected response received: %s" , error )
132+ self ._error_reason = ERROR_UNKNOWN
133+ self .state = STATE_STOPPED
112134 except (aiohttp .ClientConnectionError , asyncio .TimeoutError ) as error :
113- if self .state != STATE_STOPPED :
135+ if self .failed_attempts >= MAX_FAILED_ATTEMPTS :
136+ self ._error_reason = ERROR_TOO_MANY_RETRIES
137+ self .state = STATE_STOPPED
138+ elif self .state != STATE_STOPPED :
114139 retry_delay = min (2 ** (self .failed_attempts - 1 ) * 30 , 300 )
115140 self .failed_attempts += 1
116141 _LOGGER .error (
@@ -123,6 +148,7 @@ async def running(self):
123148 except Exception as error : # pylint: disable=broad-except
124149 if self .state != STATE_STOPPED :
125150 _LOGGER .exception ("Unexpected exception occurred: %s" , error )
151+ self ._error_reason = ERROR_UNKNOWN
126152 self .state = STATE_STOPPED
127153 else :
128154 if self .state != STATE_STOPPED :
0 commit comments