@@ -112,8 +112,11 @@ async def async_call_api_service(call):
112112 VERSION ,
113113 ISSUE_URL ,
114114 )
115- hass .data .setdefault (DOMAIN , {})
116115
116+ # Initialize DOMAIN in hass.data if it doesn't exist
117+ if DOMAIN not in hass .data :
118+ hass .data [DOMAIN ] = {}
119+
117120 entry .async_on_unload (entry .add_update_listener (update_options_listener ))
118121
119122 if entry .unique_id is not None :
@@ -153,6 +156,14 @@ async def async_call_api_service(call):
153156async def async_unload_entry (hass : HomeAssistant , entry : ConfigEntry ) -> bool :
154157 """Handle removal of an entry."""
155158
159+ # Shut down the coordinator first to close aiohttp session
160+ if entry .entry_id in hass .data [DOMAIN ]:
161+ coordinator = hass .data [DOMAIN ][entry .entry_id ].get (COORDINATOR )
162+ if coordinator :
163+ if hasattr (coordinator , "async_unload" ):
164+ await coordinator .async_unload ()
165+
166+ # Unload platforms
156167 unload_ok = all (
157168 await asyncio .gather (
158169 * [
@@ -164,6 +175,13 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
164175
165176 if unload_ok :
166177 hass .data [DOMAIN ].pop (entry .entry_id )
178+
179+ # Only remove service if this is the last entry
180+ if not hass .data [DOMAIN ]:
181+ hass .services .async_remove (DOMAIN , SERVICE_NAME_CALL_API )
182+ TeamTrackerDataUpdateCoordinator .data_cache .clear ()
183+ TeamTrackerDataUpdateCoordinator .last_update .clear ()
184+ TeamTrackerDataUpdateCoordinator .c_cache .clear ()
167185
168186 return unload_ok
169187
@@ -227,12 +245,27 @@ def __init__(self, hass, config, entry: ConfigEntry=None):
227245 self .config = config
228246 self .hass = hass
229247 self .entry = entry #None if setup from YAML
248+ self ._session = None # ADD: Track aiohttp session
230249
231250 super ().__init__ (hass , _LOGGER , name = self .name , update_interval = DEFAULT_REFRESH_RATE )
232251 _LOGGER .debug (
233252 "%s: Using default refresh rate (%s)" , self .name , self .update_interval
234253 )
235254
255+ # ADD: New method to get or create session
256+ async def _get_session (self ):
257+ """Get or create aiohttp session."""
258+ if self ._session is None or self ._session .closed :
259+ self ._session = aiohttp .ClientSession ()
260+ return self ._session
261+
262+ # ADD: New method to cleanup
263+ async def async_shutdown (self ):
264+ """Cleanup coordinator resources."""
265+ if self ._session and not self ._session .closed :
266+ await self ._session .close ()
267+ _LOGGER .debug ("%s: Closed aiohttp session" , self .name )
268+
236269
237270 #
238271 # Return the language to use for the API
@@ -401,19 +434,20 @@ async def async_call_api(self, config, hass, lang) -> dict:
401434 contents = await f .read ()
402435 data = json .loads (contents )
403436 else :
404- async with aiohttp .ClientSession () as session :
405- try :
406- async with session .get (url , headers = headers ) as r :
407- _LOGGER .debug (
408- "%s: Calling API for '%s' from %s" ,
409- sensor_name ,
410- team_id ,
411- url ,
412- )
413- if r .status == 200 :
414- data = await r .json ()
415- except :
416- data = None
437+ session = await self ._get_session ()
438+ try :
439+ async with session .get (url , headers = headers ) as r :
440+ _LOGGER .debug (
441+ "%s: Calling API for '%s' from %s" ,
442+ sensor_name ,
443+ team_id ,
444+ url ,
445+ )
446+ if r .status == 200 :
447+ data = await r .json ()
448+ except Exception as e : # pylint: disable=broad-exception-caught
449+ _LOGGER .debug ("%s: API call failed: %s" , sensor_name , e )
450+ data = None
417451
418452 num_events = 0
419453 if data is not None :
@@ -434,6 +468,8 @@ async def async_call_api(self, config, hass, lang) -> dict:
434468 num_events ,
435469 url ,
436470 )
471+
472+ # First fallback - without date constraint
437473 if num_events == 0 :
438474 url_parms = "?lang=" + lang [:2 ]
439475 if self .conference_id :
@@ -443,19 +479,19 @@ async def async_call_api(self, config, hass, lang) -> dict:
443479
444480 url = URL_HEAD + sport_path + "/" + league_path + URL_TAIL + url_parms
445481
446- async with aiohttp . ClientSession () as session :
447- try :
448- async with session . get ( url , headers = headers ) as r :
449- _LOGGER . debug (
450- "%s: Calling API without date constraint for '%s' from %s" ,
451- sensor_name ,
452- team_id ,
453- url ,
454- )
455- if r . status == 200 :
456- data = await r . json ()
457- except :
458- data = None
482+ try :
483+ async with session . get ( url , headers = headers ) as r :
484+ _LOGGER . debug (
485+ "%s: Calling API without date constraint for '%s' from %s" ,
486+ sensor_name ,
487+ team_id ,
488+ url ,
489+ )
490+ if r . status == 200 :
491+ data = await r . json ()
492+ except Exception as e : # pylint: disable=broad-exception-caught
493+ _LOGGER . debug ( "%s: API call failed: %s" , sensor_name , e )
494+ data = None
459495
460496 num_events = 0
461497 if data is not None :
@@ -465,7 +501,6 @@ async def async_call_api(self, config, hass, lang) -> dict:
465501 team_id ,
466502 url ,
467503 )
468-
469504 try :
470505 num_events = len (data ["events" ])
471506 except :
@@ -478,6 +513,7 @@ async def async_call_api(self, config, hass, lang) -> dict:
478513 url ,
479514 )
480515
516+ # Second fallback - without language
481517 if num_events == 0 :
482518 url_parms = ""
483519 if self .conference_id :
@@ -487,23 +523,25 @@ async def async_call_api(self, config, hass, lang) -> dict:
487523
488524 url = URL_HEAD + sport_path + "/" + league_path + URL_TAIL + url_parms
489525
490- async with aiohttp .ClientSession () as session :
491- try :
492- async with session .get (url , headers = headers ) as r :
493- _LOGGER .debug (
494- "%s: Calling API without language for '%s' from %s" ,
495- sensor_name ,
496- team_id ,
497- url ,
498- )
499- if r .status == 200 :
500- data = await r .json ()
501- except :
502- data = None
526+ try :
527+ async with session .get (url , headers = headers ) as r :
528+ _LOGGER .debug (
529+ "%s: Calling API without language for '%s' from %s" ,
530+ sensor_name ,
531+ team_id ,
532+ url ,
533+ )
534+ if r .status == 200 :
535+ data = await r .json ()
536+ except Exception as e : # pylint: disable=broad-exception-caught
537+ _LOGGER .debug ("%s: API call failed: %s" , sensor_name , e )
538+ data = None
539+
503540 self .api_url = url
504541
505542 return data , file_override
506543
544+
507545 async def async_update_values (self , config , hass , data , lang ) -> dict :
508546 """Return values based on the data passed into method"""
509547
0 commit comments