""" TeamTracker Team Status """ import asyncio from datetime import date, datetime, timedelta, timezone import json import locale import logging import os import re from typing import ClassVar import aiofiles import arrow from async_timeout import timeout from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_NAME from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.entity_registry import ( # pylint: disable=reimported async_entries_for_config_entry, async_get, async_get as async_get_entity_registry, ) from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import ( API_LIMIT, CONF_API_LANGUAGE, CONF_CONFERENCE_ID, CONF_LEAGUE_ID, CONF_LEAGUE_PATH, CONF_SPORT_PATH, CONF_TEAM_ID, COORDINATOR, DEFAULT_KICKOFF_IN, DEFAULT_LAST_UPDATE, DEFAULT_LEAGUE, DEFAULT_LOGO, DEFAULT_TIMEOUT, DOMAIN, ISSUE_URL, NATIVE_LEAGUES, OVERRIDE_DICT, PLATFORMS, SERVICE_NAME_CALL_API, SERVICE_NAME_RELOAD_OVERRIDES, VERSION, ) from .coordinator import TeamTrackerCoordinator from .provider_base import BaseSportProvider from .utils import has_team, is_integer, load_file_overrides _LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Load the saved entities.""" async def get_entry_id_from_entity_id(hass: HomeAssistant, entity_id: str): """Retrieve entry_id from entity_id.""" # Get the entity registry entity_registry = async_get_entity_registry(hass) # Find the entry associated with the given entity_id entry = entity_registry.async_get(entity_id) if entry: return entry.config_entry_id return None # # async_call_api_service() # Service to change the path, league, team and conference # async def async_call_api_service(call): """Handle the service action call.""" sport_path = str(call.data.get(CONF_SPORT_PATH, "football")) league_path = str(call.data.get(CONF_LEAGUE_PATH, "nfl")) team_id = str(call.data.get(CONF_TEAM_ID, "cle")) conference_id = call.data.get(CONF_CONFERENCE_ID, "") conference_id = "" if conference_id is None else str(conference_id) entity_ids = call.data.get("entity_id", "none") for entity_id in entity_ids: entry_id = await get_entry_id_from_entity_id(hass, entity_id) if entry_id: # Set up from UI, use entry_id as index sensor_coordinator = hass.data[DOMAIN][entry_id][COORDINATOR] sensor_coordinator.update_team_info(sport_path, league_path, team_id, conference_id) await sensor_coordinator.async_refresh() else: # Set up from YAML, use sensor_name (from entity_name) as index sensor_name = entity_id.split('.')[-1] if sensor_name in hass.data[DOMAIN] and COORDINATOR in hass.data[DOMAIN][sensor_name]: sensor_coordinator = hass.data[DOMAIN][sensor_name][COORDINATOR] sensor_coordinator.update_team_info(sport_path, league_path, team_id, conference_id) await sensor_coordinator.async_refresh() else: # YAML had duplicate names so it doesn't match the entity_name _LOGGER.info( "%s: [service=call_api] No entry_id found (likely because of non-unique sensor names in YAML) for entity_id: %s", sensor_name, entity_id, ) # # async_reload_overrides() # Service to reload the override files # async def async_reload_overrides(call): """Handle the service action call to reload the override file.""" _LOGGER.warning( "Reloading local teamtracker_overrides.json file. All TeamTracker sensors will be impacted on next API call." ) # Initialize DOMAIN in hass.data if it doesn't exist if DOMAIN not in hass.data: hass.data[DOMAIN] = {} # Reload the OVERRIDE_DICT override_dict = await hass.async_add_executor_job(load_file_overrides, hass) hass.data[DOMAIN][OVERRIDE_DICT] = override_dict # Print startup message sensor_name = entry.data[CONF_NAME] _LOGGER.info( "%s: Setting up sensor from UI configuration using TeamTracker %s, if you have any issues please report them here: %s", sensor_name, VERSION, ISSUE_URL, ) # Initialize DOMAIN in hass.data if it doesn't exist if DOMAIN not in hass.data: hass.data[DOMAIN] = {} # Load the OVERRIDE_DICT if it doesn't exist if OVERRIDE_DICT not in hass.data[DOMAIN]: hass.data[DOMAIN][OVERRIDE_DICT] = None override_dict = await hass.async_add_executor_job(load_file_overrides, hass) if OVERRIDE_DICT not in hass.data[DOMAIN] or hass.data[DOMAIN][OVERRIDE_DICT] is None: hass.data[DOMAIN][OVERRIDE_DICT] = override_dict entry.async_on_unload(entry.add_update_listener(update_options_listener)) if entry.unique_id is not None: _LOGGER.info( "%s: async_setup_entry() - entry.unique_id is not None: %s", sensor_name, entry.unique_id, ) hass.config_entries.async_update_entry(entry, unique_id=None) ent_reg = async_get(hass) for entity in async_entries_for_config_entry(ent_reg, entry.entry_id): ent_reg.async_update_entity(entity.entity_id, new_unique_id=entry.entry_id) # Setup the data coordinator coordinator = TeamTrackerCoordinator( hass, entry.data, entry ) # Fetch initial data so we have data when entities subscribe # await coordinator.async_refresh() # For UI, use entry_id as index hass.data[DOMAIN][entry.entry_id] = { COORDINATOR: coordinator, } await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) # # Register services for sensor # hass.services.async_register(DOMAIN, SERVICE_NAME_CALL_API, async_call_api_service,) hass.services.async_register(DOMAIN, SERVICE_NAME_RELOAD_OVERRIDES, async_reload_overrides,) return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Handle removal of an entry.""" # Unload platforms unload_ok = all( await asyncio.gather( *[ hass.config_entries.async_forward_entry_unload(entry, platform) for platform in PLATFORMS ] ) ) if unload_ok: domain_data = hass.data.get(DOMAIN, None) if domain_data and entry.entry_id in domain_data: domain_data.pop(entry.entry_id) # Only remove service if this is the last entry if not domain_data: hass.services.async_remove(DOMAIN, SERVICE_NAME_CALL_API) return unload_ok # # Only needed if Options Flow is added # async def update_options_listener(hass, entry): """Update listener.""" await hass.config_entries.async_reload(entry.entry_id) async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Migrate an old config entry.""" sensor_name = entry.data[CONF_NAME] version = entry.version # 1-> 2->3: Migration format # Add CONF_LEAGUE_ID, CONF_SPORT_PATH, and CONF_LEAGUE_PATH if not already populated if version < 3: _LOGGER.debug("%s: Migrating from version %s", sensor_name, version) updated_config = entry.data.copy() if CONF_LEAGUE_ID not in updated_config.keys(): updated_config[CONF_LEAGUE_ID] = DEFAULT_LEAGUE if (CONF_SPORT_PATH not in updated_config.keys()) or ( CONF_LEAGUE_PATH not in updated_config.keys() ): league_id = updated_config[CONF_LEAGUE_ID].upper() updated_config.update(NATIVE_LEAGUES[league_id]) if updated_config != entry.data: hass.config_entries.async_update_entry(entry, data=updated_config, version=3) _LOGGER.debug("%s: Migration to version %s complete", sensor_name, entry.version) return True