├── .github ├── funding.yml └── workflows │ ├── hacs_action.yaml │ ├── hassfest.yaml │ ├── pylint.yaml │ └── pytest.yaml ├── .gitignore ├── README.md ├── codecov.yml ├── custom_components ├── __init__.py ├── pyproject.toml └── teamtracker │ ├── __init__.py │ ├── clear_values.py │ ├── config_flow.py │ ├── const.py │ ├── event.py │ ├── manifest.json │ ├── sensor.py │ ├── services.yaml │ ├── set_baseball.py │ ├── set_cricket.py │ ├── set_golf.py │ ├── set_hockey.py │ ├── set_mma.py │ ├── set_racing.py │ ├── set_soccer.py │ ├── set_tennis.py │ ├── set_values.py │ ├── set_volleyball.py │ ├── translations │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── pt-BR.json │ └── sk.json │ └── utils.py ├── hacs.json ├── pyproject.toml ├── requirements_test.txt ├── setup.cfg └── tests ├── __init__.py ├── conftest.py ├── const.py ├── test_config_flow.py ├── test_error_conditions.py ├── test_event.py ├── test_init.py ├── test_multigame.py ├── test_sensor.py └── tt ├── README.txt ├── all-dump.txt ├── all.json ├── all.yaml ├── automations.yaml ├── mlb-dump.txt ├── mlb.json ├── mlb.yaml ├── mls-dump.txt ├── mls.json ├── mls.yaml ├── multigame.json ├── ncaaf-dump.txt ├── ncaaf.json ├── ncaaf.yaml ├── ncaavbw-dump.txt ├── ncaavbw.json ├── ncaavbw.yaml ├── nfl-dump.txt ├── nfl.json ├── nfl.yaml ├── nhl-dump.txt ├── nhl.json ├── nhl.yaml ├── results ├── test_tt_all_test01.json ├── test_tt_all_test02.json ├── test_tt_all_test03.json ├── test_tt_all_test04.json ├── test_tt_all_test05.json ├── test_tt_all_test06.json ├── test_tt_all_test07.json ├── test_tt_all_test08.json ├── test_tt_all_test09.json ├── test_tt_all_test10.json ├── test_tt_all_test11.json ├── test_tt_all_test12.json ├── test_tt_all_test13.json ├── test_tt_all_test14.json ├── test_tt_all_test15.json ├── test_tt_all_test16.json ├── test_tt_all_test17.json ├── test_tt_all_test18.json ├── test_tt_all_test19.json ├── test_tt_all_test20.json ├── test_tt_all_test21.json ├── test_tt_all_test22.json ├── test_tt_all_test23.json ├── test_tt_all_test24.json ├── test_tt_all_test25.json ├── test_tt_all_test26.json ├── test_tt_all_test27.json ├── test_tt_all_test28.json ├── test_tt_all_test29.json ├── test_tt_all_test30.json ├── test_tt_all_test31.json └── test_ttm_teste00.json └── test-case-list.txt /.github/funding.yml: -------------------------------------------------------------------------------- 1 | github: vasqued2 2 | buy_me_a_coffee: vasqued2 3 | -------------------------------------------------------------------------------- /.github/workflows/hacs_action.yaml: -------------------------------------------------------------------------------- 1 | name: HACS Action 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: "0 0 * * *" 8 | 9 | jobs: 10 | hacs: 11 | name: HACS Action 12 | runs-on: "ubuntu-latest" 13 | steps: 14 | - name: HACS Action 15 | uses: "hacs/action@main" 16 | with: 17 | category: "integration" -------------------------------------------------------------------------------- /.github/workflows/hassfest.yaml: -------------------------------------------------------------------------------- 1 | name: Hassfest 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | pull_request: 7 | schedule: 8 | - cron: "0 0 * * *" 9 | 10 | jobs: 11 | validate: 12 | runs-on: "ubuntu-latest" 13 | steps: 14 | - uses: "actions/checkout@v4" 15 | - uses: home-assistant/actions/hassfest@master 16 | -------------------------------------------------------------------------------- /.github/workflows/pylint.yaml: -------------------------------------------------------------------------------- 1 | name: Pylint 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: "0 0 * * *" 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | python-version: ["3.12"] 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Set up Python ${{ matrix.python-version }} 18 | uses: actions/setup-python@v5 19 | with: 20 | python-version: ${{ matrix.python-version }} 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install -r requirements_test.txt 25 | pip install pylint 26 | - name: Analysing the code with pylint 27 | run: | 28 | pylint $(git ls-files '*.py') 29 | -------------------------------------------------------------------------------- /.github/workflows/pytest.yaml: -------------------------------------------------------------------------------- 1 | name: Pytest 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: "0 0 * * *" 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | python-version: ['3.11', '3.12'] 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Set up Python ${{ matrix.python-version }} 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: ${{ matrix.python-version }} 22 | - name: Install dependencies 23 | run: | 24 | 25 | python -m pip install --upgrade pip 26 | pip install --upgrade -r requirements_test.txt 27 | - name: Generate coverage report 28 | run: | 29 | python -m pytest --asyncio-mode=auto 30 | pip install pytest-cov 31 | pytest ./tests/ --cov=custom_components/teamtracker/ --cov-report=xml 32 | - name: Upload coverage to Codecov 33 | uses: codecov/codecov-action@v4 34 | env: 35 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.pyc 3 | .coverage 4 | .vscode/* -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | informational: true 6 | patch: 7 | default: 8 | informational: true -------------------------------------------------------------------------------- /custom_components/__init__.py: -------------------------------------------------------------------------------- 1 | """For testing""" 2 | -------------------------------------------------------------------------------- /custom_components/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | target-version = ["py39", "py310"] 3 | extend-exclude = "/generated/" 4 | 5 | [tool.isort] 6 | # https://github.com/PyCQA/isort/wiki/isort-Settings 7 | profile = "black" 8 | # will group `import x` and `from x import` of the same module. 9 | force_sort_within_sections = true 10 | known_first_party = [ 11 | "homeassistant", 12 | "tests", 13 | ] 14 | forced_separate = [ 15 | "tests", 16 | ] 17 | combine_as_imports = true 18 | 19 | [tool.pylint.MAIN] 20 | py-version = "3.10" 21 | ignore = [ 22 | "tests", 23 | ] 24 | # Use a conservative default here; 2 should speed up most setups and not hurt 25 | # any too bad. Override on command line as appropriate. 26 | jobs = 2 27 | init-hook = """\ 28 | from pathlib import Path; \ 29 | import sys; \ 30 | 31 | from pylint.config import find_default_config_files; \ 32 | 33 | sys.path.append( \ 34 | str(Path(next(find_default_config_files())).parent.joinpath('pylint/plugins')) 35 | ) \ 36 | """ 37 | load-plugins = [ 38 | "pylint.extensions.code_style", 39 | "pylint.extensions.typing", 40 | # "hass_constructor", 41 | # "hass_enforce_type_hints", 42 | # "hass_imports", 43 | # "hass_logger", 44 | ] 45 | persistent = false 46 | extension-pkg-allow-list = [ 47 | "av.audio.stream", 48 | "av.stream", 49 | "ciso8601", 50 | "orjson", 51 | "cv2", 52 | ] 53 | fail-on = [ 54 | "I", 55 | ] 56 | 57 | [tool.pylint.BASIC] 58 | class-const-naming-style = "any" 59 | good-names = [ 60 | "_", 61 | "ev", 62 | "ex", 63 | "fp", 64 | "i", 65 | "id", 66 | "j", 67 | "k", 68 | "Run", 69 | "ip", 70 | ] 71 | good-names-rgxs = [ 72 | "\\S", 73 | "\\S\\d", 74 | ] 75 | 76 | [tool.pylint."MESSAGES CONTROL"] 77 | # Reasons disabled: 78 | # format - handled by black 79 | # locally-disabled - it spams too much 80 | # duplicate-code - unavoidable 81 | # cyclic-import - doesn't test if both import on load 82 | # abstract-class-little-used - prevents from setting right foundation 83 | # unused-argument - generic callbacks and setup methods create a lot of warnings 84 | # too-many-* - are not enforced for the sake of readability 85 | # too-few-* - same as too-many-* 86 | # abstract-method - with intro of async there are always methods missing 87 | # inconsistent-return-statements - doesn't handle raise 88 | # too-many-ancestors - it's too strict. 89 | # wrong-import-order - isort guards this 90 | # consider-using-f-string - str.format sometimes more readable 91 | # --- 92 | # Enable once current issues are fixed: 93 | # consider-using-namedtuple-or-dataclass (Pylint CodeStyle extension) 94 | # consider-using-assignment-expr (Pylint CodeStyle extension) 95 | disable = [ 96 | "format", 97 | "abstract-method", 98 | "cyclic-import", 99 | "duplicate-code", 100 | "inconsistent-return-statements", 101 | "locally-disabled", 102 | "not-context-manager", 103 | "too-few-public-methods", 104 | "too-many-ancestors", 105 | "too-many-arguments", 106 | "too-many-branches", 107 | "too-many-instance-attributes", 108 | "too-many-lines", 109 | "too-many-locals", 110 | "too-many-public-methods", 111 | "too-many-return-statements", 112 | "too-many-statements", 113 | "too-many-boolean-expressions", 114 | "unused-argument", 115 | "wrong-import-order", 116 | "consider-using-f-string", 117 | "consider-using-namedtuple-or-dataclass", 118 | "consider-using-assignment-expr", 119 | "bare-except", 120 | ] 121 | enable = [ 122 | #"useless-suppression", # temporarily every now and then to clean them up 123 | "use-symbolic-message-instead", 124 | ] 125 | 126 | [tool.pylint.REPORTS] 127 | score = false 128 | 129 | [tool.pylint.TYPECHECK] 130 | ignored-classes = [ 131 | "_CountingAttr", # for attrs 132 | ] 133 | mixin-class-rgx = ".*[Mm]ix[Ii]n" 134 | 135 | [tool.pylint.FORMAT] 136 | expected-line-ending-format = "LF" 137 | 138 | [tool.pylint.EXCEPTIONS] 139 | overgeneral-exceptions = [ 140 | "BaseException", 141 | "Exception", 142 | "HomeAssistantError", 143 | ] 144 | 145 | [tool.pylint.TYPING] 146 | runtime-typing = false 147 | 148 | [tool.pylint.CODE_STYLE] 149 | max-line-length-suggestions = 72 150 | 151 | [tool.pytest.ini_options] 152 | testpaths = [ 153 | "tests", 154 | ] 155 | norecursedirs = [ 156 | ".git", 157 | "testing_config", 158 | ] 159 | log_format = "%(asctime)s.%(msecs)03d %(levelname)-8s %(threadName)s %(name)s:%(filename)s:%(lineno)s %(message)s" 160 | log_date_format = "%Y-%m-%d %H:%M:%S" 161 | asyncio_mode = "auto" -------------------------------------------------------------------------------- /custom_components/teamtracker/clear_values.py: -------------------------------------------------------------------------------- 1 | """ Clear all sensor values """ 2 | 3 | 4 | async def async_clear_values() -> dict: 5 | """Clear all state attributes""" 6 | new_values = {} 7 | 8 | # Reset values 9 | new_values = { 10 | "sport": None, 11 | "sport_path": None, 12 | "league": None, 13 | "league_path": None, 14 | "league_logo": None, 15 | "season": None, 16 | "team_abbr": None, 17 | "opponent_abbr": None, 18 | "event_name": None, 19 | "event_url": None, 20 | "date": None, 21 | "kickoff_in": None, 22 | "series_summary": None, 23 | "venue": None, 24 | "location": None, 25 | "tv_network": None, 26 | "odds": None, 27 | "overunder": None, 28 | "team_name": None, 29 | "team_long_name": None, 30 | "team_id": None, 31 | "team_record": None, 32 | "team_rank": None, 33 | "team_conference_id": None, 34 | "team_homeaway": None, 35 | "team_logo": None, 36 | "team_url": None, 37 | "team_colors": None, 38 | "team_score": None, 39 | "team_win_probability": None, 40 | "team_winner": None, 41 | "team_timeouts": None, 42 | "opponent_name": None, 43 | "opponent_long_name": None, 44 | "opponent_id": None, 45 | "opponent_record": None, 46 | "opponent_rank": None, 47 | "opponent_conference_id": None, 48 | "opponent_homeaway": None, 49 | "opponent_logo": None, 50 | "opponent_url": None, 51 | "opponent_colors": None, 52 | "opponent_score": None, 53 | "opponent_win_probability": None, 54 | "opponent_winner": None, 55 | "opponent_timeouts": None, 56 | "quarter": None, 57 | "clock": None, 58 | "possession": None, 59 | "last_play": None, 60 | "down_distance_text": None, 61 | "outs": None, 62 | "balls": None, 63 | "strikes": None, 64 | "on_first": None, 65 | "on_second": None, 66 | "on_third": None, 67 | "team_shots_on_target": None, 68 | "team_total_shots": None, 69 | "opponent_shots_on_target": None, 70 | "opponent_total_shots": None, 71 | "team_sets_won": None, 72 | "opponent_sets_won": None, 73 | "last_update": None, 74 | "api_message": None, 75 | "api_url": None, 76 | "private_fast_refresh": False, 77 | } 78 | 79 | return new_values 80 | -------------------------------------------------------------------------------- /custom_components/teamtracker/config_flow.py: -------------------------------------------------------------------------------- 1 | """Adds config flow for TeamTracker.""" 2 | 3 | from __future__ import annotations 4 | 5 | import logging 6 | from typing import Any, Dict, Optional 7 | 8 | import voluptuous as vol 9 | 10 | from homeassistant import config_entries 11 | from homeassistant.const import CONF_NAME 12 | from homeassistant.core import HomeAssistant, callback 13 | from homeassistant.helpers import config_validation as cv 14 | 15 | from .const import ( 16 | CONF_API_LANGUAGE, 17 | CONF_CONFERENCE_ID, 18 | CONF_LEAGUE_ID, 19 | CONF_LEAGUE_PATH, 20 | CONF_SPORT_PATH, 21 | CONF_TEAM_ID, 22 | DEFAULT_CONFERENCE_ID, 23 | DEFAULT_LEAGUE, 24 | DEFAULT_NAME, 25 | DOMAIN, 26 | LEAGUE_MAP, 27 | ) 28 | 29 | JSON_FEATURES = "features" 30 | JSON_PROPERTIES = "properties" 31 | JSON_ID = "id" 32 | 33 | _LOGGER = logging.getLogger(__name__) 34 | 35 | 36 | def _get_schema( 37 | hass: HomeAssistant, 38 | user_input: Optional[Dict[str, Any]], 39 | default_dict: Dict[str, Any], 40 | entry_id: str = None, 41 | ) -> vol.Schema: 42 | # pylint: disable=deprecated-typing-alias 43 | # pylint: disable=consider-alternative-union-syntax 44 | """Gets a schema using the default_dict as a backup.""" 45 | 46 | if user_input is None: 47 | user_input = {} 48 | 49 | def _get_default(key: str, fallback_default: Any = None) -> None: 50 | """Gets default value for key.""" 51 | return user_input.get(key, default_dict.get(key, fallback_default)) 52 | 53 | return vol.Schema( 54 | { 55 | vol.Required(CONF_LEAGUE_ID, default=_get_default(CONF_LEAGUE_ID)): vol.In( 56 | { 57 | **{k: k for k in sorted(LEAGUE_MAP)}, 58 | "XXX": "Custom: Specify sport and league path", 59 | } 60 | ), 61 | vol.Required(CONF_TEAM_ID, default=_get_default(CONF_TEAM_ID)): cv.string, 62 | vol.Optional(CONF_NAME, default=_get_default(CONF_NAME)): cv.string, 63 | vol.Optional( 64 | CONF_CONFERENCE_ID, default=_get_default(CONF_CONFERENCE_ID) 65 | ): cv.string, 66 | } 67 | ) 68 | 69 | 70 | def _get_path_schema(hass: Any, user_input: list, default_dict: list) -> Any: 71 | """Gets a schema using the default_dict as a backup.""" 72 | if user_input is None: 73 | user_input = {} 74 | 75 | def _get_default(key): 76 | """Gets default value for key.""" 77 | return user_input.get(key, default_dict.get(key)) 78 | 79 | return vol.Schema( 80 | { 81 | vol.Required(CONF_SPORT_PATH, default=_get_default(CONF_SPORT_PATH)): str, 82 | vol.Required(CONF_LEAGUE_PATH, default=_get_default(CONF_LEAGUE_PATH)): str, 83 | } 84 | ) 85 | 86 | 87 | @config_entries.HANDLERS.register(DOMAIN) 88 | class TeamTrackerScoresFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): 89 | """Config flow for TeamTracker.""" 90 | 91 | VERSION = 3 92 | # CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL 93 | 94 | def __init__(self): 95 | """Initialize.""" 96 | self._data = {} 97 | self._errors = {} 98 | 99 | async def async_step_user(self, user_input=None): 100 | """Handle a flow initialized by the user.""" 101 | self._errors = {} 102 | 103 | if user_input is not None: 104 | league_id = user_input[CONF_LEAGUE_ID].upper() 105 | if league_id == "XXX": 106 | self._data.update(user_input) 107 | return await self.async_step_path() 108 | if paths := LEAGUE_MAP.get(league_id): 109 | user_input.update(paths) 110 | self._data.update(user_input) 111 | return self.async_create_entry( 112 | title=self._data[CONF_NAME], data=self._data 113 | ) 114 | self._errors["base"] = "league" 115 | return await self._show_config_form(user_input) 116 | 117 | async def async_step_path(self, user_input: Optional[Dict[str, Any]] = None): 118 | # pylint: disable=deprecated-typing-alias 119 | # pylint: disable=consider-alternative-union-syntax 120 | 121 | """Handle a flow initialized by the user.""" 122 | self._errors = {} 123 | 124 | if user_input is not None: 125 | self._data.update(user_input) 126 | return self.async_create_entry(title=self._data[CONF_NAME], data=self._data) 127 | return await self._show_path_form(user_input) 128 | 129 | async def _show_config_form(self, user_input): 130 | """Show the configuration form to edit location data.""" 131 | 132 | # Defaults 133 | defaults = { 134 | CONF_LEAGUE_ID: DEFAULT_LEAGUE, 135 | CONF_NAME: DEFAULT_NAME, 136 | CONF_TEAM_ID: "", 137 | CONF_CONFERENCE_ID: DEFAULT_CONFERENCE_ID, 138 | } 139 | return self.async_show_form( 140 | step_id="user", 141 | data_schema=_get_schema(self.hass, user_input, defaults), 142 | errors=self._errors, 143 | ) 144 | 145 | async def _show_path_form(self, user_input): 146 | """Show the path form to edit path data.""" 147 | 148 | # Defaults 149 | defaults = { 150 | CONF_SPORT_PATH: "", 151 | CONF_LEAGUE_PATH: "", 152 | } 153 | return self.async_show_form( 154 | step_id="path", 155 | data_schema=_get_path_schema(self.hass, user_input, defaults), 156 | errors=self._errors, 157 | ) 158 | 159 | 160 | @staticmethod 161 | @callback 162 | def async_get_options_flow(config_entry): 163 | return TeamTrackerScoresOptionsFlow(config_entry) 164 | 165 | class TeamTrackerScoresOptionsFlow(config_entries.OptionsFlow): 166 | """Options flow for TeamTracker.""" 167 | 168 | def __init__(self, config_entry): 169 | """Initialize.""" 170 | self.entry = config_entry 171 | self._options = dict(config_entry.options) 172 | self._errors = {} 173 | 174 | async def async_step_init(self, user_input=None): 175 | """Manage options.""" 176 | 177 | if user_input is not None: 178 | self._options.update(user_input) 179 | return self.async_create_entry(title="", data=self._options) 180 | return await self._show_options_form(user_input) 181 | 182 | async def _show_options_form(self, user_input): 183 | """Show the options form to edit location data.""" 184 | 185 | lang = None 186 | if self.entry and self.entry.options and CONF_API_LANGUAGE in self.entry.options: 187 | lang = self.entry.options[CONF_API_LANGUAGE] 188 | 189 | options_schema = vol.Schema( 190 | { 191 | vol.Optional(CONF_API_LANGUAGE, description={"suggested_value": lang}, default=""): cv.string, 192 | } 193 | ) 194 | 195 | return self.async_show_form( 196 | step_id="init", 197 | data_schema=options_schema, 198 | errors=self._errors, 199 | ) -------------------------------------------------------------------------------- /custom_components/teamtracker/const.py: -------------------------------------------------------------------------------- 1 | """ Constants for teamtracker sensor""" 2 | from homeassistant.const import Platform 3 | from datetime import timedelta 4 | 5 | # API 6 | URL_HEAD = "http://site.api.espn.com/apis/site/v2/sports/" 7 | URL_TAIL = "/scoreboard" 8 | API_LIMIT = 50 9 | USER_AGENT = ( 10 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_6) AppleWebKit/605.1.15 (KHTML, like " 11 | "Gecko) Version/15.0 Safari/605.1.15" 12 | ) 13 | 14 | # Config 15 | CONF_API_LANGUAGE = "api_language" 16 | CONF_CONFERENCE_ID = "conference_id" 17 | CONF_LEAGUE_ID = "league_id" 18 | CONF_LEAGUE_PATH = "league_path" 19 | CONF_SPORT_PATH = "sport_path" 20 | CONF_TEAM_ID = "team_id" 21 | 22 | # Sports 23 | AUSTRALIAN_FOOTBALL = "australian-football" 24 | BASEBALL = "baseball" 25 | BASKETBALL = "basketball" 26 | CRICKET = "cricket" 27 | FOOTBALL = "football" 28 | GOLF = "golf" 29 | HOCKEY = "hockey" 30 | MMA = "mma" 31 | RACING = "racing" 32 | RUGBY = "rugby" 33 | SOCCER = "soccer" 34 | TENNIS = "tennis" 35 | VOLLEYBALL = "volleyball" 36 | 37 | # Maps 38 | LEAGUE_MAP = { 39 | "AFL": { 40 | CONF_SPORT_PATH: AUSTRALIAN_FOOTBALL, 41 | CONF_LEAGUE_PATH: "afl", 42 | }, 43 | "MLB": { 44 | CONF_SPORT_PATH: BASEBALL, 45 | CONF_LEAGUE_PATH: "mlb", 46 | }, 47 | "NBA": { 48 | CONF_SPORT_PATH: BASKETBALL, 49 | CONF_LEAGUE_PATH: "nba", 50 | }, 51 | "WNBA": { 52 | CONF_SPORT_PATH: BASKETBALL, 53 | CONF_LEAGUE_PATH: "wnba", 54 | }, 55 | "NCAAM": { 56 | CONF_SPORT_PATH: BASKETBALL, 57 | CONF_LEAGUE_PATH: "mens-college-basketball", 58 | }, 59 | "NCAAW": { 60 | CONF_SPORT_PATH: BASKETBALL, 61 | CONF_LEAGUE_PATH: "womens-college-basketball", 62 | }, 63 | "NCAAF": { 64 | CONF_SPORT_PATH: FOOTBALL, 65 | CONF_LEAGUE_PATH: "college-football", 66 | }, 67 | "NFL": { 68 | CONF_SPORT_PATH: FOOTBALL, 69 | CONF_LEAGUE_PATH: "nfl", 70 | }, 71 | "XFL": { 72 | CONF_SPORT_PATH: FOOTBALL, 73 | CONF_LEAGUE_PATH: "xfl", 74 | }, 75 | "PGA": { 76 | CONF_SPORT_PATH: GOLF, 77 | CONF_LEAGUE_PATH: "pga", 78 | }, 79 | "NHL": { 80 | CONF_SPORT_PATH: HOCKEY, 81 | CONF_LEAGUE_PATH: "nhl", 82 | }, 83 | "UFC": { 84 | CONF_SPORT_PATH: MMA, 85 | CONF_LEAGUE_PATH: "ufc", 86 | }, 87 | "F1": { 88 | CONF_SPORT_PATH: RACING, 89 | CONF_LEAGUE_PATH: "f1", 90 | }, 91 | "IRL": { 92 | CONF_SPORT_PATH: RACING, 93 | CONF_LEAGUE_PATH: "irl", 94 | }, 95 | "NASCAR": { 96 | CONF_SPORT_PATH: RACING, 97 | CONF_LEAGUE_PATH: "nascar-premier", 98 | }, 99 | "BUND": { 100 | CONF_SPORT_PATH: SOCCER, 101 | CONF_LEAGUE_PATH: "ger.1", 102 | }, 103 | "CL": { 104 | CONF_SPORT_PATH: SOCCER, 105 | CONF_LEAGUE_PATH: "uefa.champions", 106 | }, 107 | "CLA": { 108 | CONF_SPORT_PATH: SOCCER, 109 | CONF_LEAGUE_PATH: "conmebol.libertadores", 110 | }, 111 | "EPL": { 112 | CONF_SPORT_PATH: SOCCER, 113 | CONF_LEAGUE_PATH: "eng.1", 114 | }, 115 | "LIGA": { 116 | CONF_SPORT_PATH: SOCCER, 117 | CONF_LEAGUE_PATH: "esp.1", 118 | }, 119 | "LIG1": { 120 | CONF_SPORT_PATH: SOCCER, 121 | CONF_LEAGUE_PATH: "fra.1", 122 | }, 123 | "MLS": { 124 | CONF_SPORT_PATH: SOCCER, 125 | CONF_LEAGUE_PATH: "usa.1", 126 | }, 127 | "NWSL": { 128 | CONF_SPORT_PATH: SOCCER, 129 | CONF_LEAGUE_PATH: "usa.nwsl", 130 | }, 131 | "SERA": { 132 | CONF_SPORT_PATH: SOCCER, 133 | CONF_LEAGUE_PATH: "ita.1", 134 | }, 135 | "WC": { 136 | CONF_SPORT_PATH: SOCCER, 137 | CONF_LEAGUE_PATH: "fifa.world", 138 | }, 139 | "WWC": { 140 | CONF_SPORT_PATH: SOCCER, 141 | CONF_LEAGUE_PATH: "fifa.wwc", 142 | }, 143 | "ATP": { 144 | CONF_SPORT_PATH: TENNIS, 145 | CONF_LEAGUE_PATH: "atp", 146 | }, 147 | "WTA": { 148 | CONF_SPORT_PATH: TENNIS, 149 | CONF_LEAGUE_PATH: "wta", 150 | }, 151 | "NCAAVB": { 152 | CONF_SPORT_PATH: VOLLEYBALL, 153 | CONF_LEAGUE_PATH: "mens-college-volleyball", 154 | }, 155 | "NCAAVBW": { 156 | CONF_SPORT_PATH: VOLLEYBALL, 157 | CONF_LEAGUE_PATH: "womens-college-volleyball", 158 | }, 159 | } 160 | 161 | SPORT_ICON_MAP = { 162 | AUSTRALIAN_FOOTBALL: "mdi:football-australian", 163 | BASEBALL: "mdi:baseball", 164 | BASKETBALL: "mdi:basketball", 165 | CRICKET: "mdi:cricket", 166 | FOOTBALL: "mdi:football", 167 | GOLF: "mdi:golf-tee", 168 | HOCKEY: "mdi:hockey-puck", 169 | MMA: "mdi:karate", 170 | RACING: "mdi:flag-checkered", 171 | RUGBY: "mdi:rugby", 172 | SOCCER: "mdi:soccer", 173 | TENNIS: "mdi:tennis", 174 | VOLLEYBALL: "mdi:volleyball", 175 | } 176 | 177 | # Defaults 178 | DEFAULT_CONFERENCE_ID = "" 179 | DEFAULT_ICON = "mdi:scoreboard" 180 | DEFAULT_LEAGUE = "NFL" 181 | DEFAULT_LOGO = ( 182 | "https://cdn0.iconfinder.com/data/icons/shift-interfaces/32/Error-512.png" 183 | ) 184 | DEFAULT_NAME = "team_tracker" 185 | DEFAULT_PROB = 0.0 186 | DEFAULT_SPORT_PATH = "UNDEFINED_SPORT" 187 | DEFAULT_TIMEOUT = 120 188 | DEFAULT_LAST_UPDATE = "2022-02-02 02:02:02-05:00" 189 | DEFAULT_KICKOFF_IN = "{test} days" 190 | DEFAULT_REFRESH_RATE = timedelta(minutes=10) 191 | RAPID_REFRESH_RATE = timedelta(seconds=5) 192 | 193 | # Services 194 | SERVICE_NAME_CALL_API = "call_api" 195 | 196 | # Misc 197 | TEAM_ID = "" 198 | VERSION = "v0.14.9" 199 | ISSUE_URL = "https://github.com/vasqued2/ha-teamtracker" 200 | DOMAIN = "teamtracker" 201 | ATTRIBUTION = "Data provided by ESPN" 202 | COORDINATOR = "coordinator" 203 | PLATFORMS = [Platform.SENSOR] 204 | -------------------------------------------------------------------------------- /custom_components/teamtracker/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain": "teamtracker", 3 | "name": "Team Tracker", 4 | "codeowners": ["@vasqued2"], 5 | "config_flow": true, 6 | "dependencies": [], 7 | "documentation": "https://github.com/vasqued2/ha-teamtracker", 8 | "iot_class": "cloud_polling", 9 | "issue_tracker": "https://github.com/vasqued2/ha-teamtracker/issues", 10 | "requirements": ["arrow", "aiofiles"], 11 | "version": "0.1" 12 | } -------------------------------------------------------------------------------- /custom_components/teamtracker/services.yaml: -------------------------------------------------------------------------------- 1 | call_api: 2 | name: Teamtracker Call API 3 | description: Sets the teamtracker sensor based on the input parameters, calls the ESPN API, and populates the sensor attributes. 4 | target: 5 | entity: 6 | domain: sensor 7 | integration: teamtracker 8 | fields: 9 | sport_path: 10 | name: Sport 11 | description: Sport path 12 | required: true 13 | example: 'football' 14 | selector: 15 | text: 16 | league_path: 17 | name: League 18 | description: League path 19 | required: true 20 | example: 'nfl' 21 | selector: 22 | text: 23 | team_id: 24 | name: Team 25 | description: Team ID 26 | required: true 27 | example: 'CLE' 28 | selector: 29 | text: 30 | conference_id: 31 | name: Conference 32 | description: Conference ID (only for NCAA) 33 | required: false 34 | example: '5' 35 | selector: 36 | text: -------------------------------------------------------------------------------- /custom_components/teamtracker/set_baseball.py: -------------------------------------------------------------------------------- 1 | """ Baseball specific functionality""" 2 | 3 | from .utils import async_get_value 4 | 5 | 6 | async def async_set_baseball_values( 7 | new_values, event, competition_index, team_index, sensor_name 8 | ) -> bool: 9 | """Set baseball specific values""" 10 | 11 | new_values["clock"] = await async_get_value( 12 | event, "status", "type", "detail" 13 | ) # Inning 14 | if new_values["clock"][:3].lower() in ["bot", "mid"]: 15 | if new_values["team_homeaway"] in [ 16 | "home" 17 | ]: # Home outs, at bat in bottom of inning 18 | new_values["possession"] = new_values["team_id"] 19 | else: # Away outs, at bat in bottom of inning 20 | new_values["possession"] = new_values["opponent_id"] 21 | else: 22 | if new_values["team_homeaway"] in [ 23 | "away" 24 | ]: # Away outs, at bat in top of inning 25 | new_values["possession"] = new_values["team_id"] 26 | else: # Home outs, at bat in top of inning 27 | new_values["possession"] = new_values["opponent_id"] 28 | 29 | new_values["outs"] = await async_get_value( 30 | event, "competitions", 0, "situation", "outs" 31 | ) 32 | new_values["balls"] = await async_get_value( 33 | event, "competitions", 0, "situation", "balls" 34 | ) 35 | new_values["strikes"] = await async_get_value( 36 | event, "competitions", 0, "situation", "strikes" 37 | ) 38 | new_values["on_first"] = await async_get_value( 39 | event, "competitions", 0, "situation", "onFirst" 40 | ) 41 | new_values["on_second"] = await async_get_value( 42 | event, "competitions", 0, "situation", "onSecond" 43 | ) 44 | new_values["on_third"] = await async_get_value( 45 | event, "competitions", 0, "situation", "onThird" 46 | ) 47 | 48 | return True 49 | -------------------------------------------------------------------------------- /custom_components/teamtracker/set_cricket.py: -------------------------------------------------------------------------------- 1 | """ Cricket specific functionality""" 2 | 3 | import logging 4 | 5 | from .utils import async_get_value 6 | 7 | _LOGGER = logging.getLogger(__name__) 8 | 9 | 10 | async def async_set_cricket_values( 11 | new_values, event, competition_index, team_index, lang, sensor_name 12 | ) -> bool: 13 | """Set cricket specific values""" 14 | 15 | oppo_index = 1 - team_index 16 | competition = await async_get_value(event, "competitions", competition_index) 17 | competitor = await async_get_value(competition, "competitors", team_index) 18 | opponent = await async_get_value(competition, "competitors", oppo_index) 19 | 20 | if competition is None or competitor is None or opponent is None: 21 | _LOGGER.debug("%s: async_set_cricket_values() 0: %s", sensor_name, sensor_name) 22 | return False 23 | 24 | new_values["odds"] = await async_get_value(competition, "class", "generalClassCard") 25 | new_values["clock"] = await async_get_value( 26 | competition, "status", "type", "description" 27 | ) 28 | new_values["quarter"] = await async_get_value(competition, "status", "session") 29 | 30 | if await async_get_value(competitor, "linescores", -1, "isBatting"): 31 | new_values["possession"] = await async_get_value(competitor, "id") 32 | if await async_get_value(opponent, "linescores", -1, "isBatting"): 33 | new_values["possession"] = await async_get_value(opponent, "id") 34 | 35 | new_values["last_play"] = await async_get_value(competition, "status", "summary") 36 | 37 | return True 38 | -------------------------------------------------------------------------------- /custom_components/teamtracker/set_golf.py: -------------------------------------------------------------------------------- 1 | """ Golf specific functionality""" 2 | 3 | import logging 4 | 5 | from .utils import async_get_value 6 | 7 | _LOGGER = logging.getLogger(__name__) 8 | 9 | 10 | async def async_set_golf_values( 11 | new_values, event, competition_index, team_index, lang, sensor_name 12 | ) -> bool: 13 | """Set golf specific values""" 14 | 15 | oppo_index = 1 if team_index == 0 else 0 16 | competition = await async_get_value(event, "competitions", competition_index) 17 | competitor = await async_get_value(competition, "competitors", team_index) 18 | opponent = await async_get_value(competition, "competitors", oppo_index) 19 | 20 | if competition is None or competitor is None or opponent is None: 21 | # _LOGGER.debug("%s: async_set_golf_values() 0: %s", sensor_name, sensor_name) 22 | return False 23 | 24 | # _LOGGER.debug("%s: async_set_golf_values() 1: %s %s %s", sensor_name, competition_index, team_index, oppo_index) 25 | 26 | if new_values["state"] in ["IN", "POST"]: 27 | # _LOGGER.debug("%s: async_set_golf_values() 1.1: %s", sensor_name, sensor_name) 28 | 29 | new_values["team_rank"] = await async_get_golf_position(competition, team_index) 30 | new_values["opponent_rank"] = await async_get_golf_position( 31 | competition, oppo_index 32 | ) 33 | else: 34 | # _LOGGER.debug("%s: async_set_golf_values() 1.2: %s", sensor_name, sensor_name) 35 | new_values["team_rank"] = None 36 | new_values["opponent_rank"] = None 37 | 38 | # _LOGGER.debug("%s: async_set_golf_values() 1.3: %s", sensor_name, new_values) 39 | 40 | if new_values["state"] in ["IN", "POST"]: 41 | golf_round = new_values["quarter"] - 1 42 | # _LOGGER.debug("%s: async_set_golf_values() 2: %s", sensor_name, golf_round) 43 | 44 | new_values["team_total_shots"] = await async_get_value( 45 | competitor, "linescores", golf_round, "value", default=0 46 | ) 47 | new_values["team_shots_on_target"] = len( 48 | await async_get_value( 49 | competitor, "linescores", golf_round, "linescores", default=[] 50 | ) 51 | ) 52 | new_values["opponent_total_shots"] = await async_get_value( 53 | opponent, "linescores", golf_round, "value", default=0 54 | ) 55 | new_values["opponent_shots_on_target"] = len( 56 | await async_get_value( 57 | opponent, "linescores", golf_round, "linescores", default=[] 58 | ) 59 | ) 60 | 61 | # _LOGGER.debug("%s: async_set_golf_values() 3: %s", sensor_name, golf_round) 62 | 63 | new_values["last_play"] = "" 64 | for x in range(0, 10): 65 | p = await async_get_golf_position(competition, x) 66 | new_values["last_play"] = new_values["last_play"] + p + ". " 67 | new_values["last_play"] = new_values["last_play"] + await async_get_value( 68 | competition, "competitors", x, "athlete", "shortName" 69 | ) 70 | new_values["last_play"] = ( 71 | new_values["last_play"] 72 | + " (" 73 | + str( 74 | await async_get_value( 75 | competition, "competitors", x, "score", default="" 76 | ) 77 | ) 78 | + "), " 79 | ) 80 | 81 | # _LOGGER.debug("%s: async_set_golf_values() 4: %s", sensor_name, new_values) 82 | new_values["last_play"] = new_values["last_play"][:-1] 83 | 84 | return True 85 | 86 | 87 | async def async_get_golf_position(competition, index) -> str: 88 | """Determine the position of index considering ties if score matches leading or trailing position""" 89 | 90 | t = 0 91 | tie = "" 92 | for x in range(1, index + 1): 93 | if await async_get_value( 94 | competition, "competitors", x, "score", default=1000 95 | ) == await async_get_value( 96 | competition, "competitors", t, "score", default=1001 97 | ): 98 | tie = "T" 99 | else: 100 | tie = "" 101 | t = x 102 | if await async_get_value( 103 | competition, "competitors", index, "score", default=1000 104 | ) == await async_get_value( 105 | competition, "competitors", index + 1, "score", default=1001 106 | ): 107 | tie = "T" 108 | 109 | return tie + str(t + 1) 110 | -------------------------------------------------------------------------------- /custom_components/teamtracker/set_hockey.py: -------------------------------------------------------------------------------- 1 | """ Hockey specific functionality""" 2 | 3 | import logging 4 | 5 | from .utils import async_get_value 6 | 7 | _LOGGER = logging.getLogger(__name__) 8 | 9 | 10 | async def async_set_hockey_values( 11 | new_values, event, competition_index, team_index, sensor_name 12 | ) -> bool: 13 | """Set hockey specific values""" 14 | 15 | oppo_index = 1 - team_index 16 | competition = await async_get_value(event, "competitions", competition_index) 17 | competitor = await async_get_value(competition, "competitors", team_index) 18 | opponent = await async_get_value(competition, "competitors", oppo_index) 19 | 20 | if competition is None or competitor is None or opponent is None: 21 | _LOGGER.debug("%s: async_set_hockey_values() 0: %s", sensor_name, sensor_name) 22 | return False 23 | 24 | # new_values["clock"] = await async_get_value(event, "status", "type", "shortDetail") # Period clock 25 | 26 | new_values["team_shots_on_target"] = 0 27 | for statistic in await async_get_value(opponent, "statistics", default=[]): 28 | if "saves" in await async_get_value(statistic, "name", default=[]): 29 | shots = int(new_values["team_score"]) + int( 30 | await async_get_value(statistic, "displayValue", default=0) 31 | ) 32 | new_values["team_shots_on_target"] = str(shots) 33 | 34 | new_values["opponent_shots_on_target"] = 0 35 | for statistic in await async_get_value(competitor, "statistics", default=[]): 36 | if "saves" in await async_get_value(statistic, "name", default=[]): 37 | shots = int(new_values["opponent_score"]) + int( 38 | await async_get_value(statistic, "displayValue", default=0) 39 | ) 40 | new_values["opponent_shots_on_target"] = str(shots) 41 | 42 | return True 43 | -------------------------------------------------------------------------------- /custom_components/teamtracker/set_mma.py: -------------------------------------------------------------------------------- 1 | """ MMA specific functionality""" 2 | 3 | import logging 4 | 5 | from .utils import async_get_value 6 | 7 | _LOGGER = logging.getLogger(__name__) 8 | 9 | 10 | async def async_set_mma_values( 11 | new_values, event, competition_index, team_index, lang, sensor_name 12 | ) -> bool: 13 | """Set MMA specific values""" 14 | 15 | _LOGGER.debug("%s: async_set_mma_values() 1: %s", sensor_name, sensor_name) 16 | 17 | oppo_index = 1 - team_index 18 | competition = await async_get_value(event, "competitions", competition_index) 19 | competitor = await async_get_value(competition, "competitors", team_index) 20 | opponent = await async_get_value(competition, "competitors", oppo_index) 21 | 22 | if competition is None or competitor is None or opponent is None: 23 | # _LOGGER.debug("%s: async_set_mma_values() 1.1: %s", sensor_name, sensor_name) 24 | return False 25 | 26 | # _LOGGER.debug("%s: async_set_mma_values() 2: %s %s %s", sensor_name, competition_index, team_index, oppo_index) 27 | 28 | new_values["event_name"] = await async_get_value(event, "name") 29 | 30 | t = 0 31 | o = 0 32 | for ls in range( 33 | 0, 34 | len( 35 | await async_get_value( 36 | competitor, "linescores", -1, "linescores", default=[] 37 | ) 38 | ), 39 | ): 40 | # _LOGGER.debug("%s: async_set_mma_values() 2.1: %s", sensor_name, ls) 41 | 42 | if await async_get_value( 43 | competitor, "linescores", -1, "linescores", ls, "value", default=0 44 | ) > await async_get_value( 45 | opponent, "linescores", -1, "linescores", ls, "value", default=0 46 | ): 47 | t = t + 1 48 | if await async_get_value( 49 | competitor, "linescores", -1, "linescores", ls, "value", default=0 50 | ) < await async_get_value( 51 | opponent, "linescores", -1, "linescores", ls, "value", default=0 52 | ): 53 | o = o + 1 54 | 55 | new_values["team_score"] = t 56 | new_values["opponent_score"] = o 57 | if t == o: 58 | # _LOGGER.debug("%s: async_set_mma_values() 3: %s", sensor_name, sensor_name) 59 | if await async_get_value(competitor, "winner", default=False): 60 | new_values["team_score"] = "W" 61 | new_values["opponent_score"] = "L" 62 | if await async_get_value(opponent, "winner", default=False): 63 | new_values["team_score"] = "L" 64 | new_values["opponent_score"] = "W" 65 | 66 | # _LOGGER.debug("%s: async_set_mma_values() 4: %s %s %s", sensor_name, competition_index, team_index, oppo_index) 67 | new_values["last_play"] = await async_get_prior_fights(event, sensor_name) 68 | 69 | # _LOGGER.debug("%s: async_set_mma_values() 5: %s %s %s", sensor_name, competition_index, team_index, oppo_index) 70 | 71 | return True 72 | 73 | 74 | async def async_get_prior_fights(event, sensor_name) -> str: 75 | """Get the results of the prior fights""" 76 | 77 | prior_fights = "" 78 | 79 | # _LOGGER.debug("%s: async_get_prior_fights() 1: %s", sensor_name, sensor_name) 80 | c = 1 81 | for competition in await async_get_value(event, "competitions", default=[]): 82 | # _LOGGER.debug("%s: async_get_prior_fights() 2: %s", sensor_name, sensor_name) 83 | 84 | if ( 85 | str( 86 | await async_get_value( 87 | competition, "status", "type", "state", default="NOT_FOUND" 88 | ) 89 | ).upper() 90 | == "POST" 91 | ): 92 | # _LOGGER.debug("%s: async_get_prior_fights() 2.1: %s", sensor_name, sensor_name) 93 | 94 | prior_fights = prior_fights + str(c) + ". " 95 | if await async_get_value( 96 | competition, "competitors", 0, "winner", default=False 97 | ): 98 | prior_fights = ( 99 | prior_fights 100 | + "*" 101 | + str( 102 | await async_get_value( 103 | competition, 104 | "competitors", 105 | 0, 106 | "athlete", 107 | "shortName", 108 | default="{shortName}", 109 | ) 110 | ).upper() 111 | ) 112 | else: 113 | prior_fights = prior_fights + str( 114 | await async_get_value( 115 | competition, 116 | "competitors", 117 | 0, 118 | "athlete", 119 | "shortName", 120 | default="{shortName}", 121 | ) 122 | ) 123 | prior_fights = prior_fights + " v. " 124 | if await async_get_value( 125 | competition, "competitors", 1, "winner", default=False 126 | ): 127 | prior_fights = ( 128 | prior_fights 129 | + str( 130 | await async_get_value( 131 | competition, 132 | "competitors", 133 | 1, 134 | "athlete", 135 | "shortName", 136 | default="{shortName}", 137 | ) 138 | ).upper() 139 | + "*" 140 | ) 141 | else: 142 | prior_fights = prior_fights + str( 143 | await async_get_value( 144 | competition, 145 | "competitors", 146 | 1, 147 | "athlete", 148 | "shortName", 149 | default="{shortName}", 150 | ) 151 | ) 152 | f1 = 0 153 | f2 = 0 154 | t = 0 155 | # _LOGGER.debug("%s: async_get_prior_fights() 2.2: %s", sensor_name, len(await async_get_value(competition, "competitors", 0, "linescores", 0, "linescores", default=[]))) 156 | for ls in range( 157 | 0, 158 | len( 159 | await async_get_value( 160 | competition, 161 | "competitors", 162 | 0, 163 | "linescores", 164 | 0, 165 | "linescores", 166 | default=[], 167 | ) 168 | ), 169 | ): 170 | # _LOGGER.debug("%s: async_get_prior_fights() 2.3: %s %s %s %s", sensor_name, ls, f1, f2, t) 171 | if int( 172 | await async_get_value( 173 | competition, 174 | "competitors", 175 | 0, 176 | "linescores", 177 | 0, 178 | "linescores", 179 | ls, 180 | "value", 181 | default=0, 182 | ) 183 | ) > int( 184 | await async_get_value( 185 | competition, 186 | "competitors", 187 | 1, 188 | "linescores", 189 | 0, 190 | "linescores", 191 | ls, 192 | "value", 193 | default=0, 194 | ) 195 | ): 196 | f1 = f1 + 1 197 | elif int( 198 | await async_get_value( 199 | competition, 200 | "competitors", 201 | 0, 202 | "linescores", 203 | 0, 204 | "linescores", 205 | ls, 206 | "value", 207 | default=0, 208 | ) 209 | ) < int( 210 | await async_get_value( 211 | competition, 212 | "competitors", 213 | 1, 214 | "linescores", 215 | 0, 216 | "linescores", 217 | ls, 218 | "value", 219 | default=0, 220 | ) 221 | ): 222 | f2 = f2 + 1 223 | else: 224 | t = t + 1 225 | 226 | # _LOGGER.debug("%s: async_get_prior_fights() 3: %s %s %s %s %s", sensor_name, f1, f2, t, prior_fights) 227 | 228 | if f1 == 0 and f2 == 0 and t == 0: 229 | prior_fights = ( 230 | prior_fights 231 | + " (KO/TKO/Sub: R" 232 | + str( 233 | await async_get_value( 234 | competition, "status", "period", default="{period}" 235 | ) 236 | ) 237 | + "@" 238 | + str( 239 | await async_get_value( 240 | competition, 241 | "status", 242 | "displayClock", 243 | default="{displayClock}", 244 | ) 245 | ) 246 | ) 247 | else: 248 | prior_fights = prior_fights + " (Dec: " + str(f1) + "-" + str(f2) 249 | if t != 0: 250 | prior_fights = prior_fights + "-" + str(t) 251 | 252 | prior_fights = prior_fights + "); " 253 | c = c + 1 254 | # _LOGGER.debug("%s: async_get_prior_fights() 4: %s", sensor_name, prior_fights) 255 | 256 | return prior_fights 257 | -------------------------------------------------------------------------------- /custom_components/teamtracker/set_racing.py: -------------------------------------------------------------------------------- 1 | """ Racing specific functionality""" 2 | 3 | import logging 4 | 5 | from .utils import async_get_value 6 | 7 | _LOGGER = logging.getLogger(__name__) 8 | race_laps = {} 9 | 10 | async def async_set_racing_values( 11 | new_values, event, competition_index, team_index, lang, sensor_name 12 | ) -> bool: 13 | """Set racing specific values""" 14 | 15 | 16 | # 17 | # Pylint doesn't recognize values set by setdefault() method 18 | # 19 | global race_laps # pylint: disable=global-variable-not-assigned 20 | 21 | # _LOGGER.debug("%s: async_set_racing_values() 0: %s", sensor_name, new_values) 22 | 23 | oppo_index = 1 if team_index == 0 else 0 24 | competition = await async_get_value(event, "competitions", competition_index) 25 | competitor = await async_get_value(competition, "competitors", team_index) 26 | opponent = await async_get_value(competition, "competitors", oppo_index) 27 | 28 | if competition is None or competitor is None or opponent is None: 29 | _LOGGER.debug("%s: async_set_racing_values() 0: %s", sensor_name, sensor_name) 30 | return False 31 | 32 | city = await async_get_value(event, "circuit", "address", "city") 33 | country = await async_get_value(event, "circuit", "address", "country") 34 | # _LOGGER.debug("%s: async_set_racing_values() 1: %s", sensor_name, new_values) 35 | 36 | if city is not None: 37 | new_values["location"] = "%s, %s" % (city, country) 38 | else: 39 | new_values["location"] = country 40 | 41 | new_values["team_score"] = team_index + 1 42 | new_values["opponent_score"] = oppo_index + 1 43 | # _LOGGER.debug("%s: async_set_racing_values() 2: %s", sensor_name, new_values) 44 | 45 | if new_values["state"] == "PRE": 46 | new_values["team_rank"] = team_index + 1 47 | new_values["opponent_rank"] = oppo_index + 1 48 | # _LOGGER.debug("%s: async_set_racing_values() 3: %s", sensor_name, new_values) 49 | 50 | race_key = ( 51 | str(new_values["league"]) 52 | + "-" 53 | + str(new_values["event_name"]) 54 | ) 55 | new_values["team_total_shots"] = await async_get_value( 56 | competition, "status", "period", 57 | default=race_laps.setdefault(race_key, 0), 58 | ) 59 | race_laps.update({race_key: new_values["team_total_shots"]}) 60 | 61 | new_values["quarter"] = await async_get_value(competition, "type", "abbreviation") 62 | # _LOGGER.debug("%s: async_set_racing_values() 4: %s", sensor_name, new_values) 63 | 64 | new_values["last_play"] = "" 65 | for x in range(0, 10): 66 | new_values["last_play"] = ( 67 | new_values["last_play"] 68 | + str( 69 | await async_get_value(competition, "competitors", x, "order", default=x) 70 | ) 71 | + ". " 72 | ) 73 | new_values["last_play"] = ( 74 | new_values["last_play"] 75 | + str( 76 | await async_get_value( 77 | competition, 78 | "competitors", 79 | x, 80 | "athlete", 81 | "shortName", 82 | default="{shortName}", 83 | ) 84 | ) 85 | + ", " 86 | ) 87 | new_values["last_play"] = new_values["last_play"][:-1] 88 | 89 | return True 90 | -------------------------------------------------------------------------------- /custom_components/teamtracker/set_soccer.py: -------------------------------------------------------------------------------- 1 | """ Soccer specific functionality""" 2 | 3 | import logging 4 | 5 | from .utils import async_get_value 6 | 7 | _LOGGER = logging.getLogger(__name__) 8 | 9 | 10 | async def async_set_soccer_values( 11 | new_values, event, competition_index, team_index, sensor_name 12 | ) -> bool: 13 | """Set soccer specific values""" 14 | 15 | teamPP = None 16 | oppoPP = None 17 | 18 | oppo_index = 1 - team_index 19 | competition = await async_get_value(event, "competitions", competition_index) 20 | competitor = await async_get_value(competition, "competitors", team_index) 21 | opponent = await async_get_value(competition, "competitors", oppo_index) 22 | 23 | if competition is None or competitor is None or opponent is None: 24 | _LOGGER.debug("%s: async_set_soccer_values() 0: %s", sensor_name, sensor_name) 25 | return False 26 | 27 | # _LOGGER.debug("%s: async_set_soccer_values() 1: %s", sensor_name, sensor_name) 28 | 29 | new_values["team_shots_on_target"] = 0 30 | new_values["team_total_shots"] = 0 31 | for statistic in await async_get_value(competitor, "statistics", default=[]): 32 | if "shotsOnTarget" in await async_get_value(statistic, "name", default=[]): 33 | new_values["team_shots_on_target"] = await async_get_value( 34 | statistic, "displayValue" 35 | ) 36 | if "totalShots" in await async_get_value(statistic, "name", default=[]): 37 | new_values["team_total_shots"] = await async_get_value( 38 | statistic, "displayValue" 39 | ) 40 | if "possessionPct" in await async_get_value(statistic, "name", default=[]): 41 | teamPP = await async_get_value(statistic, "displayValue") 42 | 43 | # _LOGGER.debug("%s: async_set_soccer_values() 2: %s", sensor_name, sensor_name) 44 | 45 | new_values["opponent_shots_on_target"] = 0 46 | new_values["opponent_total_shots"] = 0 47 | for statistic in await async_get_value(opponent, "statistics", default=[]): 48 | if "shotsOnTarget" in await async_get_value(statistic, "name", default=[]): 49 | new_values["opponent_shots_on_target"] = await async_get_value( 50 | statistic, "displayValue" 51 | ) 52 | if "totalShots" in await async_get_value(statistic, "name", default=[]): 53 | new_values["opponent_total_shots"] = await async_get_value( 54 | statistic, "displayValue" 55 | ) 56 | if "possessionPct" in await async_get_value(statistic, "name", default=[]): 57 | oppoPP = await async_get_value(statistic, "displayValue") 58 | 59 | # _LOGGER.debug("%s: async_set_soccer_values() 3: %s", sensor_name, sensor_name) 60 | 61 | new_values["last_play"] = "" 62 | if teamPP and oppoPP: 63 | new_values["last_play"] = ( 64 | new_values["team_abbr"] 65 | + " " 66 | + str(teamPP) 67 | + "%, " 68 | + new_values["opponent_abbr"] 69 | + " " 70 | + str(oppoPP) 71 | + "%; " 72 | ) 73 | for detail in await async_get_value( 74 | event, "competitions", 0, "details", default=[] 75 | ): 76 | try: 77 | mls_team_id = await async_get_value(detail, "team", "id", default=0) 78 | 79 | new_values["last_play"] = ( 80 | new_values["last_play"] 81 | + " " 82 | + await async_get_value( 83 | detail, "clock", "displayValue", default="{clock}" 84 | ) 85 | ) 86 | new_values["last_play"] = ( 87 | new_values["last_play"] 88 | + " " 89 | + await async_get_value(detail, "type", "text", default="{type}") 90 | ) 91 | new_values["last_play"] = ( 92 | new_values["last_play"] 93 | + ": " 94 | + await async_get_value( 95 | detail, 96 | "athletesInvolved", 97 | 0, 98 | "displayName", 99 | default="{displayName}", 100 | ) 101 | ) 102 | if mls_team_id == new_values["team_id"]: 103 | new_values["last_play"] = ( 104 | new_values["last_play"] + " (" + new_values["team_abbr"] + ")" 105 | ) 106 | else: 107 | new_values["last_play"] = ( 108 | new_values["last_play"] 109 | + " (" 110 | + new_values["opponent_abbr"] 111 | + ") " 112 | ) 113 | except: 114 | new_values["last_play"] = new_values["last_play"] + " {last_play} " 115 | 116 | return True 117 | -------------------------------------------------------------------------------- /custom_components/teamtracker/set_tennis.py: -------------------------------------------------------------------------------- 1 | """ Tennis specific functionality""" 2 | 3 | import logging 4 | 5 | from .utils import async_get_value 6 | 7 | _LOGGER = logging.getLogger(__name__) 8 | 9 | 10 | async def async_set_tennis_values( 11 | new_values, event, grouping_index, competition_index, team_index, lang, sensor_name 12 | ) -> bool: 13 | """Set tennis specific values""" 14 | 15 | # _LOGGER.debug("%s: async_set_tennis_values() 0: %s %s %s", sensor_name, sensor_name, grouping_index, competition_index) 16 | 17 | oppo_index = 1 - team_index 18 | 19 | grouping = await async_get_value(event, "groupings", grouping_index) 20 | if grouping is None: 21 | competition = await async_get_value(event, "competitions", competition_index) 22 | else: 23 | competition = await async_get_value(grouping, "competitions", competition_index) 24 | competitor = await async_get_value(competition, "competitors", team_index) 25 | opponent = await async_get_value(competition, "competitors", oppo_index) 26 | 27 | if competition is None or competitor is None or opponent is None: 28 | # _LOGGER.debug("%s: async_set_tennis_values() 0.1: %s", sensor_name, sensor_name) 29 | return False 30 | 31 | # remaining_games = ( 32 | # len(await async_get_value(event, "competitions", default=[])) 33 | # - competition_index 34 | # ) 35 | # new_values["odds"] = 1 << remaining_games.bit_length() # Game is in the round of X 36 | # # _LOGGER.debug("%s: async_set_tennis_values() 1: %s %s %s", sensor_name, remaining_games, len(event["competitions"]), competition_index) 37 | 38 | new_values["location"] = await async_get_value( 39 | competition, 40 | "venue", 41 | "court" 42 | ) 43 | new_values["down_distance_text"] = await async_get_value( 44 | competition, 45 | "round", 46 | "displayName" 47 | ) 48 | new_values["overunder"] = await async_get_value( 49 | competition, 50 | "type", 51 | "text" 52 | ) 53 | new_values["team_rank"] = await async_get_value(competitor, "tournamentSeed") 54 | new_values["opponent_rank"] = await async_get_value(opponent, "tournamentSeed") 55 | 56 | new_values["clock"] = await async_get_value( 57 | competition, 58 | "status", 59 | "type", 60 | "detail", 61 | default=await async_get_value(event, "status", "type", "shortDetail"), 62 | ) 63 | 64 | # _LOGGER.debug("%s: async_set_tennis_values() 2: %s", sensor_name, sensor_name) 65 | 66 | new_values["team_score"] = await async_get_value(competitor, "score") 67 | # _LOGGER.debug("%s: async_set_tennis_values() 3: %s", sensor_name, sensor_name) 68 | 69 | new_values["opponent_score"] = await async_get_value(opponent, "score") 70 | # _LOGGER.debug("%s: async_set_tennis_values() 4: %s", sensor_name, sensor_name) 71 | 72 | new_values["team_score"] = await async_get_value( 73 | competitor, "linescores", -1, "value" 74 | ) 75 | # _LOGGER.debug("%s: async_set_tennis_values() 5: %s", sensor_name, sensor_name) 76 | new_values["opponent_score"] = await async_get_value( 77 | opponent, "linescores", -1, "value" 78 | ) 79 | # _LOGGER.debug("%s: async_set_tennis_values() 5.1: %s", sensor_name, sensor_name) 80 | new_values["team_shots_on_target"] = await async_get_value( 81 | competitor, "linescores", -1, "tiebreak" 82 | ) 83 | # _LOGGER.debug("%s: async_set_tennis_values() 5.2: %s", sensor_name, sensor_name) 84 | new_values["opponent_shots_on_target"] = await async_get_value( 85 | opponent, "linescores", -1, "tiebreak" 86 | ) 87 | 88 | # _LOGGER.debug("%s: async_set_tennis_values() 6: %s", sensor_name, sensor_name) 89 | 90 | if new_values["state"] == "POST": 91 | new_values["team_score"] = 0 92 | new_values["opponent_score"] = 0 93 | # _LOGGER.debug("%s: async_set_tennis_values() 6.1: %s", sensor_name, sensor_name) 94 | 95 | for x in range( 96 | 0, len(await async_get_value(competitor, "linescores", default=[])) 97 | ): 98 | if int( 99 | await async_get_value(competitor, "linescores", x, "value", default=0) 100 | ) > int( 101 | await async_get_value(opponent, "linescores", x, "value", default=0) 102 | ): 103 | new_values["team_score"] = new_values["team_score"] + 1 104 | else: 105 | new_values["opponent_score"] = new_values["opponent_score"] + 1 106 | 107 | new_values["last_play"] = "" 108 | # _LOGGER.debug("%s: async_set_tennis_values() 7: %s", sensor_name, await async_get_value(competitor, "linescores")) 109 | sets = len(await async_get_value(competitor, "linescores", default=[])) 110 | 111 | # _LOGGER.debug("%s: async_set_tennis_values() 8: %s", sensor_name, sets) 112 | 113 | for x in range(0, sets): 114 | new_values["last_play"] = new_values["last_play"] + " Set " + str(x + 1) + ": " 115 | new_values["last_play"] = ( 116 | new_values["last_play"] 117 | + str( 118 | await async_get_value(competitor, "athlete", "shortName", 119 | default=await async_get_value(competitor, "roster", "shortDisplayName", 120 | default="{shortName}") 121 | ) 122 | ) 123 | + " " 124 | ) 125 | new_values["last_play"] = ( 126 | new_values["last_play"] 127 | + str( 128 | int( 129 | await async_get_value( 130 | competitor, "linescores", x, "value", default=0 131 | ) 132 | ) 133 | ) 134 | + " " 135 | ) 136 | new_values["last_play"] = ( 137 | new_values["last_play"] 138 | + str( 139 | await async_get_value(opponent, "athlete", "shortName", 140 | default=await async_get_value(opponent, "roster", "shortDisplayName", 141 | default="{shortName}") 142 | ) 143 | ) 144 | + " " 145 | ) 146 | new_values["last_play"] = ( 147 | new_values["last_play"] 148 | + str( 149 | int( 150 | await async_get_value(opponent, "linescores", x, "value", default=0) 151 | ) 152 | ) 153 | + "; " 154 | ) 155 | 156 | new_values["team_sets_won"] = 0 157 | new_values["opponent_sets_won"] = 0 158 | for x in range(0, sets - 1): 159 | if await async_get_value( 160 | competitor, "linescores", x, "value", default=0 161 | ) > await async_get_value(opponent, "linescores", x, "value", default=0): 162 | new_values["team_sets_won"] = new_values["team_sets_won"] + 1 163 | else: 164 | new_values["opponent_sets_won"] = new_values["opponent_sets_won"] + 1 165 | 166 | # _LOGGER.debug("%s: async_set_tennis_values() 9: %s", sensor_name, new_values) 167 | 168 | return True 169 | -------------------------------------------------------------------------------- /custom_components/teamtracker/set_volleyball.py: -------------------------------------------------------------------------------- 1 | """ Volleyball specific functionality""" 2 | 3 | import logging 4 | 5 | from .utils import async_get_value 6 | 7 | _LOGGER = logging.getLogger(__name__) 8 | 9 | 10 | async def async_set_volleyball_values( 11 | new_values, event, competition_index, team_index, sensor_name 12 | ) -> bool: 13 | """Set volleyball specific values""" 14 | 15 | oppo_index = 1 - team_index 16 | competition = await async_get_value(event, "competitions", competition_index) 17 | competitor = await async_get_value(competition, "competitors", team_index) 18 | opponent = await async_get_value(competition, "competitors", oppo_index) 19 | 20 | if competition is None or competitor is None or opponent is None: 21 | _LOGGER.debug( 22 | "%s: async_set_volleyball_values() 0: %s", sensor_name, sensor_name 23 | ) 24 | return False 25 | 26 | new_values["clock"] = await async_get_value( 27 | event, "status", "type", "detail" 28 | ) # Set 29 | new_values["team_sets_won"] = new_values["team_score"] 30 | new_values["opponent_sets_won"] = new_values["opponent_score"] 31 | 32 | if new_values["state"] == "IN": 33 | new_values["team_score"] = await async_get_value( 34 | competitor, "linescores", -1, "value", default=0 35 | ) 36 | new_values["opponent_score"] = await async_get_value( 37 | opponent, "linescores", -1, "value", default=0 38 | ) 39 | 40 | new_values["last_play"] = "" 41 | sets = len(await async_get_value(competitor, "linescores", default=[])) 42 | 43 | for x in range(0, sets): 44 | new_values["last_play"] = new_values["last_play"] + " Set " + str(x + 1) + ": " 45 | new_values["last_play"] = ( 46 | new_values["last_play"] + new_values["team_abbr"] + " " 47 | ) 48 | new_values["last_play"] = ( 49 | new_values["last_play"] 50 | + str( 51 | int( 52 | await async_get_value( 53 | competitor, "linescores", x, "value", default=0 54 | ) 55 | ) 56 | ) 57 | + " " 58 | ) 59 | new_values["last_play"] = ( 60 | new_values["last_play"] + new_values["opponent_abbr"] + " " 61 | ) 62 | new_values["last_play"] = ( 63 | new_values["last_play"] 64 | + str( 65 | int( 66 | await async_get_value(opponent, "linescores", x, "value", default=0) 67 | ) 68 | ) 69 | + "; " 70 | ) 71 | return True 72 | -------------------------------------------------------------------------------- /custom_components/teamtracker/translations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "error": { 4 | "league": "Invalid league entered." 5 | }, 6 | "step": { 7 | "user": { 8 | "description": "Enter the League and Team to create the sensor.\n\nLeague: NFL, NCAAF, MLB, NBA, WNBA, NCAAM, NCAAW, NCAAVB, NCAAVBW, MLS, NWSL, BUND, CL, CLA, EPL, LIGA, LIG1, SERA, WC, PGA, ATP, WTA, F1, IRL, XFL\n\nTeam: 2-4 letter team abreviation used on ESPN's scoreboards and stats pages\n\nConference Number: For NCAA football and basketball only. See README.md for valid values. Leave blank to only include games with ranked teams.", 9 | "data": { 10 | "league_id": "League", 11 | "team_id": "Team/Athlete", 12 | "name": "Friendly Name", 13 | "timeout": "Update Timeout (in seconds)", 14 | "conference_id": "Conference Number" 15 | } 16 | }, 17 | "path": { 18 | "title": "Custom API Configurator", 19 | "description": "Enter the 'sport' and 'league' portion of the ESPN API path to use a self-configured API", 20 | "data": { 21 | "sport_path": "Sport Path", 22 | "league_path": "League Path" 23 | } 24 | } 25 | } 26 | }, 27 | "options": { 28 | "step": { 29 | "init": { 30 | "title": "Team Tracker Options", 31 | "description": "Enter the 2-character language code to use for the API call.", 32 | "data": { 33 | "api_language": "API Language" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /custom_components/teamtracker/translations/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "error": { 4 | "league": "Se ha introducido una liga no válida." 5 | }, 6 | "step": { 7 | "user": { 8 | "description": "Introduce la Liga y el Equipo para crear el sensor.\n\nLiga: NFL, NCAAF, MLB, NBA, WNBA, NCAAM, NCAAW, NCAAVB, NCAAVBW, MLS, NWSL, BUND, CL, CLA, EPL, LIGA, LIG1, SERA, WC, PGA, ATP, WTA, F1, IRL, XFL\n\nTeam: 2-4 letter team abreviation used on ESPN's scoreboards and stats pages\n\nConference Number: For NCAA football and basketball only. See README.md for valid values. Leave blank to only include games with ranked teams.", 9 | "data": { 10 | "league_id": "Liga", 11 | "team_id": "Equipo/Atleta", 12 | "name": "Nombre descriptivo", 13 | "timeout": "Tiempo de espera de actualización (en segundos)", 14 | "conference_id": "Número de conferencia" 15 | } 16 | }, 17 | "path": { 18 | "title": "Configurador personalizado de API", 19 | "description": "Introduzca la parte 'deporte' y 'liga' de la ruta de la API de ESPN para utilizar una API autoconfigurada", 20 | "data": { 21 | "sport_path": "Ruta deporte", 22 | "league_path": "Ruta liga" 23 | } 24 | } 25 | } 26 | }, 27 | "options": { 28 | "step": { 29 | "init": { 30 | "title": "Opciones de Team Tracker", 31 | "description": "Ingrese el código de idioma de 2 caracteres que se usará para la llamada API.", 32 | "data": { 33 | "api_language": "Lenguaje API" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /custom_components/teamtracker/translations/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "error": { 4 | "league": "Entrée du championnat invalide." 5 | }, 6 | "step": { 7 | "user": { 8 | "description": "Entrée le championnat et l'équipe pour créer le sensor.\n\nChampionnat: NFL, NCAAF, MLB, NBA, WNBA, NCAAM, NCAAW, NCAAVB, NCAAVBW, MLS, NWSL, BUND, CL, CLA, EPL, LIGA, LIG1, SERA, WC, PGA, ATP, WTA, F1, IRL, XFL\n\nEquipe: Abréviation de l'équipe en 2-4 lettres comme utilisé sur le tableau des scores ESPN et la page des statistiques\n\nNuméro de Conference: Pour NCAA football et basketball seulement. voir README.md pour les bonnes valeurs. Laissez le champs videpour inclure uniquement des sports avec classements d'équipes.", 9 | "data": { 10 | "league_id": "Championnat", 11 | "team_id": "Equipe/Athlète", 12 | "name": "Nom du sensor", 13 | "timeout": "Délai d'attente de la mise a jour (en secondes)", 14 | "conference_id": "Numéro de la Conférence" 15 | } 16 | }, 17 | "path": { 18 | "title": "API Configuration", 19 | "description": "Entrez la partie du 'sport' et du 'championnat' de l'API ESPN pour utilisé une auto-configuration de l'API", 20 | "data": { 21 | "sport_path": "Choix du sport", 22 | "league_path": "Choix du championnat" 23 | } 24 | } 25 | } 26 | }, 27 | "options": { 28 | "step": { 29 | "init": { 30 | "title": "Options de suivi d'équipe", 31 | "description": "Saisissez le code de langue à 2 caractères à utiliser pour l'appel API.", 32 | "data": { 33 | "api_language": "Langage API" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /custom_components/teamtracker/translations/pt-BR.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "error": { 4 | "league": "Liga inválida inserida." 5 | }, 6 | "step": { 7 | "user": { 8 | "title": "Rastreador de equipe", 9 | "description": "Digite a Liga e a Equipe para criar o sensor.\n\nLiga: NFL, NCAAF, MLB, NBA, WNBA, NCAAM, NCAAW, NCAAVB, NCAAVBW, MLS, NWSL, BUND, CL, CLA, EPL, LIGA, LIG1, SERA, WC, PGA, ATP, WTA, F1, IRL, XFL\n\nEquipe : abreviação de equipe de 2 a 4 letras usada nos placares e nas páginas de estatísticas da ESPN\n\nNúmero da conferência: somente para futebol e basquete da NCAA. Consulte README.md para valores válidos. Deixe em branco para incluir apenas jogos com equipes classificadas.", 10 | "data": { 11 | "league_id": "Liga", 12 | "team_id": "Time", 13 | "name": "Nome amigável", 14 | "timeout": "Tempo limite de atualização (em segundos)", 15 | "conference_id": "Número da Conferência" 16 | } 17 | }, 18 | "path": { 19 | "title": "Configurador de API personalizado", 20 | "description": "Insira a parte 'sport' e 'league' do caminho da API ESPN para usar uma API autoconfigurada", 21 | "data": { 22 | "sport_path": "Caminho do esporte", 23 | "league_path": "Caminho da Liga" 24 | } 25 | } 26 | } 27 | }, 28 | "options": { 29 | "step": { 30 | "init": { 31 | "title": "Opções do rastreador de equipe", 32 | "description": "Digite o código de idioma de dois caracteres a ser usado para a chamada de API.", 33 | "data": { 34 | "api_language": "Linguagem API" 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /custom_components/teamtracker/translations/sk.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "error": { 4 | "league": "Bola zadaná neplatná liga." 5 | }, 6 | "step": { 7 | "user": { 8 | "description": "Zadajte ligu a tím a vytvorte senzor.\n\nLiga: NFL, NCAAF, MLB, NBA, WNBA, NCAAM, NCAAW, NCAAVB, NCAAVBW, MLS, NWSL, BUND, CL, CLA, EPL, LIGA, LIG1, SERA, WC, PGA, ATP, WTA, F1, IRL, XFL\n\nTeam: 2-4-písmenová skratka tímu používaná na výsledkovej tabuli ESPN a na stránkach so štatistikami\n\nKonferenčné číslo: Len pre futbal a basketbal NCAA. Platné hodnoty nájdete v súbore README.md. Ak chcete zahrnúť iba hry s hodnotenými tímami, nechajte pole prázdne.", 9 | "data": { 10 | "league_id": "Liga", 11 | "team_id": "Tím/Športovec", 12 | "name": "Priateľské meno", 13 | "timeout": "Časový limit aktualizácie (v sekundách)", 14 | "conference_id": "Číslo konferencie" 15 | } 16 | }, 17 | "path": { 18 | "title": "Vlastný konfigurátor API", 19 | "description": "Zadajte 'šport' a 'ligu' časť cesty ESPN API na použitie samokonfigurovaného API", 20 | "data": { 21 | "sport_path": "Športová cesta", 22 | "league_path": "Ligová cesta" 23 | } 24 | } 25 | } 26 | }, 27 | "options": { 28 | "step": { 29 | "init": { 30 | "title": "Možnosti sledovania tímu", 31 | "description": "Zadajte 2-miestny kód jazyka, ktorý sa má použiť pre volanie API.", 32 | "data": { 33 | "api_language": "Jazyk API" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /custom_components/teamtracker/utils.py: -------------------------------------------------------------------------------- 1 | """ Miscellaneous Utilities """ 2 | 3 | import logging 4 | 5 | _LOGGER = logging.getLogger(__name__) 6 | 7 | 8 | # 9 | # Traverse json and return the value at the end of the chain of keys. 10 | # json - json to be traversed 11 | # *keys - list of keys to use to retrieve the value 12 | # default - default value to be returned if a key is missing 13 | # 14 | async def async_get_value(json, *keys, default=None): 15 | """Traverse the json using keys to return the associated value, or default if invalid keys""" 16 | 17 | j = json 18 | try: 19 | for k in keys: 20 | j = j[k] 21 | return j 22 | except: 23 | return default 24 | -------------------------------------------------------------------------------- /hacs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Team Tracker", 3 | "homeassistant": "0.95.4", 4 | "render_readme": true 5 | } 6 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | target-version = ["py310", "py311"] 3 | extend-exclude = "/generated/" 4 | 5 | [tool.isort] 6 | # https://github.com/PyCQA/isort/wiki/isort-Settings 7 | profile = "black" 8 | # will group `import x` and `from x import` of the same module. 9 | force_sort_within_sections = true 10 | known_first_party = [ 11 | "homeassistant", 12 | "tests", 13 | ] 14 | forced_separate = [ 15 | "tests", 16 | ] 17 | combine_as_imports = true 18 | 19 | [tool.pylint.MAIN] 20 | py-version = "3.11" 21 | ignore = [ 22 | "tests", 23 | ] 24 | # Use a conservative default here; 2 should speed up most setups and not hurt 25 | # any too bad. Override on command line as appropriate. 26 | jobs = 2 27 | init-hook = """\ 28 | from pathlib import Path; \ 29 | import sys; \ 30 | 31 | from pylint.config import find_default_config_files; \ 32 | 33 | sys.path.append( \ 34 | str(Path(next(find_default_config_files())).parent.joinpath('pylint/plugins')) 35 | ) \ 36 | """ 37 | load-plugins = [ 38 | "pylint.extensions.code_style", 39 | "pylint.extensions.typing", 40 | # "hass_constructor", 41 | # "hass_enforce_type_hints", 42 | # "hass_imports", 43 | # "hass_logger", 44 | ] 45 | persistent = false 46 | extension-pkg-allow-list = [ 47 | "av.audio.stream", 48 | "av.stream", 49 | "ciso8601", 50 | "orjson", 51 | "cv2", 52 | ] 53 | fail-on = [ 54 | "I", 55 | ] 56 | 57 | [tool.pylint.BASIC] 58 | class-const-naming-style = "any" 59 | good-names = [ 60 | "_", 61 | "ev", 62 | "ex", 63 | "fp", 64 | "i", 65 | "id", 66 | "j", 67 | "k", 68 | "Run", 69 | "ip", 70 | ] 71 | good-names-rgxs = [ 72 | "\\S", 73 | "\\S\\d", 74 | ] 75 | 76 | [tool.pylint."MESSAGES CONTROL"] 77 | # Reasons disabled: 78 | # format - handled by black 79 | # locally-disabled - it spams too much 80 | # duplicate-code - unavoidable 81 | # cyclic-import - doesn't test if both import on load 82 | # abstract-class-little-used - prevents from setting right foundation 83 | # unused-argument - generic callbacks and setup methods create a lot of warnings 84 | # too-many-* - are not enforced for the sake of readability 85 | # too-few-* - same as too-many-* 86 | # abstract-method - with intro of async there are always methods missing 87 | # inconsistent-return-statements - doesn't handle raise 88 | # too-many-ancestors - it's too strict. 89 | # wrong-import-order - isort guards this 90 | # consider-using-f-string - str.format sometimes more readable 91 | # --- 92 | # Enable once current issues are fixed: 93 | # consider-using-namedtuple-or-dataclass (Pylint CodeStyle extension) 94 | # consider-using-assignment-expr (Pylint CodeStyle extension) 95 | disable = [ 96 | "format", 97 | "abstract-method", 98 | "cyclic-import", 99 | "duplicate-code", 100 | "inconsistent-return-statements", 101 | "locally-disabled", 102 | "not-context-manager", 103 | "too-few-public-methods", 104 | "too-many-ancestors", 105 | "too-many-arguments", 106 | "too-many-branches", 107 | "too-many-instance-attributes", 108 | "too-many-lines", 109 | "too-many-locals", 110 | "too-many-positional-arguments", 111 | "too-many-public-methods", 112 | "too-many-return-statements", 113 | "too-many-statements", 114 | "too-many-boolean-expressions", 115 | "unused-argument", 116 | "wrong-import-order", 117 | "consider-using-f-string", 118 | "consider-using-namedtuple-or-dataclass", 119 | "consider-using-assignment-expr", 120 | "bare-except", 121 | ] 122 | enable = [ 123 | #"useless-suppression", # temporarily every now and then to clean them up 124 | "use-symbolic-message-instead", 125 | ] 126 | 127 | [tool.pylint.REPORTS] 128 | score = false 129 | 130 | [tool.pylint.TYPECHECK] 131 | ignored-classes = [ 132 | "_CountingAttr", # for attrs 133 | ] 134 | mixin-class-rgx = ".*[Mm]ix[Ii]n" 135 | 136 | [tool.pylint.FORMAT] 137 | expected-line-ending-format = "LF" 138 | 139 | [tool.pylint.EXCEPTIONS] 140 | overgeneral-exceptions = [ 141 | "builtins.BaseException", 142 | "builtins.Exception", 143 | "builtins.HomeAssistantError", 144 | ] 145 | 146 | [tool.pylint.TYPING] 147 | runtime-typing = false 148 | 149 | [tool.pylint.CODE_STYLE] 150 | max-line-length-suggestions = 72 151 | 152 | [tool.pytest.ini_options] 153 | testpaths = [ 154 | "tests", 155 | ] 156 | norecursedirs = [ 157 | ".git", 158 | "testing_config", 159 | ] 160 | log_format = "%(asctime)s.%(msecs)03d %(levelname)-8s %(threadName)s %(name)s:%(filename)s:%(lineno)s %(message)s" 161 | log_date_format = "%Y-%m-%d %H:%M:%S" 162 | asyncio_mode = "auto" 163 | -------------------------------------------------------------------------------- /requirements_test.txt: -------------------------------------------------------------------------------- 1 | async-timeout 2 | black 3 | isort 4 | pytest>=8.0.2 5 | pytest-asyncio>=0.23.5 6 | pytest-cov>=4.1.0 7 | pytest-mock>=3.14.0 8 | pytest-homeassistant-custom-component>=0.13.109 9 | arrow 10 | aiofiles 11 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = .venv,.git,docs,venv,bin,lib,deps,build 3 | max-complexity = 25 4 | doctests = True 5 | # To work with Black 6 | # E501: line too long 7 | # W503: Line break occurred before a binary operator 8 | # E203: Whitespace before ':' 9 | # D202 No blank lines allowed after function docstring 10 | # W504 line break after binary operator 11 | ignore = 12 | E722, 13 | E402, 14 | E501, 15 | W503, 16 | E203, 17 | D202, 18 | W504 19 | noqa-require-code = True -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasqued2/ha-teamtracker/f4b67a40133a3d0812eb2f5504c22cc6c1d75c3e/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | """Fixtures for tests""" 2 | import pytest 3 | 4 | pytest_plugins = ("pytest_homeassistant_custom_component", "pytest_asyncio") 5 | 6 | 7 | @pytest.fixture(autouse=True) 8 | def auto_enable_custom_integrations(enable_custom_integrations): 9 | """ enable custom integrations """ 10 | yield 11 | -------------------------------------------------------------------------------- /tests/const.py: -------------------------------------------------------------------------------- 1 | """Constants for tests.""" 2 | 3 | class PlatformConfigType(dict): 4 | """Wrapper class to be able to add attributes on a dict.""" 5 | 6 | def __init__(self, data=None, entry_id=None, **kwargs): 7 | super().__init__(**kwargs) 8 | self.data = data 9 | self.entry_id = entry_id 10 | 11 | # Create the mock object 12 | PLATFORM_TEST_DATA = [ 13 | [PlatformConfigType( 14 | platform='teamtracker', 15 | league_id="BAD", 16 | team_id="MIA", 17 | name="test_tt_all_test03", 18 | conference_id="9999", 19 | timeout=1200, 20 | entry_id={}, 21 | data={} 22 | ), False], 23 | [PlatformConfigType( 24 | platform='teamtracker', 25 | league_id="XXX", 26 | team_id="MIA", 27 | name="test_tt_all_test04", 28 | conference_id="9999", 29 | timeout=1200, 30 | entry_id={}, 31 | data={} 32 | ), False], 33 | [PlatformConfigType( 34 | platform='teamtracker', 35 | league_id="MLB", 36 | team_id="MIA", 37 | name="test_tt_all_test02", 38 | conference_id="9999", 39 | timeout=1200, 40 | entry_id={}, 41 | data={} 42 | ), True], 43 | [PlatformConfigType( 44 | platform='teamtracker', 45 | league_id="MLB", 46 | team_id="MIA", 47 | name="test_tt_all_test01", 48 | conference_id="9999", 49 | entry_id={}, 50 | data={} 51 | ), True], 52 | ] 53 | 54 | CONFIG_DATA = { 55 | "league_id": "MLB", 56 | "team_id": "MIA", 57 | "name": "test_tt_all_test01", 58 | "timeout": 120, 59 | "conference_id": "9999", 60 | } 61 | 62 | CONFIG_DATA2 = { 63 | "league_id": "NCAAF", 64 | "team_id": "OSU", 65 | "name": "test_tt_all_test02", 66 | "timeout": 120, 67 | "conference_id": "5", 68 | } 69 | 70 | TEST_DATA = [ 71 | { 72 | "sensor_name": "test_tt_all_test01", 73 | "sport": "baseball", 74 | "league": "MLB", 75 | "team_abbr": "MIA", 76 | }, 77 | { 78 | "sensor_name": "test_tt_all_test02", 79 | "sport": "baseball", 80 | "league": "MLB", 81 | "team_abbr": "MIL", 82 | }, 83 | { 84 | "sensor_name": "test_tt_all_test03", 85 | "sport": "baseball", 86 | "league": "MLB", 87 | "team_abbr": "CIN", 88 | }, 89 | { 90 | "sensor_name": "test_tt_all_test04", 91 | "sport": "football", 92 | "league": "NCAAF", 93 | "team_abbr": "BGSU", 94 | }, 95 | { 96 | "sensor_name": "test_tt_all_test05", 97 | "sport": "football", 98 | "league": "NCAAF", 99 | "team_abbr": "ALA", 100 | }, 101 | { 102 | "sensor_name": "test_tt_all_test06", 103 | "sport": "football", 104 | "league": "NFL", 105 | "team_abbr": "BUF", 106 | }, 107 | { 108 | "sensor_name": "test_tt_all_test07", 109 | "sport": "soccer", 110 | "league": "NWSL", 111 | "team_abbr": "ORL", 112 | }, 113 | { 114 | "sensor_name": "test_tt_all_test08", 115 | "sport": "soccer", 116 | "league": "MLS", 117 | "team_abbr": "CLB", 118 | }, 119 | { 120 | "sensor_name": "test_tt_all_test09", 121 | "sport": "soccer", 122 | "league": "WC", 123 | "team_abbr": "ARG", 124 | }, 125 | { 126 | "sensor_name": "test_tt_all_test10", 127 | "sport": "basketball", 128 | "league": "NBA", 129 | "team_abbr": "DET", 130 | }, 131 | { 132 | "sensor_name": "test_tt_all_test11", 133 | "sport": "basketball", 134 | "league": "NBA", 135 | "team_abbr": "UTAH", 136 | }, 137 | { 138 | "sensor_name": "test_tt_all_test12", 139 | "sport": "basketball", 140 | "league": "NBA", 141 | "team_abbr": "CHA", 142 | }, 143 | { 144 | "sensor_name": "test_tt_all_test13", 145 | "sport": "hockey", 146 | "league": "NHL", 147 | "team_abbr": "WPG", 148 | }, 149 | { 150 | "sensor_name": "test_tt_all_test14", 151 | "sport": "hockey", 152 | "league": "NHL", 153 | "team_abbr": "NYI", 154 | }, 155 | { 156 | "sensor_name": "test_tt_all_test15", 157 | "sport": "hockey", 158 | "league": "NHL", 159 | "team_abbr": "CBJ", 160 | }, 161 | { 162 | "sensor_name": "test_tt_all_test16", 163 | "sport": "volleyball", 164 | "league": "NCAAVBW", 165 | "team_abbr": "2492", #PEPP 166 | }, 167 | { 168 | "sensor_name": "test_tt_all_test17", 169 | "sport": "volleyball", 170 | "league": "NCAAVBW", 171 | "team_abbr": "MSST", 172 | }, 173 | { 174 | "sensor_name": "test_tt_all_test18", 175 | "sport": "volleyball", 176 | "league": "NCAAVBW", 177 | "team_abbr": "ARMY", 178 | }, 179 | { 180 | "sensor_name": "test_tt_all_test19", 181 | "sport": "tennis", 182 | "league": "ATP", 183 | "team_abbr": "STRUFF", 184 | }, 185 | { 186 | "sensor_name": "test_tt_all_test20", 187 | "sport": "tennis", 188 | "league": "WTA", 189 | "team_abbr": ".*(?:FUCSOVICS|MAROZSAN).*/.*(?:FUCSOVICS|MAROZSAN).*", 190 | }, 191 | { 192 | "sensor_name": "test_tt_all_test21", 193 | "sport": "tennis", 194 | "league": "WTA", 195 | "team_abbr": "PAOLINI", 196 | }, 197 | { 198 | "sensor_name": "test_tt_all_test22", 199 | "sport": "mma", 200 | "league": "UFC", 201 | "team_abbr": "STRICKLAND", 202 | }, 203 | { 204 | "sensor_name": "test_tt_all_test23", 205 | "sport": "mma", 206 | "league": "UFC", 207 | "team_abbr": "CACERES", 208 | }, 209 | { 210 | "sensor_name": "test_tt_all_test24", 211 | "sport": "mma", 212 | "league": "UFC", 213 | "team_abbr": "FAKHRETDINOV", 214 | }, 215 | { 216 | "sensor_name": "test_tt_all_test25", 217 | "sport": "golf", 218 | "league": "PGA", 219 | "team_abbr": "CONNERS", 220 | }, 221 | { 222 | "sensor_name": "test_tt_all_test26", 223 | "sport": "cricket", 224 | "league": "XXX", 225 | "team_abbr": "BH", 226 | }, 227 | { 228 | "sensor_name": "test_tt_all_test27", 229 | "sport": "cricket", 230 | "league": "XXX", 231 | "team_abbr": "MR", 232 | }, 233 | { 234 | "sensor_name": "test_tt_all_test28", 235 | "sport": "cricket", 236 | "league": "XXX", 237 | "team_abbr": "IND", 238 | }, 239 | { 240 | "sensor_name": "test_tt_all_test29", 241 | "sport": "racing", 242 | "league": "F1", 243 | "team_abbr": "SAINTZ", 244 | }, 245 | { 246 | "sensor_name": "test_tt_all_test30", 247 | "sport": "racing", 248 | "league": "F1", 249 | "team_abbr": "VERSTAPPEN", 250 | }, 251 | { 252 | "sensor_name": "test_tt_all_test31", 253 | "sport": "racing", 254 | "league": "F1", 255 | "team_abbr": "STROLLZ", 256 | }, 257 | ] 258 | 259 | MULTIGAME_DATA = [ 260 | { 261 | "sensor_name": "test_tt_all_test01", 262 | "sport": "football", 263 | "league": "NFL", 264 | "team_abbr": "CLE", #PRE, PRE 265 | "expected_event_name": "BAL @ CLE" 266 | }, 267 | { 268 | "sensor_name": "test_tt_all_test02", 269 | "sport": "football", 270 | "league": "NFL", 271 | "team_abbr": "CIN", # PRE, IN 272 | "expected_event_name": "PIT @ CIN" 273 | }, 274 | { 275 | "sensor_name": "test_tt_all_test03", 276 | "sport": "football", 277 | "league": "NFL", 278 | "team_abbr": "BAL", # PRE, POST 279 | "expected_event_name": "TB @ BAL" 280 | }, 281 | { 282 | "sensor_name": "test_tt_all_test04", 283 | "sport": "football", 284 | "league": "NFL", 285 | "team_abbr": "PIT", #IN, PRE 286 | "expected_event_name": "PIT @ CIN" 287 | }, 288 | { 289 | "sensor_name": "test_tt_all_test05", 290 | "sport": "football", 291 | "league": "NFL", 292 | "team_abbr": "GB", # IN, IN 293 | "expected_event_name": "GB @ ATL" 294 | }, 295 | { 296 | "sensor_name": "test_tt_all_test06", 297 | "sport": "football", 298 | "league": "NFL", 299 | "team_abbr": "TB", # IN, POST 300 | "expected_event_name": "TB @ BAL" 301 | }, 302 | { 303 | "sensor_name": "test_tt_all_test07", 304 | "sport": "football", 305 | "league": "NFL", 306 | "team_abbr": "NE", #POST, PRE 307 | "expected_event_name": "NE @ TB" 308 | }, 309 | { 310 | "sensor_name": "test_tt_all_test08", 311 | "sport": "football", 312 | "league": "NFL", 313 | "team_abbr": "JAX", # POST, IN 314 | "expected_event_name": "JAX @ WSH" 315 | }, 316 | { 317 | "sensor_name": "test_tt_all_test09", 318 | "sport": "football", 319 | "league": "NFL", 320 | "team_abbr": "BUF", # POST, POST 321 | "expected_event_name": "IND @ BUF" 322 | }, 323 | { 324 | "sensor_name": "test_tt_all_test10", 325 | "sport": "football", 326 | "league": "NFL", 327 | "team_abbr": "*", #PRE, PRE 328 | "expected_event_name": "GB @ ATL" 329 | }, 330 | { 331 | "sensor_name": "test_tt_all_test11", 332 | "sport": "football", 333 | "league": "NFL", 334 | "team_abbr": "KNC", # PRE, IN 335 | "expected_event_name": "KNC @ ARI" 336 | }, 337 | { 338 | "sensor_name": "test_tt_all_test12", 339 | "sport": "football", 340 | "league": "NFL", 341 | "team_abbr": "TNT", # PRE, POST 342 | "expected_event_name": "NYG @ TNT" 343 | }, 344 | { 345 | "sensor_name": "test_tt_all_test13", 346 | "sport": "football", 347 | "league": "NFL", 348 | "team_abbr": "NOT_FOUND", # PRE, POST 349 | "expected_event_name": None 350 | }, 351 | ] -------------------------------------------------------------------------------- /tests/test_config_flow.py: -------------------------------------------------------------------------------- 1 | """Test for config flow""" 2 | from unittest.mock import patch 3 | from pytest_homeassistant_custom_component.common import MockConfigEntry 4 | 5 | import pytest 6 | 7 | from custom_components.teamtracker.const import DOMAIN, CONF_API_LANGUAGE 8 | from homeassistant import setup 9 | from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN 10 | from tests.const import CONFIG_DATA 11 | 12 | 13 | @pytest.mark.parametrize( 14 | "input,step_id,title,description,data", 15 | [ 16 | ( 17 | { 18 | "league_id": "NFL", 19 | "team_id": "SEA", 20 | "name": "team_tracker", 21 | "conference_id": "9999", 22 | }, 23 | "user", 24 | "team_tracker", 25 | "description", 26 | { 27 | "league_id": "NFL", 28 | "team_id": "SEA", 29 | "name": "team_tracker", 30 | "conference_id": "9999", 31 | "league_path": "nfl", 32 | "sport_path": "football", 33 | }, 34 | ), 35 | ], 36 | ) 37 | async def test_user_form( 38 | input, # pylint: disable=redefined-builtin 39 | step_id, 40 | title, 41 | description, 42 | data, 43 | hass, 44 | ): 45 | """Test we get the form.""" 46 | await setup.async_setup_component(hass, "persistent_notification", {}) 47 | result = await hass.config_entries.flow.async_init( 48 | DOMAIN, context={"source": step_id} 49 | ) 50 | assert result["type"] == "form" 51 | assert result["errors"] == {} 52 | 53 | with patch( 54 | "custom_components.teamtracker.async_setup_entry", 55 | return_value=True, 56 | ) as mock_setup_entry: 57 | 58 | result2 = await hass.config_entries.flow.async_configure( 59 | result["flow_id"], input 60 | ) 61 | 62 | assert result2["type"] == "create_entry" 63 | assert result2["title"] == title 64 | assert result2["data"] == data 65 | 66 | await hass.async_block_till_done() 67 | assert len(mock_setup_entry.mock_calls) == 1 68 | 69 | 70 | async def test_path_form( 71 | hass, 72 | ): 73 | """Test we get the form.""" 74 | await setup.async_setup_component(hass, "persistent_notification", {}) 75 | result = await hass.config_entries.flow.async_init( 76 | DOMAIN, context={"source": "path"} 77 | ) 78 | assert result["type"] == "form" 79 | assert result["errors"] == {} 80 | 81 | #@patch("custom_components.teamtracker.sensor.async_add_entities") 82 | async def test_options_flow_init( 83 | hass, 84 | ): 85 | """ Test config flow options """ 86 | 87 | entry = MockConfigEntry( 88 | domain=DOMAIN, 89 | title="team_tracker", 90 | data=CONFIG_DATA, 91 | ) 92 | 93 | entry.add_to_hass(hass) 94 | assert await hass.config_entries.async_setup(entry.entry_id) 95 | await hass.async_block_till_done() 96 | 97 | assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1 98 | entries = hass.config_entries.async_entries(DOMAIN) 99 | assert len(entries) == 1 100 | 101 | # Show Options Flow Form 102 | 103 | result = await hass.config_entries.options.async_init(entry.entry_id) 104 | assert "form" == result["type"] 105 | assert "init" == result["step_id"] 106 | assert {} == result["errors"] 107 | 108 | # Submit Form with Options 109 | result = await hass.config_entries.options.async_configure( 110 | result["flow_id"], user_input={"api_language": "en"} 111 | ) 112 | 113 | assert "create_entry" == result["type"] 114 | assert "" == result["title"] 115 | assert result["result"] is True 116 | assert {CONF_API_LANGUAGE: "en"} == result["data"] 117 | 118 | # Unload 119 | 120 | # No need to unload any more 121 | 122 | # assert await entry.async_unload(hass) 123 | await hass.async_block_till_done() 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /tests/test_error_conditions.py: -------------------------------------------------------------------------------- 1 | """Test NFL Sensor""" 2 | import logging 3 | 4 | from custom_components.teamtracker.set_cricket import async_set_cricket_values 5 | from custom_components.teamtracker.set_golf import async_set_golf_values 6 | from custom_components.teamtracker.set_hockey import async_set_hockey_values 7 | from custom_components.teamtracker.set_mma import async_set_mma_values 8 | from custom_components.teamtracker.set_racing import async_set_racing_values 9 | from custom_components.teamtracker.set_soccer import async_set_soccer_values 10 | from custom_components.teamtracker.set_tennis import async_set_tennis_values 11 | from custom_components.teamtracker.set_volleyball import async_set_volleyball_values 12 | from custom_components.teamtracker.set_values import async_set_values, async_set_universal_values, async_set_team_values, async_set_in_values 13 | 14 | _LOGGER = logging.getLogger(__name__) 15 | 16 | 17 | async def test_error_conditions(hass): 18 | """ Use file w/ test json and loop through test cases and compare to expected results """ 19 | 20 | rc = await async_set_cricket_values({}, {}, 0, 0, "en", "sensor_name") 21 | assert rc is False 22 | rc = await async_set_golf_values({}, {}, 0, 0, "en", "sensor_name") 23 | assert rc is False 24 | rc = await async_set_hockey_values({}, {}, 0, 0, "sensor_name") 25 | assert rc is False 26 | rc = await async_set_mma_values({}, {}, 0, 0, "en", "sensor_name") 27 | assert rc is False 28 | rc = await async_set_racing_values({}, {}, 0, 0, "en", "sensor_name") 29 | assert rc is False 30 | rc = await async_set_soccer_values({}, {}, 0, 0, "sensor_name") 31 | assert rc is False 32 | rc = await async_set_tennis_values({}, {}, 0, 0, 0, "en", "sensor_name") 33 | assert rc is False 34 | rc = await async_set_volleyball_values({}, {}, 0, 0, "sensor_name") 35 | assert rc is False 36 | 37 | rc = await async_set_values({}, {}, 0, 0, 0, "en", "sensor_name") 38 | assert rc is False 39 | rc = await async_set_universal_values({}, {}, 0, 0, 0, "en", "sensor_name") 40 | assert rc is False 41 | rc = await async_set_team_values({}, {}, 0, 0, 0, "en", "sensor_name") 42 | assert rc is False 43 | rc = await async_set_in_values({}, {}, 0, 0, 0, "sensor_name") 44 | assert rc is False 45 | -------------------------------------------------------------------------------- /tests/test_event.py: -------------------------------------------------------------------------------- 1 | """Test NFL Sensor""" 2 | import json 3 | import logging 4 | import aiofiles 5 | 6 | from custom_components.teamtracker.clear_values import async_clear_values 7 | from custom_components.teamtracker.const import ( 8 | DEFAULT_KICKOFF_IN, 9 | DEFAULT_LAST_UPDATE, 10 | DEFAULT_LOGO, 11 | ) 12 | from custom_components.teamtracker.event import async_process_event 13 | from tests.const import TEST_DATA 14 | 15 | _LOGGER = logging.getLogger(__name__) 16 | 17 | 18 | async def test_event(hass): 19 | """ Use file w/ test json and loop through test cases and compare to expected results """ 20 | 21 | async with aiofiles.open("tests/tt/all.json", mode="r") as f: 22 | contents = await f.read() 23 | data = json.loads(contents) 24 | if data is None: 25 | _LOGGER.warning("test_event(): Error with test file '%s'", "tests/tt/all.json") 26 | assert False 27 | 28 | for t in TEST_DATA: 29 | values = await async_clear_values() 30 | values["sport"] = t["sport"] 31 | values["league"] = t["league"] 32 | values["league_logo"] = DEFAULT_LOGO 33 | values["team_abbr"] = t["team_abbr"] 34 | values["state"] = "NOT_FOUND" 35 | values["last_update"] = DEFAULT_LAST_UPDATE 36 | values["private_fast_refresh"] = False 37 | 38 | sensor_name = t["sensor_name"] 39 | sport_path = values["sport"] 40 | league_id = values["league"] 41 | team_id = values["team_abbr"] 42 | lang = "en" 43 | 44 | _LOGGER.debug("%s: calling async_process_event()", sensor_name) 45 | values = await async_process_event( 46 | values, 47 | sensor_name, 48 | data, 49 | sport_path, 50 | league_id, 51 | DEFAULT_LOGO, 52 | team_id, 53 | lang, 54 | ) 55 | 56 | assert values 57 | 58 | file = "tests/tt/results/" + sensor_name + ".json" 59 | async with aiofiles.open(file, mode="r") as f: 60 | contents = await f.read() 61 | expected_results = json.loads(contents) 62 | 63 | # Ignore expected values not set in the async_process_event() function 64 | 65 | expected_results["api_url"] = None 66 | expected_results["sport_path"] = None 67 | expected_results["league_path"] = None 68 | 69 | values["kickoff_in"] = DEFAULT_KICKOFF_IN # set to default value for compare 70 | assert values == expected_results 71 | -------------------------------------------------------------------------------- /tests/test_init.py: -------------------------------------------------------------------------------- 1 | """ Tests for TeamTracker """ 2 | 3 | import pytest 4 | from pytest_homeassistant_custom_component.common import MockConfigEntry 5 | from custom_components.teamtracker.const import DOMAIN 6 | from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN 7 | from tests.const import CONFIG_DATA, CONFIG_DATA2 8 | import logging 9 | _LOGGER = logging.getLogger(__name__) 10 | 11 | @pytest.fixture(autouse=False) 12 | def expected_lingering_timers() -> bool: 13 | """ Temporary ability to bypass test failures due to lingering timers. 14 | Parametrize to True to bypass the pytest failure. 15 | @pytest.mark.parametrize("expected_lingering_timers", [True]) 16 | This should be removed when all lingering timers have been cleaned up. 17 | """ 18 | return False 19 | 20 | 21 | #@pytest.mark.parametrize("expected_lingering_timers", [True]) 22 | async def test_setup_entry( 23 | hass, 24 | ): 25 | """ test setup """ 26 | 27 | entry = MockConfigEntry( 28 | domain=DOMAIN, 29 | title="team_tracker", 30 | data=CONFIG_DATA, 31 | ) 32 | 33 | entry.add_to_hass(hass) 34 | assert await hass.config_entries.async_setup(entry.entry_id) 35 | await hass.async_block_till_done() 36 | 37 | assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1 38 | entries = hass.config_entries.async_entries(DOMAIN) 39 | assert len(entries) == 1 40 | 41 | # 42 | # Validate sensor state and attributes based on CONFIG_DATA 43 | # 44 | 45 | sensor_state = hass.states.get("sensor.test_tt_all_test01") 46 | 47 | assert sensor_state.state == "PRE" 48 | team_abbr = sensor_state.attributes.get("team_abbr") 49 | assert team_abbr == "MIA" 50 | sport = sensor_state.attributes.get("sport") 51 | assert sport == "baseball" 52 | 53 | 54 | await hass.services.async_call( 55 | domain="teamtracker", 56 | service="call_api", 57 | service_data={ 58 | "sport_path": "basketball", 59 | "league_path": "nba", 60 | "team_id": "bos" 61 | }, 62 | target={ 63 | "entity_id": [ 64 | "sensor.test_tt_all_test01", 65 | ] 66 | }, 67 | blocking=True 68 | ) 69 | 70 | # 71 | # Validate sensor state and attributes changed based on API call 72 | # 73 | 74 | sensor_state = hass.states.get("sensor.test_tt_all_test01") 75 | 76 | assert sensor_state.state == "NOT_FOUND" 77 | team_abbr = sensor_state.attributes.get("team_abbr") 78 | assert team_abbr == "BOS" 79 | sport = sensor_state.attributes.get("sport") 80 | assert sport == "basketball" 81 | 82 | # assert await entry.async_unload(hass) 83 | # await hass.async_block_till_done() 84 | 85 | 86 | #@pytest.mark.parametrize("expected_lingering_timers", [True]) 87 | async def test_unload_entry(hass): 88 | """ test unload """ 89 | 90 | entry = MockConfigEntry( 91 | domain=DOMAIN, 92 | title="team_tracker", 93 | data=CONFIG_DATA2, 94 | ) 95 | 96 | entry.add_to_hass(hass) 97 | assert await hass.config_entries.async_setup(entry.entry_id) 98 | await hass.async_block_till_done() 99 | 100 | assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1 101 | entries = hass.config_entries.async_entries(DOMAIN) 102 | assert len(entries) == 1 103 | 104 | assert await hass.config_entries.async_unload(entries[0].entry_id) 105 | await hass.async_block_till_done() 106 | assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1 107 | assert len(hass.states.async_entity_ids(DOMAIN)) == 0 108 | 109 | assert await hass.config_entries.async_remove(entries[0].entry_id) 110 | await hass.async_block_till_done() 111 | assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 0 112 | 113 | assert await entry.async_unload(hass) 114 | await hass.async_block_till_done() 115 | -------------------------------------------------------------------------------- /tests/test_multigame.py: -------------------------------------------------------------------------------- 1 | """Test NFL Sensor""" 2 | import json 3 | import logging 4 | import aiofiles 5 | 6 | from custom_components.teamtracker.clear_values import async_clear_values 7 | from custom_components.teamtracker.const import ( 8 | DEFAULT_LAST_UPDATE, 9 | DEFAULT_LOGO, 10 | ) 11 | from custom_components.teamtracker.event import async_process_event 12 | from tests.const import MULTIGAME_DATA 13 | 14 | _LOGGER = logging.getLogger(__name__) 15 | 16 | 17 | async def test_multigame(hass): 18 | """ Use file w/ test json and loop through test cases and compare to expected results """ 19 | 20 | async with aiofiles.open("tests/tt/multigame.json", mode="r") as f: 21 | contents = await f.read() 22 | data = json.loads(contents) 23 | if data is None: 24 | _LOGGER.warning("test_event(): Error with test file '%s'", "tests/tt/multigame.json") 25 | assert False 26 | 27 | for t in MULTIGAME_DATA: 28 | values = await async_clear_values() 29 | values["sport"] = t["sport"] 30 | values["league"] = t["league"] 31 | values["league_logo"] = DEFAULT_LOGO 32 | values["team_abbr"] = t["team_abbr"] 33 | values["state"] = "NOT_FOUND" 34 | values["last_update"] = DEFAULT_LAST_UPDATE 35 | values["private_fast_refresh"] = False 36 | 37 | sensor_name = t["sensor_name"] 38 | sport_path = values["sport"] 39 | league_id = values["league"] 40 | team_id = values["team_abbr"] 41 | lang = "en" 42 | 43 | _LOGGER.debug("%s: calling async_process_event()", sensor_name) 44 | values = await async_process_event( 45 | values, 46 | sensor_name, 47 | data, 48 | sport_path, 49 | league_id, 50 | DEFAULT_LOGO, 51 | team_id, 52 | lang, 53 | ) 54 | 55 | assert values 56 | assert values["event_name"] == t["expected_event_name"] 57 | -------------------------------------------------------------------------------- /tests/test_sensor.py: -------------------------------------------------------------------------------- 1 | """ Test TeamTracker Sensor """ 2 | 3 | import pytest 4 | from pytest_homeassistant_custom_component.common import MockConfigEntry 5 | from typing import Any 6 | from custom_components.teamtracker.const import DOMAIN 7 | from custom_components.teamtracker.sensor import async_setup_platform 8 | from tests.const import CONFIG_DATA, PLATFORM_TEST_DATA 9 | 10 | 11 | @pytest.fixture(autouse=False) 12 | def expected_lingering_timers() -> bool: 13 | """" Temporary ability to bypass test failures due to lingering timers. 14 | Parametrize to True to bypass the pytest failure. 15 | @pytest.mark.parametrize("expected_lingering_timers", [True]) 16 | This should be removed when all lingering timers have been cleaned up. 17 | """ 18 | return False 19 | 20 | 21 | #@pytest.mark.parametrize("expected_lingering_timers", [True]) 22 | async def test_sensor(hass, mocker): 23 | """ test sensor """ 24 | 25 | entry = MockConfigEntry( 26 | domain=DOMAIN, 27 | title="NFL", 28 | data=CONFIG_DATA, 29 | ) 30 | 31 | mocker.patch("locale.getlocale", return_value=("en", 0)) 32 | 33 | entry.add_to_hass(hass) 34 | assert await hass.config_entries.async_setup(entry.entry_id) 35 | await hass.async_block_till_done() 36 | 37 | assert "teamtracker" in hass.config.components 38 | 39 | # assert await entry.async_unload(hass) 40 | # await hass.async_block_till_done() 41 | 42 | 43 | async def test_setup_platform(hass): 44 | """test platform setup""" 45 | 46 | # Mock implementation of async_add_entities callback 47 | entity_list = [] 48 | async def mock_async_add_entities_callback(entities: list[Any], update_before_add: bool = False) -> None: 49 | """Mock implementation of the async_add_entities callback.""" 50 | # Simulate async_add_entities callback behavior 51 | entity_list.extend(entities) 52 | print(f"Adding entities: {entity_list}") 53 | 54 | for test in PLATFORM_TEST_DATA: 55 | await async_setup_platform( 56 | hass, 57 | test[0], 58 | mock_async_add_entities_callback, 59 | discovery_info=None, 60 | ) 61 | 62 | assert (DOMAIN in hass.data) == test[1] 63 | -------------------------------------------------------------------------------- /tests/tt/README.txt: -------------------------------------------------------------------------------- 1 | How to regression test. 2 | 3 | Use test_event.py to validate that sensor is returning the expected results across all supported sports and game situations. 4 | 5 | The all.json file contains the json response for the simulated API call for the TEST_DATA sensors defined in const.py. 6 | The expected result for each sensor defined in TEST_DATA is in the results directory. 7 | The test_event.py file will test each sensor in the TEST_DATA and confirm it matches the expected result in the directory. 8 | The test will fail if there are differences. 9 | If the expected results are supposed to be different, the expected results file should be updated so the test will pass. 10 | The expected results files can be updated manually. 11 | The expected results files can also be regenerated in the /share/tt/results directory 12 | Add the yaml to your config to create the sensor. 13 | Copy the all.json file to /share/tt/test.yaml 14 | Create an empty /share/tt/results directory 15 | Restart HA 16 | The integrationw will create a new version of the expected results file. Compare to prior version to ensure no unexpected changes were introduce. 17 | Update the expected results in the test/results directory. 18 | 19 | Use test_multigame.py to validate the sensor pulls the correct competition to test situations like doubleheaders in baseball. 20 | 21 | The multigame.json file contains the json response for the simulated API call for the MULTIGAME_DATA sensors defined in const.py. 22 | The test_multigame.py file will test each sensor in MULTIGAME_DATA and confirm it matches expected results. 23 | The test will fail if the sensor returns the wrong competition. 24 | The test only validates if the right competition is returned. It does not do the deep compare of each value as the prior test does. 25 | 26 | Legacy tests 27 | 28 | The additional files are legacy manual tests that can be run for even deeper tests of specific sports if desired. 29 | 30 | The .json files contain the json for the simulated API call. 31 | The .yaml files contain the yaml to create the sensors for the corresponding .json file 32 | The -dump.txt files contain the expected output from the sensor dump. 33 | The automations.yaml file contains an automation that can be manually triggered to dump the sensor data. 34 | 35 | Copy the desired .json file to /share/tt/test.json 36 | Insert the corresponding .yaml file into Home Assistant's configuration.yaml file to create the test sensors. 37 | Create an automation to dump the test sensors based on the automations.yaml file. 38 | Restart HA. 39 | Manually trigger the automation to create the sensor dump. 40 | Compare to the expected results dump. 41 | -------------------------------------------------------------------------------- /tests/tt/all.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Test Scenarios 3 | # Use conference_id: 9999 to override API call and use /share/test.json file instead 4 | # 5 | 6 | # Baseball 7 | # PRE - MLB, MIA 8 | - platform: teamtracker 9 | league_id: MLB 10 | team_id: MIA 11 | conference_id: 9999 12 | name: test_tt_all_test01 13 | # IN - MLB, MIL 14 | - platform: teamtracker 15 | league_id: MLB 16 | team_id: MIL 17 | conference_id: 9999 18 | name: test_tt_all_test02 19 | # POST - MLB, CIN 20 | - platform: teamtracker 21 | league_id: MLB 22 | team_id: CIN 23 | conference_id: 9999 24 | name: test_tt_all_test03 25 | 26 | # Football 27 | # PRE - NCAAF, BGSU 28 | - platform: teamtracker 29 | league_id: NCAAF 30 | team_id: BGSU 31 | conference_id: 9999 32 | name: test_tt_all_test04 33 | # IN - NCAAF, ALA 34 | - platform: teamtracker 35 | league_id: NCAAF 36 | team_id: ALA 37 | conference_id: 9999 38 | name: test_tt_all_test05 39 | # POST - NFL, BUF 40 | - platform: teamtracker 41 | league_id: NFL 42 | team_id: BUF 43 | conference_id: 9999 44 | name: test_tt_all_test06 45 | 46 | # Soccer 47 | # PRE - NWSL, ORL 48 | - platform: teamtracker 49 | league_id: NWSL 50 | team_id: ORL 51 | conference_id: 9999 52 | name: test_tt_all_test07 53 | # IN - MLS, CLB 54 | - platform: teamtracker 55 | league_id: MLS 56 | team_id: CLB 57 | conference_id: 9999 58 | name: test_tt_all_test08 59 | # POST - BUND, FCA 60 | - platform: teamtracker 61 | league_id: WC 62 | team_id: ARG 63 | conference_id: 9999 64 | name: test_tt_all_test09 65 | 66 | # Basketball 67 | # PRE - NBA, GS 68 | - platform: teamtracker 69 | league_id: NBA 70 | team_id: DET 71 | conference_id: 9999 72 | name: test_tt_all_test10 73 | # IN - NBA, BOS 74 | - platform: teamtracker 75 | league_id: NBA 76 | team_id: UTAH 77 | conference_id: 9999 78 | name: test_tt_all_test11 79 | # POST - NBA,CHA 80 | - platform: teamtracker 81 | league_id: NBA 82 | team_id: CHA 83 | conference_id: 9999 84 | name: test_tt_all_test12 85 | 86 | # Hockey 87 | # PRE - 88 | - platform: teamtracker 89 | league_id: NHL 90 | team_id: WPG 91 | conference_id: 9999 92 | name: test_tt_all_test13 93 | # IN - 94 | - platform: teamtracker 95 | league_id: NHL 96 | team_id: NYI 97 | conference_id: 9999 98 | name: test_tt_all_test14 99 | # POST - 100 | - platform: teamtracker 101 | league_id: NHL 102 | team_id: CBJ 103 | conference_id: 9999 104 | name: test_tt_all_test15 105 | 106 | # 107 | # Volleyball 108 | # PRE - NCAAVBW, PEPP 109 | - platform: teamtracker 110 | league_id: NCAAVBW 111 | team_id: PEPP 112 | conference_id: 9999 113 | name: test_tt_all_test16 114 | # IN - NCAAVBW, MSST 115 | - platform: teamtracker 116 | league_id: NCAAVBW 117 | team_id: MSST 118 | conference_id: 9999 119 | name: test_tt_all_test17 120 | # POST - NCAAVBW, ARMY 121 | - platform: teamtracker 122 | league_id: NCAAVBW 123 | team_id: ARMY 124 | conference_id: 9999 125 | name: test_tt_all_test18 126 | 127 | # 128 | # Tennis 129 | # PRE - 130 | - platform: teamtracker 131 | league_id: WTA 132 | team_id: Kalinina 133 | conference_id: 9999 134 | name: test_tt_all_test19 135 | # IN - 136 | - platform: teamtracker 137 | league_id: WTA 138 | team_id: Bogdan 139 | conference_id: 9999 140 | name: test_tt_all_test20 141 | # POST - 142 | - platform: teamtracker 143 | league_id: WTA 144 | team_id: Partaud 145 | conference_id: 9999 146 | name: test_tt_all_test21 147 | 148 | # 149 | # MMA 150 | # PRE - 151 | - platform: teamtracker 152 | league_id: UFC 153 | team_id: Strickland 154 | conference_id: 9999 155 | name: test_tt_all_test22 156 | # IN - 157 | - platform: teamtracker 158 | league_id: UFC 159 | team_id: Caceres 160 | conference_id: 9999 161 | name: test_tt_all_test23 162 | 163 | # POST - 164 | - platform: teamtracker 165 | league_id: UFC 166 | team_id: Fakhretdinov 167 | conference_id: 9999 168 | name: test_tt_all_test24 169 | 170 | # 171 | # Golf 172 | # IN - 173 | - platform: teamtracker 174 | league_id: PGA 175 | team_id: Conners 176 | conference_id: 9999 177 | name: test_tt_all_test25 178 | 179 | # Error Conditions 180 | # Invalid League - 181 | - platform: teamtracker 182 | league_id: BADLEAGUE 183 | team_id: BADTEAM 184 | name: test_tt_all_teste01 185 | # Invalid Team - 186 | - platform: teamtracker 187 | league_id: NFL 188 | team_id: BADTEAM 189 | name: test_tt_all_teste02 190 | -------------------------------------------------------------------------------- /tests/tt/automations.yaml: -------------------------------------------------------------------------------- 1 | alias: XXX - TeamTracker Test 2 | description: "" 3 | trigger: 4 | - platform: time 5 | at: "17:00:00" 6 | condition: [] 7 | action: 8 | - service: notify.filenotify_teamtracker 9 | data: 10 | message: >- 11 | {% for tt_sensor in states | selectattr('state', 'in', ['PRE', 'IN', 12 | 'POST', 'NOT_FOUND']) | map(attribute='entity_id') | list %} 13 | {% if 'test_tt_' in tt_sensor %} 14 | {{tt_sensor}} {% for attr in states.sensor.test_tt_test01.attributes %} {% if attr not in ['kickoff_in', 'last_update'] %} 15 | {{tt_sensor}}.{{attr}}: {{state_attr(tt_sensor, attr)}} {% endif %} {%- endfor -%} 16 | {% endif %} 17 | {%- endfor -%} 18 | mode: single -------------------------------------------------------------------------------- /tests/tt/mlb.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Test Scenarios 3 | # Use conference_id: 9999 to override API call and use /share/tt/test.json file instead 4 | # 5 | 6 | # Baseball 7 | - platform: teamtracker 8 | league_id: MLB 9 | team_id: CLE 10 | conference_id: 9999 11 | name: test_tt_mlb_test01 12 | 13 | - platform: teamtracker 14 | league_id: MLB 15 | team_id: CIN 16 | conference_id: 9999 17 | name: test_tt_mlb_test02 18 | 19 | - platform: teamtracker 20 | league_id: MLB 21 | team_id: CHC 22 | conference_id: 9999 23 | name: test_tt_mlb_test03 24 | 25 | - platform: teamtracker 26 | league_id: MLB 27 | team_id: TOR 28 | conference_id: 9999 29 | name: test_tt_mlb_test04 30 | 31 | - platform: teamtracker 32 | league_id: MLB 33 | team_id: MIA 34 | conference_id: 9999 35 | name: test_tt_mlb_test05 36 | 37 | - platform: teamtracker 38 | league_id: MLB 39 | team_id: BOS 40 | conference_id: 9999 41 | name: test_tt_mlb_test06 42 | 43 | - platform: teamtracker 44 | league_id: MLB 45 | team_id: DET 46 | conference_id: 9999 47 | name: test_tt_mlb_test07 48 | 49 | - platform: teamtracker 50 | league_id: MLB 51 | team_id: TEX 52 | conference_id: 9999 53 | name: test_tt_mlb_test08 54 | 55 | - platform: teamtracker 56 | league_id: MLB 57 | team_id: MIN 58 | conference_id: 9999 59 | name: test_tt_mlb_test09 60 | 61 | - platform: teamtracker 62 | league_id: MLB 63 | team_id: NYY 64 | conference_id: 9999 65 | name: test_tt_mlb_test10 66 | 67 | - platform: teamtracker 68 | league_id: MLB 69 | team_id: HOU 70 | conference_id: 9999 71 | name: test_tt_mlb_test11 72 | 73 | - platform: teamtracker 74 | league_id: MLB 75 | team_id: PIT 76 | conference_id: 9999 77 | name: test_tt_mlb_test12 78 | 79 | - platform: teamtracker 80 | league_id: MLB 81 | team_id: STL 82 | conference_id: 9999 83 | name: test_tt_mlb_test13 84 | 85 | - platform: teamtracker 86 | league_id: MLB 87 | team_id: ATL 88 | conference_id: 9999 89 | name: test_tt_mlb_test14 90 | 91 | - platform: teamtracker 92 | league_id: MLB 93 | team_id: SD 94 | conference_id: 9999 95 | name: test_tt_mlb_test15 96 | 97 | - platform: teamtracker 98 | league_id: MLB 99 | team_id: LAD 100 | conference_id: 9999 101 | name: test_tt_mlb_test16 102 | 103 | - platform: teamtracker 104 | league_id: MLB 105 | team_id: LAA 106 | conference_id: 9999 107 | name: test_tt_mlb_test17 108 | 109 | # Error Conditions 110 | # Invalid Team - 111 | - platform: teamtracker 112 | league_id: MLB 113 | team_id: BADTEAM 114 | name: test_tt_mlb_teste01 115 | -------------------------------------------------------------------------------- /tests/tt/mls.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Test Scenarios 3 | # Use conference_id: 9999 to override API call and use /share/tt/test.json file instead 4 | # 5 | 6 | # MLS 7 | - platform: teamtracker 8 | league_id: MLS 9 | team_id: NY 10 | conference_id: 9999 11 | name: test_tt_mls_test01 12 | 13 | - platform: teamtracker 14 | league_id: MLS 15 | team_id: ATL 16 | conference_id: 9999 17 | name: test_tt_mls_test02 18 | 19 | - platform: teamtracker 20 | league_id: MLS 21 | team_id: MTL 22 | conference_id: 9999 23 | name: test_tt_mls_test03 24 | 25 | - platform: teamtracker 26 | league_id: MLS 27 | team_id: ORL 28 | conference_id: 9999 29 | name: test_tt_mls_test04 30 | 31 | - platform: teamtracker 32 | league_id: MLS 33 | team_id: CLT 34 | conference_id: 9999 35 | name: test_tt_mls_test05 36 | 37 | - platform: teamtracker 38 | league_id: MLS 39 | team_id: SKC 40 | conference_id: 9999 41 | name: test_tt_mls_test06 42 | 43 | - platform: teamtracker 44 | league_id: MLS 45 | team_id: NSH 46 | conference_id: 9999 47 | name: test_tt_mls_test07 48 | 49 | - platform: teamtracker 50 | league_id: MLS 51 | team_id: CIN 52 | conference_id: 9999 53 | name: test_tt_mls_test08 54 | 55 | - platform: teamtracker 56 | league_id: MLS 57 | team_id: VAN 58 | conference_id: 9999 59 | name: test_tt_mls_test09 60 | 61 | - platform: teamtracker 62 | league_id: MLS 63 | team_id: LA 64 | conference_id: 9999 65 | name: test_tt_mls_test10 66 | 67 | - platform: teamtracker 68 | league_id: MLS 69 | team_id: DAL 70 | conference_id: 9999 71 | name: test_tt_mls_test11 72 | 73 | # Error Conditions 74 | # Invalid Team - 75 | - platform: teamtracker 76 | league_id: MLS 77 | team_id: BADTEAM 78 | name: test_tt_mls_teste01 79 | -------------------------------------------------------------------------------- /tests/tt/ncaaf.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Test Scenarios 3 | # Use conference_id: 9999 to override API call and use /share/tt/test.json file instead 4 | # 5 | 6 | # NCAAF 7 | - platform: teamtracker 8 | league_id: NCAAF 9 | team_id: VT 10 | conference_id: 9999 11 | name: test_tt_ncaaf_test01 12 | 13 | - platform: teamtracker 14 | league_id: NCAAF 15 | team_id: SC 16 | conference_id: 9999 17 | name: test_tt_ncaaf_test02 18 | 19 | - platform: teamtracker 20 | league_id: NCAAF 21 | team_id: MICH 22 | conference_id: 9999 23 | name: test_tt_ncaaf_test03 24 | 25 | - platform: teamtracker 26 | league_id: NCAAF 27 | team_id: OU 28 | conference_id: 9999 29 | name: test_tt_ncaaf_test04 30 | 31 | - platform: teamtracker 32 | league_id: NCAAF 33 | team_id: UK 34 | conference_id: 9999 35 | name: test_tt_ncaaf_test05 36 | 37 | - platform: teamtracker 38 | league_id: NCAAF 39 | team_id: BAY 40 | conference_id: 9999 41 | name: test_tt_ncaaf_test06 42 | 43 | - platform: teamtracker 44 | league_id: NCAAF 45 | team_id: ACU 46 | conference_id: 9999 47 | name: test_tt_ncaaf_test07 48 | 49 | - platform: teamtracker 50 | league_id: NCAAF 51 | team_id: ARMY 52 | conference_id: 9999 53 | name: test_tt_ncaaf_test08 54 | 55 | - platform: teamtracker 56 | league_id: NCAAF 57 | team_id: NU 58 | conference_id: 9999 59 | name: test_tt_ncaaf_test09 60 | 61 | - platform: teamtracker 62 | league_id: NCAAF 63 | team_id: PUR 64 | conference_id: 9999 65 | name: test_tt_ncaaf_test10 66 | 67 | - platform: teamtracker 68 | league_id: NCAAF 69 | team_id: "M-OH" 70 | conference_id: 9999 71 | name: test_tt_ncaaf_test11 72 | 73 | - platform: teamtracker 74 | league_id: NCAAF 75 | team_id: KENT 76 | conference_id: 9999 77 | name: test_tt_ncaaf_test12 78 | 79 | - platform: teamtracker 80 | league_id: NCAAF 81 | team_id: IU 82 | conference_id: 9999 83 | name: test_tt_ncaaf_test13 84 | 85 | - platform: teamtracker 86 | league_id: NCAAF 87 | team_id: TOW 88 | conference_id: 9999 89 | name: test_tt_ncaaf_test14 90 | 91 | - platform: teamtracker 92 | league_id: NCAAF 93 | team_id: BUFF 94 | conference_id: 9999 95 | name: test_tt_ncaaf_test15 96 | 97 | - platform: teamtracker 98 | league_id: NCAAF 99 | team_id: CMU 100 | conference_id: 9999 101 | name: test_tt_ncaaf_test16 102 | 103 | - platform: teamtracker 104 | league_id: NCAAF 105 | team_id: USA 106 | conference_id: 9999 107 | name: test_tt_ncaaf_test17 108 | 109 | - platform: teamtracker 110 | league_id: NCAAF 111 | team_id: OHIO 112 | conference_id: 9999 113 | name: test_tt_ncaaf_test18 114 | 115 | - platform: teamtracker 116 | league_id: NCAAF 117 | team_id: TEM 118 | conference_id: 9999 119 | name: test_tt_ncaaf_test19 120 | 121 | - platform: teamtracker 122 | league_id: NCAAF 123 | team_id: ODU 124 | conference_id: 9999 125 | name: test_tt_ncaaf_test20 126 | 127 | - platform: teamtracker 128 | league_id: NCAAF 129 | team_id: BALL 130 | conference_id: 9999 131 | name: test_tt_ncaaf_test21 132 | 133 | - platform: teamtracker 134 | league_id: NCAAF 135 | team_id: ND 136 | conference_id: 9999 137 | name: test_tt_ncaaf_test22 138 | 139 | - platform: teamtracker 140 | league_id: NCAAF 141 | team_id: TULN 142 | conference_id: 9999 143 | name: test_tt_ncaaf_test23 144 | 145 | - platform: teamtracker 146 | league_id: NCAAF 147 | team_id: UNLV 148 | conference_id: 9999 149 | name: test_tt_ncaaf_test24 150 | 151 | - platform: teamtracker 152 | league_id: NCAAF 153 | team_id: BYU 154 | conference_id: 9999 155 | name: test_tt_ncaaf_test25 156 | 157 | # Error Conditions 158 | # Invalid Team - 159 | - platform: teamtracker 160 | league_id: NCAAF 161 | team_id: BADTEAM 162 | name: test_tt_ncaaf_teste01 163 | -------------------------------------------------------------------------------- /tests/tt/ncaavbw.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Test Scenarios 3 | # Use conference_id: 9999 to override API call and use /share/tt/test.json file instead 4 | # 5 | 6 | # NCAAVBW 7 | - platform: teamtracker 8 | league_id: NCAAVBW 9 | team_id: AUB 10 | conference_id: 9999 11 | name: test_tt_ncaavbw_test01 12 | 13 | - platform: teamtracker 14 | league_id: NCAAVBW 15 | team_id: TXST 16 | conference_id: 9999 17 | name: test_tt_ncaavbw_test02 18 | 19 | - platform: teamtracker 20 | league_id: NCAAVBW 21 | team_id: OSU 22 | conference_id: 9999 23 | name: test_tt_ncaavbw_test03 24 | 25 | - platform: teamtracker 26 | league_id: NCAAVBW 27 | team_id: ARK 28 | conference_id: 9999 29 | name: test_tt_ncaavbw_test04 30 | 31 | - platform: teamtracker 32 | league_id: NCAAVBW 33 | team_id: MILW 34 | conference_id: 9999 35 | name: test_tt_ncaavbw_test05 36 | 37 | - platform: teamtracker 38 | league_id: NCAAVBW 39 | team_id: MIA 40 | conference_id: 9999 41 | name: test_tt_ncaavbw_test06 42 | 43 | # Error Conditions 44 | # Invalid Team - 45 | - platform: teamtracker 46 | league_id: NCAAVBW 47 | team_id: BADTEAM 48 | name: test_tt_ncaavbw_teste01 49 | -------------------------------------------------------------------------------- /tests/tt/nfl.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Test Scenarios 3 | # Use conference_id: 9999 to override API call and use /share/tt/test.json file instead 4 | # 5 | 6 | # Football 7 | - platform: teamtracker 8 | league_id: NFL 9 | team_id: 'NO' 10 | conference_id: 9999 11 | name: test_tt_nfl_test01 12 | 13 | - platform: teamtracker 14 | league_id: NFL 15 | team_id: SF 16 | conference_id: 9999 17 | name: test_tt_nfl_test02 18 | 19 | - platform: teamtracker 20 | league_id: NFL 21 | team_id: CIN 22 | conference_id: 9999 23 | name: test_tt_nfl_test03 24 | 25 | - platform: teamtracker 26 | league_id: NFL 27 | team_id: DET 28 | conference_id: 9999 29 | name: test_tt_nfl_test04 30 | 31 | - platform: teamtracker 32 | league_id: NFL 33 | team_id: NE 34 | conference_id: 9999 35 | name: test_tt_nfl_test05 36 | 37 | - platform: teamtracker 38 | league_id: NFL 39 | team_id: NYJ 40 | conference_id: 9999 41 | name: test_tt_nfl_test06 42 | 43 | - platform: teamtracker 44 | league_id: NFL 45 | team_id: JAX 46 | conference_id: 9999 47 | name: test_tt_nfl_test07 48 | 49 | - platform: teamtracker 50 | league_id: NFL 51 | team_id: CLE 52 | conference_id: 9999 53 | name: test_tt_nfl_test08 54 | 55 | - platform: teamtracker 56 | league_id: NFL 57 | team_id: HOU 58 | conference_id: 9999 59 | name: test_tt_nfl_test09 60 | 61 | - platform: teamtracker 62 | league_id: NFL 63 | team_id: NYG 64 | conference_id: 9999 65 | name: test_tt_nfl_test10 66 | 67 | - platform: teamtracker 68 | league_id: NFL 69 | team_id: GB 70 | conference_id: 9999 71 | name: test_tt_nfl_test11 72 | 73 | - platform: teamtracker 74 | league_id: NFL 75 | team_id: ARI 76 | conference_id: 9999 77 | name: test_tt_nfl_test12 78 | 79 | - platform: teamtracker 80 | league_id: NFL 81 | team_id: LV 82 | conference_id: 9999 83 | name: test_tt_nfl_test13 84 | 85 | - platform: teamtracker 86 | league_id: NFL 87 | team_id: DAL 88 | conference_id: 9999 89 | name: test_tt_nfl_test14 90 | 91 | - platform: teamtracker 92 | league_id: NFL 93 | team_id: BUF 94 | conference_id: 9999 95 | name: test_tt_nfl_test15 96 | 97 | - platform: teamtracker 98 | league_id: NFL 99 | team_id: SEA 100 | conference_id: 9999 101 | name: test_tt_nfl_test16 102 | 103 | # Error Conditions 104 | # Invalid Team - 105 | - platform: teamtracker 106 | league_id: NFL 107 | team_id: BADTEAM 108 | name: test_tt_nfl_teste01 109 | -------------------------------------------------------------------------------- /tests/tt/nhl.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Test Scenarios 3 | # Use conference_id: 9999 to override API call and use /share/tt/test.json file instead 4 | # 5 | 6 | # Hockey 7 | - platform: teamtracker 8 | league_id: NHL 9 | team_id: BUF 10 | conference_id: 9999 11 | name: test_tt_nhl_test01 12 | 13 | - platform: teamtracker 14 | league_id: NHL 15 | team_id: MIN 16 | conference_id: 9999 17 | name: test_tt_nhl_test02 18 | 19 | - platform: teamtracker 20 | league_id: NHL 21 | team_id: ANA 22 | conference_id: 9999 23 | name: test_tt_nhl_test03 24 | 25 | - platform: teamtracker 26 | league_id: NHL 27 | team_id: EDM 28 | conference_id: 9999 29 | name: test_tt_nhl_test04 30 | 31 | - platform: teamtracker 32 | league_id: NHL 33 | team_id: LA 34 | conference_id: 9999 35 | name: test_tt_nhl_test05 36 | 37 | - platform: teamtracker 38 | league_id: NHL 39 | team_id: VAN 40 | conference_id: 9999 41 | name: test_tt_nhl_test06 42 | 43 | - platform: teamtracker 44 | league_id: NHL 45 | team_id: PIT 46 | conference_id: 9999 47 | name: test_tt_nhl_test07 48 | 49 | - platform: teamtracker 50 | league_id: NHL 51 | team_id: VGK 52 | conference_id: 9999 53 | name: test_tt_nhl_test08 54 | 55 | # Error Conditions 56 | # Invalid Team - 57 | - platform: teamtracker 58 | league_id: NHL 59 | team_id: BADTEAM 60 | name: test_tt_nhl_teste01 61 | -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test01.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "baseball", 3 | "sport_path": "baseball", 4 | "league": "MLB", 5 | "league_path": "mlb", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "MIA", 9 | "opponent_abbr": "PHI", 10 | "event_name": "MIA @ PHI", 11 | "event_url": "http://www.espn.com/mlb/game/_/gameId/401356264", 12 | "date": "2022-09-08T22:45Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Citizens Bank Park", 16 | "location": "Philadelphia, Pennsylvania", 17 | "tv_network": null, 18 | "odds": "PHI -140", 19 | "overunder": 7, 20 | "team_name": "Marlins", 21 | "team_long_name": "Miami Marlins", 22 | "team_id": "28", 23 | "team_record": "55-80", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "away", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/mlb/500/scoreboard/mia.png", 28 | "team_url": "https://www.espn.com/mlb/team/_/name/mia/miami-marlins", 29 | "team_colors": [ 30 | "#0081c7", 31 | "#0077c8" 32 | ], 33 | "team_score": "0", 34 | "team_win_probability": null, 35 | "team_winner": null, 36 | "team_timeouts": null, 37 | "opponent_name": "Phillies", 38 | "opponent_long_name": "Philadelphia Phillies", 39 | "opponent_id": "22", 40 | "opponent_record": "75-61", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "home", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/mlb/500/scoreboard/phi.png", 45 | "opponent_url": "https://www.espn.com/mlb/team/_/name/phi/philadelphia-phillies", 46 | "opponent_colors": [ 47 | "#be0011", 48 | "#284898" 49 | ], 50 | "opponent_score": "0", 51 | "opponent_win_probability": null, 52 | "opponent_winner": null, 53 | "opponent_timeouts": null, 54 | "quarter": 1, 55 | "clock": "9/8 - 6:45 PM EDT", 56 | "possession": null, 57 | "last_play": null, 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/baseball/mlb/scoreboard?lang=en&limit=25&groups=9999", 74 | "private_fast_refresh": false, 75 | "state": "PRE" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test02.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "baseball", 3 | "sport_path": "baseball", 4 | "league": "MLB", 5 | "league_path": "mlb", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "MIL", 9 | "opponent_abbr": "SF", 10 | "event_name": "SF @ MIL", 11 | "event_url": "http://www.espn.com/mlb/game?gameId=401423258", 12 | "date": "2022-09-08T20:10Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "American Family Field", 16 | "location": "Milwaukee, Wisconsin", 17 | "tv_network": null, 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Brewers", 21 | "team_long_name": "Milwaukee Brewers", 22 | "team_id": "8", 23 | "team_record": "71-65", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "home", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/mlb/500/scoreboard/mil.png", 28 | "team_url": "https://www.espn.com/mlb/team/_/name/mil/milwaukee-brewers", 29 | "team_colors": [ 30 | "#050C33", 31 | "#f1f2f3" 32 | ], 33 | "team_score": "2", 34 | "team_win_probability": 0.762, 35 | "team_winner": null, 36 | "team_timeouts": null, 37 | "opponent_name": "Giants", 38 | "opponent_long_name": "San Francisco Giants", 39 | "opponent_id": "26", 40 | "opponent_record": "65-70", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "away", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/mlb/500/scoreboard/sf.png", 45 | "opponent_url": "https://www.espn.com/mlb/team/_/name/sf/san-francisco-giants", 46 | "opponent_colors": [ 47 | "#161415", 48 | "#000000" 49 | ], 50 | "opponent_score": "1", 51 | "opponent_win_probability": 0.238, 52 | "opponent_winner": null, 53 | "opponent_timeouts": null, 54 | "quarter": 6, 55 | "clock": "Top 6th", 56 | "possession": "26", 57 | "last_play": "Yastrzemski struck out looking.", 58 | "down_distance_text": null, 59 | "outs": 2, 60 | "balls": 1, 61 | "strikes": 3, 62 | "on_first": false, 63 | "on_second": false, 64 | "on_third": false, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/baseball/mlb/scoreboard?lang=en&limit=25&groups=9999", 74 | "private_fast_refresh": true, 75 | "state": "IN" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test03.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "baseball", 3 | "sport_path": "baseball", 4 | "league": "MLB", 5 | "league_path": "mlb", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "CIN", 9 | "opponent_abbr": "CHC", 10 | "event_name": "CIN @ CHC", 11 | "event_url": "http://www.espn.com/mlb/game/_/gameId/401356263", 12 | "date": "2022-09-08T18:20Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Wrigley Field", 16 | "location": "Chicago, Illinois", 17 | "tv_network": null, 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Reds", 21 | "team_long_name": "Cincinnati Reds", 22 | "team_id": "17", 23 | "team_record": "55-80", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "away", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/mlb/500/scoreboard/cin.png", 28 | "team_url": "https://www.espn.com/mlb/team/_/name/cin/cincinnati-reds", 29 | "team_colors": [ 30 | "#c41422", 31 | "#ffffff" 32 | ], 33 | "team_score": "4", 34 | "team_win_probability": null, 35 | "team_winner": true, 36 | "team_timeouts": null, 37 | "opponent_name": "Cubs", 38 | "opponent_long_name": "Chicago Cubs", 39 | "opponent_id": "16", 40 | "opponent_record": "57-80", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "home", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/mlb/500/scoreboard/chc.png", 45 | "opponent_url": "https://www.espn.com/mlb/team/_/name/chc/chicago-cubs", 46 | "opponent_colors": [ 47 | "#00417d", 48 | "#00417d" 49 | ], 50 | "opponent_score": "3", 51 | "opponent_win_probability": null, 52 | "opponent_winner": false, 53 | "opponent_timeouts": null, 54 | "quarter": 9, 55 | "clock": "Final", 56 | "possession": null, 57 | "last_play": null, 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/baseball/mlb/scoreboard?lang=en&limit=25&groups=9999", 74 | "private_fast_refresh": false, 75 | "state": "POST" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test04.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "football", 3 | "sport_path": "football", 4 | "league": "NCAAF", 5 | "league_path": "college-football", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "BGSU", 9 | "opponent_abbr": "EKU", 10 | "event_name": "EKU @ BGSU", 11 | "event_url": "https://www.espn.com/college-football/game/_/gameId/401416595", 12 | "date": "2022-09-10T20:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Doyt L. Perry Stadium", 16 | "location": "Bowling Green, OH", 17 | "tv_network": "ESPN3", 18 | "odds": "BGSU -7.5", 19 | "overunder": 57, 20 | "team_name": "Falcons", 21 | "team_long_name": "Bowling Green Falcons", 22 | "team_id": "189", 23 | "team_record": "0-1", 24 | "team_rank": null, 25 | "team_conference_id": "15", 26 | "team_homeaway": "home", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/ncaa/500/189.png", 28 | "team_url": "https://www.espn.com/college-football/team/_/id/189/bowling-green-falcons", 29 | "team_colors": [ 30 | "#2b1000", 31 | "#492000" 32 | ], 33 | "team_score": "0", 34 | "team_win_probability": null, 35 | "team_winner": null, 36 | "team_timeouts": null, 37 | "opponent_name": "Colonels", 38 | "opponent_long_name": "Eastern Kentucky Colonels", 39 | "opponent_id": "2198", 40 | "opponent_record": "0-1", 41 | "opponent_rank": null, 42 | "opponent_conference_id": "176", 43 | "opponent_homeaway": "away", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/ncaa/500/2198.png", 45 | "opponent_url": "https://www.espn.com/college-football/team/_/id/2198/eastern-kentucky-colonels", 46 | "opponent_colors": [ 47 | "#660819", 48 | "#f0f0f0" 49 | ], 50 | "opponent_score": "0", 51 | "opponent_win_probability": null, 52 | "opponent_winner": null, 53 | "opponent_timeouts": null, 54 | "quarter": 0, 55 | "clock": "9/10 - 4:00 PM EDT", 56 | "possession": null, 57 | "last_play": null, 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/football/college-football/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": false, 75 | "state": "PRE" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test05.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "football", 3 | "sport_path": "football", 4 | "league": "NCAAF", 5 | "league_path": "college-football", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "ALA", 9 | "opponent_abbr": "TEX", 10 | "event_name": "ALA @ TEX", 11 | "event_url": "https://www.espn.com/college-football/game?gameId=401403868", 12 | "date": "2022-09-10T16:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "DKR-Texas Memorial Stadium", 16 | "location": "Austin, TX", 17 | "tv_network": "FOX", 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Crimson Tide", 21 | "team_long_name": "Alabama Crimson Tide", 22 | "team_id": "333", 23 | "team_record": "1-0", 24 | "team_rank": 1, 25 | "team_conference_id": "8", 26 | "team_homeaway": "away", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/ncaa/500/333.png", 28 | "team_url": "https://www.espn.com/college-football/team/_/id/333/alabama-crimson-tide", 29 | "team_colors": [ 30 | "#690014", 31 | "#f1f2f3" 32 | ], 33 | "team_score": "17", 34 | "team_win_probability": 0.848, 35 | "team_winner": null, 36 | "team_timeouts": 3, 37 | "opponent_name": "Longhorns", 38 | "opponent_long_name": "Texas Longhorns", 39 | "opponent_id": "251", 40 | "opponent_record": "1-0", 41 | "opponent_rank": null, 42 | "opponent_conference_id": "4", 43 | "opponent_homeaway": "home", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/ncaa/500/251.png", 45 | "opponent_url": "https://www.espn.com/college-football/team/_/id/251/texas-longhorns", 46 | "opponent_colors": [ 47 | "#EE7524", 48 | "#f0f0f0" 49 | ], 50 | "opponent_score": "16", 51 | "opponent_win_probability": 0.152, 52 | "opponent_winner": null, 53 | "opponent_timeouts": 3, 54 | "quarter": 4, 55 | "clock": "4:15 - 4th", 56 | "possession": "333", 57 | "last_play": "Jahmyr Gibbs run for 2 yds to the Texas 24 for a 1ST down", 58 | "down_distance_text": "1st & 10 at TEX 24", 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/football/college-football/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": true, 75 | "state": "IN" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test06.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "football", 3 | "sport_path": "football", 4 | "league": "NFL", 5 | "league_path": "nfl", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "BUF", 9 | "opponent_abbr": "LAR", 10 | "event_name": "BUF @ LAR", 11 | "event_url": "https://www.espn.com/nfl/game/_/gameId/401437654", 12 | "date": "2022-09-09T00:20Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "SoFi Stadium", 16 | "location": "Inglewood, CA", 17 | "tv_network": "NBC", 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Bills", 21 | "team_long_name": "Buffalo Bills", 22 | "team_id": "2", 23 | "team_record": "1-0", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "away", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/nfl/500/scoreboard/buf.png", 28 | "team_url": "https://www.espn.com/nfl/team/_/name/buf/buffalo-bills", 29 | "team_colors": [ 30 | "#04407F", 31 | "#c60c30" 32 | ], 33 | "team_score": "31", 34 | "team_win_probability": null, 35 | "team_winner": true, 36 | "team_timeouts": null, 37 | "opponent_name": "Rams", 38 | "opponent_long_name": "Los Angeles Rams", 39 | "opponent_id": "14", 40 | "opponent_record": "0-1", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "home", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/nfl/500/scoreboard/lar.png", 45 | "opponent_url": "https://www.espn.com/nfl/team/_/name/lar/los-angeles-rams", 46 | "opponent_colors": [ 47 | "#00295B", 48 | "#b3995d" 49 | ], 50 | "opponent_score": "10", 51 | "opponent_win_probability": null, 52 | "opponent_winner": false, 53 | "opponent_timeouts": null, 54 | "quarter": 4, 55 | "clock": "Final", 56 | "possession": null, 57 | "last_play": null, 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": false, 75 | "state": "POST" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test07.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "soccer", 3 | "sport_path": "soccer", 4 | "league": "NWSL", 5 | "league_path": "usa.nwsl", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "ORL", 9 | "opponent_abbr": "POR", 10 | "event_name": "POR @ ORL", 11 | "event_url": "http://www.espn.com/soccer/match/_/gameId/633108", 12 | "date": "2022-09-09T23:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Exploria Stadium", 16 | "location": "Orlando, Florida", 17 | "tv_network": null, 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Orlando", 21 | "team_long_name": "Orlando Pride", 22 | "team_id": "18206", 23 | "team_record": "5-6-6", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "home", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/soccer/500/18206.png", 28 | "team_url": "http://www.espn.com/soccer/club/_/id/18206/orlando-pride", 29 | "team_colors": [ 30 | "#633492", 31 | "#a7c0dd" 32 | ], 33 | "team_score": "0", 34 | "team_win_probability": null, 35 | "team_winner": false, 36 | "team_timeouts": null, 37 | "opponent_name": "Portland", 38 | "opponent_long_name": "Portland Thorns FC", 39 | "opponent_id": "15362", 40 | "opponent_record": "7-7-3", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "away", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/soccer/500/15362.png", 45 | "opponent_url": "http://www.espn.com/soccer/club/_/id/15362/portland-thorns-fc", 46 | "opponent_colors": [ 47 | "#004812", 48 | "#000000" 49 | ], 50 | "opponent_score": "0", 51 | "opponent_win_probability": null, 52 | "opponent_winner": false, 53 | "opponent_timeouts": null, 54 | "quarter": 0, 55 | "clock": "9/9 - 7:00 PM EDT", 56 | "possession": null, 57 | "last_play": null, 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/soccer/usa.nwsl/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": false, 75 | "state": "PRE" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test08.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "soccer", 3 | "sport_path": "soccer", 4 | "league": "MLS", 5 | "league_path": "usa.1", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "CLB", 9 | "opponent_abbr": "MTL", 10 | "event_name": "CLB @ MTL", 11 | "event_url": "http://www.espn.com/soccer/match?gameId=623574", 12 | "date": "2022-09-09T23:30Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Stade Saputo", 16 | "location": "Montreal, Canada", 17 | "tv_network": "ESPN+", 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Columbus", 21 | "team_long_name": "Columbus Crew", 22 | "team_id": "183", 23 | "team_record": "9-13-6", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "away", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/soccer/500/183.png", 28 | "team_url": "http://www.espn.com/soccer/club/_/id/183/columbus-crew", 29 | "team_colors": [ 30 | "#ffff00", 31 | "#c9c7c8" 32 | ], 33 | "team_score": "2", 34 | "team_win_probability": 0.0, 35 | "team_winner": false, 36 | "team_timeouts": null, 37 | "opponent_name": "CF Montr\u00e9al", 38 | "opponent_long_name": "CF Montr\u00e9al", 39 | "opponent_id": "9720", 40 | "opponent_record": "16-4-9", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "home", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/soccer/500/9720.png", 45 | "opponent_url": "http://www.espn.com/soccer/club/_/id/9720/cf-montreal", 46 | "opponent_colors": [ 47 | "#181818", 48 | "#c9c7c8" 49 | ], 50 | "opponent_score": "0", 51 | "opponent_win_probability": 0.0, 52 | "opponent_winner": false, 53 | "opponent_timeouts": null, 54 | "quarter": 2, 55 | "clock": "69'", 56 | "possession": null, 57 | "last_play": "CLB 48.2%, MTL 51.8%; 11' Yellow Card: Rudy Camacho (MTL) 64' Yellow Card: Joel Waterman (MTL) 66' Goal - Header: Jonathan Mensah (CLB)", 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": "5", 66 | "team_total_shots": "11", 67 | "opponent_shots_on_target": "8", 68 | "opponent_total_shots": "15", 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/soccer/usa.1/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": true, 75 | "state": "IN" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test09.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "soccer", 3 | "sport_path": "soccer", 4 | "league": "WC", 5 | "league_path": "fifa.world", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "final", 8 | "team_abbr": "ARG", 9 | "opponent_abbr": "FRA", 10 | "event_name": "FRA @ ARG", 11 | "event_url": "http://www.espn.com/soccer/match/_/gameId/633850", 12 | "date": "2022-12-18T15:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Lusail Iconic Stadium", 16 | "location": "Lusail, Qatar", 17 | "tv_network": "FOX", 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Argentina", 21 | "team_long_name": "Argentina", 22 | "team_id": "202", 23 | "team_record": null, 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "home", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/countries/500/arg.png", 28 | "team_url": "http://www.espn.com/soccer/team/_/id/202/argentina", 29 | "team_colors": [ 30 | "#43A1D5", 31 | "#43A1D5" 32 | ], 33 | "team_score": "3(4)", 34 | "team_win_probability": null, 35 | "team_winner": true, 36 | "team_timeouts": null, 37 | "opponent_name": "France", 38 | "opponent_long_name": "France", 39 | "opponent_id": "478", 40 | "opponent_record": null, 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "away", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/countries/500/fra.png", 45 | "opponent_url": "http://www.espn.com/soccer/team/_/id/478/france", 46 | "opponent_colors": [ 47 | "#112855", 48 | "#112855" 49 | ], 50 | "opponent_score": "3(2)", 51 | "opponent_win_probability": null, 52 | "opponent_winner": false, 53 | "opponent_timeouts": null, 54 | "quarter": 5, 55 | "clock": "FT-Pens", 56 | "possession": null, 57 | "last_play": null, 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/soccer/fifa.world/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": false, 75 | "state": "POST" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test10.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "basketball", 3 | "sport_path": "basketball", 4 | "league": "NBA", 5 | "league_path": "nba", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "DET", 9 | "opponent_abbr": "UTAH", 10 | "event_name": "UTAH @ DET", 11 | "event_url": "https://www.espn.com/nba/game/_/gameId/401468616", 12 | "date": "2022-12-21T00:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Little Caesars Arena", 16 | "location": "Detroit, MI", 17 | "tv_network": null, 18 | "odds": "UTAH -2.5", 19 | "overunder": 229, 20 | "team_name": "Pistons", 21 | "team_long_name": "Detroit Pistons", 22 | "team_id": "8", 23 | "team_record": "8-24", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "home", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/nba/500/scoreboard/det.png", 28 | "team_url": "https://www.espn.com/nba/team/_/name/det/detroit-pistons", 29 | "team_colors": [ 30 | "#FA002C", 31 | "#006bb6" 32 | ], 33 | "team_score": "0", 34 | "team_win_probability": null, 35 | "team_winner": null, 36 | "team_timeouts": null, 37 | "opponent_name": "Jazz", 38 | "opponent_long_name": "Utah Jazz", 39 | "opponent_id": "26", 40 | "opponent_record": "17-16", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "away", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/nba/500/scoreboard/utah.png", 45 | "opponent_url": "https://www.espn.com/nba/team/_/name/utah/utah-jazz", 46 | "opponent_colors": [ 47 | "#000000", 48 | "#fff21f" 49 | ], 50 | "opponent_score": "0", 51 | "opponent_win_probability": null, 52 | "opponent_winner": null, 53 | "opponent_timeouts": null, 54 | "quarter": 0, 55 | "clock": "12/20 - 7:00 PM EST", 56 | "possession": null, 57 | "last_play": null, 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/basketball/nba/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": false, 75 | "state": "PRE" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test11.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "basketball", 3 | "sport_path": "basketball", 4 | "league": "NBA", 5 | "league_path": "nba", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "preseason", 8 | "team_abbr": "UTAH", 9 | "opponent_abbr": "TOR", 10 | "event_name": "UTAH @ TOR", 11 | "event_url": "https://www.espn.com/nba/game?gameId=401474846", 12 | "date": "2022-10-02T22:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Rogers Place", 16 | "location": "Edmonton, AB", 17 | "tv_network": "NBA TV", 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Jazz", 21 | "team_long_name": "Utah Jazz", 22 | "team_id": "26", 23 | "team_record": "0-0", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "away", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/nba/500/scoreboard/utah.png", 28 | "team_url": "https://www.espn.com/nba/team/_/name/utah/utah-jazz", 29 | "team_colors": [ 30 | "#06143F", 31 | "#f9a01b" 32 | ], 33 | "team_score": "41", 34 | "team_win_probability": 0.0, 35 | "team_winner": null, 36 | "team_timeouts": null, 37 | "opponent_name": "Raptors", 38 | "opponent_long_name": "Toronto Raptors", 39 | "opponent_id": "28", 40 | "opponent_record": "0-0", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "home", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/nba/500/scoreboard/tor.png", 45 | "opponent_url": "https://www.espn.com/nba/team/_/name/tor/toronto-raptors", 46 | "opponent_colors": [ 47 | "#CE0F41", 48 | "#061922" 49 | ], 50 | "opponent_score": "47", 51 | "opponent_win_probability": 0.0, 52 | "opponent_winner": null, 53 | "opponent_timeouts": null, 54 | "quarter": 2, 55 | "clock": "1:38 - 2nd", 56 | "possession": null, 57 | "last_play": "Gary Trent Jr. defensive rebound", 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/basketball/nba/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": true, 75 | "state": "IN" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test12.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "basketball", 3 | "sport_path": "basketball", 4 | "league": "NBA", 5 | "league_path": "nba", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "preseason", 8 | "team_abbr": "CHA", 9 | "opponent_abbr": "BOS", 10 | "event_name": "CHA @ BOS", 11 | "event_url": "http://www.espn.com/nba/game/_/gameId/401474845", 12 | "date": "2022-10-02T17:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "TD Garden", 16 | "location": "Boston, MA", 17 | "tv_network": "NBA TV", 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Hornets", 21 | "team_long_name": "Charlotte Hornets", 22 | "team_id": "30", 23 | "team_record": "0-1", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "away", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/nba/500/scoreboard/cha.png", 28 | "team_url": "https://www.espn.com/nba/team/_/name/cha/charlotte-hornets", 29 | "team_colors": [ 30 | "#1D1060", 31 | "#008ca8" 32 | ], 33 | "team_score": "93", 34 | "team_win_probability": null, 35 | "team_winner": false, 36 | "team_timeouts": null, 37 | "opponent_name": "Celtics", 38 | "opponent_long_name": "Boston Celtics", 39 | "opponent_id": "2", 40 | "opponent_record": "1-0", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "home", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/nba/500/scoreboard/bos.png", 45 | "opponent_url": "https://www.espn.com/nba/team/_/name/bos/boston-celtics", 46 | "opponent_colors": [ 47 | "#006532", 48 | "#f1f2f3" 49 | ], 50 | "opponent_score": "134", 51 | "opponent_win_probability": null, 52 | "opponent_winner": true, 53 | "opponent_timeouts": null, 54 | "quarter": 4, 55 | "clock": "Final", 56 | "possession": null, 57 | "last_play": null, 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/basketball/nba/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": false, 75 | "state": "POST" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test13.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "hockey", 3 | "sport_path": "hockey", 4 | "league": "NHL", 5 | "league_path": "nhl", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "WPG", 9 | "opponent_abbr": "OTT", 10 | "event_name": "OTT @ WPG", 11 | "event_url": "https://www.espn.com/nhl/game/_/gameId/401459106", 12 | "date": "2022-12-21T01:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Canada Life Centre", 16 | "location": "Winnipeg, MB, Canada", 17 | "tv_network": "NHLPP|ESPN+", 18 | "odds": "WPG -130", 19 | "overunder": 6.5, 20 | "team_name": "Jets", 21 | "team_long_name": "Winnipeg Jets", 22 | "team_id": "28", 23 | "team_record": "20-10-1", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "home", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/nhl/500/scoreboard/wpg.png", 28 | "team_url": "http://www.espn.com/nhl/team/_/name/wpg/winnipeg-jets", 29 | "team_colors": [ 30 | "#133866", 31 | "#b81f3d" 32 | ], 33 | "team_score": "0", 34 | "team_win_probability": null, 35 | "team_winner": null, 36 | "team_timeouts": null, 37 | "opponent_name": "Senators", 38 | "opponent_long_name": "Ottawa Senators", 39 | "opponent_id": "14", 40 | "opponent_record": "14-15-2", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "away", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/nhl/500/scoreboard/ott.png", 45 | "opponent_url": "http://www.espn.com/nhl/team/_/name/ott/ottawa-senators", 46 | "opponent_colors": [ 47 | "#d01b35", 48 | "#c89a1c" 49 | ], 50 | "opponent_score": "0", 51 | "opponent_win_probability": null, 52 | "opponent_winner": null, 53 | "opponent_timeouts": null, 54 | "quarter": 0, 55 | "clock": "12/20 - 8:00 PM EST", 56 | "possession": null, 57 | "last_play": null, 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/hockey/nhl/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": false, 75 | "state": "PRE" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test14.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "hockey", 3 | "sport_path": "hockey", 4 | "league": "NHL", 5 | "league_path": "nhl", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "preseason", 8 | "team_abbr": "NYI", 9 | "opponent_abbr": "PHI", 10 | "event_name": "PHI @ NYI", 11 | "event_url": "http://www.espn.com/nhl/game/_/gameId/401461502", 12 | "date": "2022-10-02T23:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "UBS Arena", 16 | "location": "Elmont, NY, USA", 17 | "tv_network": null, 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Islanders", 21 | "team_long_name": "New York Islanders", 22 | "team_id": "12", 23 | "team_record": "0-2-0", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "home", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/nhl/500/scoreboard/nyi.png", 28 | "team_url": "http://www.espn.com/nhl/team/_/name/nyi/new-york-islanders", 29 | "team_colors": [ 30 | "#10539c", 31 | "#e17033" 32 | ], 33 | "team_score": "0", 34 | "team_win_probability": 0.0, 35 | "team_winner": null, 36 | "team_timeouts": null, 37 | "opponent_name": "Flyers", 38 | "opponent_long_name": "Philadelphia Flyers", 39 | "opponent_id": "15", 40 | "opponent_record": "1-2-0", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "away", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/nhl/500/scoreboard/phi.png", 45 | "opponent_url": "http://www.espn.com/nhl/team/_/name/phi/philadelphia-flyers", 46 | "opponent_colors": [ 47 | "#eb7131", 48 | "#000000" 49 | ], 50 | "opponent_score": "1", 51 | "opponent_win_probability": 0.0, 52 | "opponent_winner": null, 53 | "opponent_timeouts": null, 54 | "quarter": 2, 55 | "clock": "11:58 - 2th", 56 | "possession": null, 57 | "last_play": null, 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": "7", 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": "13", 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/hockey/nhl/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": true, 75 | "state": "IN" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test15.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "hockey", 3 | "sport_path": "hockey", 4 | "league": "NHL", 5 | "league_path": "nhl", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "preseason", 8 | "team_abbr": "CBJ", 9 | "opponent_abbr": "WSH", 10 | "event_name": "WSH @ CBJ", 11 | "event_url": "https://www.espn.com/nhl/game/_/gameId/401461499", 12 | "date": "2022-10-01T23:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": "CBJ leads series 3-2", 15 | "venue": "Nationwide Arena", 16 | "location": "Columbus, OH, USA", 17 | "tv_network": null, 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Blue Jackets", 21 | "team_long_name": "Columbus Blue Jackets", 22 | "team_id": "29", 23 | "team_record": "3-1-1", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "home", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/nhl/500/scoreboard/cbj.png", 28 | "team_url": "http://www.espn.com/nhl/team/_/name/cbj/columbus-blue-jackets", 29 | "team_colors": [ 30 | "#002d62", 31 | "#cd202c" 32 | ], 33 | "team_score": "2", 34 | "team_win_probability": null, 35 | "team_winner": true, 36 | "team_timeouts": null, 37 | "opponent_name": "Capitals", 38 | "opponent_long_name": "Washington Capitals", 39 | "opponent_id": "23", 40 | "opponent_record": "2-1-1", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "away", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/nhl/500/scoreboard/wsh.png", 45 | "opponent_url": "http://www.espn.com/nhl/team/_/name/wsh/washington-capitals", 46 | "opponent_colors": [ 47 | "#d01b35", 48 | "#002d62" 49 | ], 50 | "opponent_score": "1", 51 | "opponent_win_probability": null, 52 | "opponent_winner": false, 53 | "opponent_timeouts": null, 54 | "quarter": 3, 55 | "clock": "Final", 56 | "possession": null, 57 | "last_play": null, 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/hockey/nhl/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": false, 75 | "state": "POST" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test16.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "volleyball", 3 | "sport_path": "volleyball", 4 | "league": "NCAAVBW", 5 | "league_path": "womens-college-volleyball", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "PEPP", 9 | "opponent_abbr": "CP", 10 | "event_name": "CP @ PEPP", 11 | "event_url": null, 12 | "date": "2022-09-09T00:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Alaska Airlines Arena", 16 | "location": "Seattle, Washington", 17 | "tv_network": null, 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Waves", 21 | "team_long_name": "Pepperdine", 22 | "team_id": "2492", 23 | "team_record": "1-1", 24 | "team_rank": 24, 25 | "team_conference_id": null, 26 | "team_homeaway": "home", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/ncaa/500/2492.png", 28 | "team_url": null, 29 | "team_colors": [ 30 | "#009c98", 31 | "#009c98" 32 | ], 33 | "team_score": "0", 34 | "team_win_probability": null, 35 | "team_winner": null, 36 | "team_timeouts": null, 37 | "opponent_name": "Mustangs", 38 | "opponent_long_name": "Cal Poly", 39 | "opponent_id": "13", 40 | "opponent_record": "0-1", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "away", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/ncaa/500/13.png", 45 | "opponent_url": null, 46 | "opponent_colors": [ 47 | "#083808", 48 | "#083808" 49 | ], 50 | "opponent_score": "0", 51 | "opponent_win_probability": null, 52 | "opponent_winner": null, 53 | "opponent_timeouts": null, 54 | "quarter": 0, 55 | "clock": "9/8 - 8:00 PM EDT", 56 | "possession": null, 57 | "last_play": null, 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/volleyball/womens-college-volleyball/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": false, 75 | "state": "PRE" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test17.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "volleyball", 3 | "sport_path": "volleyball", 4 | "league": "NCAAVBW", 5 | "league_path": "womens-college-volleyball", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "MSST", 9 | "opponent_abbr": "KENN", 10 | "event_name": "KENN @ MSST", 11 | "event_url": null, 12 | "date": "2022-09-08T23:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Newell-Grissom Building", 16 | "location": "Starkville, Mississippi", 17 | "tv_network": "SECN+", 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Bulldogs", 21 | "team_long_name": "Mississippi State", 22 | "team_id": "344", 23 | "team_record": "5-0", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "home", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/ncaa/500/344.png", 28 | "team_url": null, 29 | "team_colors": [ 30 | "#531122", 31 | "#531122" 32 | ], 33 | "team_score": 20, 34 | "team_win_probability": 0.0, 35 | "team_winner": null, 36 | "team_timeouts": null, 37 | "opponent_name": "Owls", 38 | "opponent_long_name": "Kennesaw State", 39 | "opponent_id": "338", 40 | "opponent_record": "0-1", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "away", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/ncaa/500/338.png", 45 | "opponent_url": null, 46 | "opponent_colors": [ 47 | "#A9A9A9", 48 | "#A9A9A9" 49 | ], 50 | "opponent_score": 13, 51 | "opponent_win_probability": 0.0, 52 | "opponent_winner": null, 53 | "opponent_timeouts": null, 54 | "quarter": 1, 55 | "clock": "1st Set", 56 | "possession": null, 57 | "last_play": " Set 1: MSST 20 KENN 13; ", 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": "0", 70 | "opponent_sets_won": "0", 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/volleyball/womens-college-volleyball/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": true, 75 | "state": "IN" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test18.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "volleyball", 3 | "sport_path": "volleyball", 4 | "league": "NCAAVBW", 5 | "league_path": "womens-college-volleyball", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "ARMY", 9 | "opponent_abbr": "SYR", 10 | "event_name": "ARMY @ SYR", 11 | "event_url": null, 12 | "date": "2022-09-08T21:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Women's Building", 16 | "location": "Syracuse, New York", 17 | "tv_network": "ACCNX", 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Black Knights", 21 | "team_long_name": "Army", 22 | "team_id": "349", 23 | "team_record": "0-1", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "away", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/ncaa/500/349.png", 28 | "team_url": null, 29 | "team_colors": [ 30 | "#ce9c00", 31 | "#ce9c00" 32 | ], 33 | "team_score": "3", 34 | "team_win_probability": null, 35 | "team_winner": true, 36 | "team_timeouts": null, 37 | "opponent_name": "Orange", 38 | "opponent_long_name": "Syracuse", 39 | "opponent_id": "183", 40 | "opponent_record": "3-3", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "home", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/ncaa/500/183.png", 45 | "opponent_url": null, 46 | "opponent_colors": [ 47 | "#1A3E86", 48 | "#1A3E86" 49 | ], 50 | "opponent_score": "1", 51 | "opponent_win_probability": null, 52 | "opponent_winner": false, 53 | "opponent_timeouts": null, 54 | "quarter": 4, 55 | "clock": "Final", 56 | "possession": null, 57 | "last_play": null, 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/volleyball/womens-college-volleyball/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": false, 75 | "state": "POST" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test19.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "tennis", 3 | "sport_path": "tennis", 4 | "league": "ATP", 5 | "league_path": "atp", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": null, 8 | "team_abbr": "STRUFF", 9 | "opponent_abbr": null, 10 | "event_name": "Olympics", 11 | "event_url": "https://www.espn.com/tennis/scoreboard/tournament/_/eventId/771-2024/competitionType/2", 12 | "date": "2024-07-28T13:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Paris, France", 16 | "location": "Court 7", 17 | "tv_network": "Peacock", 18 | "odds": null, 19 | "overunder": "Men's Singles", 20 | "team_name": "Jan-Lennard Struff", 21 | "team_long_name": "Jan-Lennard Struff", 22 | "team_id": "2337", 23 | "team_record": null, 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": null, 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/countries/500/ger.png", 28 | "team_url": null, 29 | "team_colors": null, 30 | "team_score": 2, 31 | "team_win_probability": null, 32 | "team_winner": true, 33 | "team_timeouts": null, 34 | "opponent_name": "Francisco Cabral", 35 | "opponent_long_name": "Francisco Cabral", 36 | "opponent_id": "3825", 37 | "opponent_record": null, 38 | "opponent_rank": null, 39 | "opponent_conference_id": null, 40 | "opponent_homeaway": null, 41 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/countries/500/por.png", 42 | "opponent_url": null, 43 | "opponent_colors": null, 44 | "opponent_score": 0, 45 | "opponent_win_probability": null, 46 | "opponent_winner": false, 47 | "opponent_timeouts": null, 48 | "quarter": 2, 49 | "clock": "Final", 50 | "possession": null, 51 | "last_play": " Set 1: J. Struff 6 F. Cabral 2; Set 2: J. Struff 6 F. Cabral 2; ", 52 | "down_distance_text": "Round 1", 53 | "outs": null, 54 | "balls": null, 55 | "strikes": null, 56 | "on_first": null, 57 | "on_second": null, 58 | "on_third": null, 59 | "team_shots_on_target": null, 60 | "team_total_shots": null, 61 | "opponent_shots_on_target": null, 62 | "opponent_total_shots": null, 63 | "team_sets_won": 1, 64 | "opponent_sets_won": 0, 65 | "last_update": "2022-02-02 02:02:02-05:00", 66 | "api_message": null, 67 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/tennis/atp/scoreboard?lang=en&limit=25&groups=9999", 68 | "private_fast_refresh": false, 69 | "state": "POST" 70 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test20.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "tennis", 3 | "sport_path": "tennis", 4 | "league": "WTA", 5 | "league_path": "wta", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": null, 8 | "team_abbr": null, 9 | "opponent_abbr": null, 10 | "event_name": "Olympics", 11 | "event_url": "https://www.espn.com/tennis/scoreboard/tournament/_/eventId/771-2024/competitionType/2", 12 | "date": "2024-07-28T18:50Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Paris, France", 16 | "location": "Court 11", 17 | "tv_network": "Peacock", 18 | "odds": null, 19 | "overunder": "Men's Doubles", 20 | "team_name": "M. Fucsovics / F. Marozsan", 21 | "team_long_name": "Marton Fucsovics / Fabian Marozsan", 22 | "team_id": "1862-9213", 23 | "team_record": null, 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "away", 27 | "team_logo": "https://cdn0.iconfinder.com/data/icons/shift-interfaces/32/Error-512.png", 28 | "team_url": null, 29 | "team_colors": [ 30 | "#D3D3D3", 31 | "#D3D3D3" 32 | ], 33 | "team_score": 0, 34 | "team_win_probability": 0.0, 35 | "team_winner": null, 36 | "team_timeouts": null, 37 | "opponent_name": "T. Griekspoor / W. Koolhof", 38 | "opponent_long_name": "Tallon Griekspoor / Wesley Koolhof", 39 | "opponent_id": "3319-3191", 40 | "opponent_record": null, 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "home", 44 | "opponent_logo": "https://cdn0.iconfinder.com/data/icons/shift-interfaces/32/Error-512.png", 45 | "opponent_url": null, 46 | "opponent_colors": [ 47 | "#A9A9A9", 48 | "#A9A9A9" 49 | ], 50 | "opponent_score": 0, 51 | "opponent_win_probability": 0.0, 52 | "opponent_winner": null, 53 | "opponent_timeouts": null, 54 | "quarter": 2, 55 | "clock": "2nd Set", 56 | "possession": null, 57 | "last_play": " Set 1: M. Fucsovics / F. Marozsan 2 T. Griekspoor / W. Koolhof 6; Set 2: M. Fucsovics / F. Marozsan 0 T. Griekspoor / W. Koolhof 0; ", 58 | "down_distance_text": "Round 1", 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": 0, 70 | "opponent_sets_won": 1, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/tennis/wta/scoreboard?lang=en&limit=25&groups=9999", 74 | "private_fast_refresh": true, 75 | "state": "IN" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test21.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "tennis", 3 | "sport_path": "tennis", 4 | "league": "WTA", 5 | "league_path": "wta", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": null, 8 | "team_abbr": "PAOLINI", 9 | "opponent_abbr": null, 10 | "event_name": "Olympics", 11 | "event_url": "https://www.espn.com/tennis/scoreboard/tournament/_/eventId/771-2024/competitionType/2", 12 | "date": "2024-07-29T10:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Paris, France", 16 | "location": "Court Suzanne-Lenglen", 17 | "tv_network": "Peacock", 18 | "odds": null, 19 | "overunder": "Women's Singles", 20 | "team_name": "Jasmine Paolini", 21 | "team_long_name": "Jasmine Paolini", 22 | "team_id": "2615", 23 | "team_record": null, 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": null, 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/countries/500/ita.png", 28 | "team_url": null, 29 | "team_colors": null, 30 | "team_score": null, 31 | "team_win_probability": null, 32 | "team_winner": null, 33 | "team_timeouts": null, 34 | "opponent_name": "Magda Linette", 35 | "opponent_long_name": "Magda Linette", 36 | "opponent_id": "1649", 37 | "opponent_record": null, 38 | "opponent_rank": null, 39 | "opponent_conference_id": null, 40 | "opponent_homeaway": null, 41 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/countries/500/pol.png", 42 | "opponent_url": null, 43 | "opponent_colors": null, 44 | "opponent_score": null, 45 | "opponent_win_probability": null, 46 | "opponent_winner": null, 47 | "opponent_timeouts": null, 48 | "quarter": 1, 49 | "clock": "Mon, July 29th at 6:00 AM EDT", 50 | "possession": null, 51 | "last_play": "", 52 | "down_distance_text": "Round 2", 53 | "outs": null, 54 | "balls": null, 55 | "strikes": null, 56 | "on_first": null, 57 | "on_second": null, 58 | "on_third": null, 59 | "team_shots_on_target": null, 60 | "team_total_shots": null, 61 | "opponent_shots_on_target": null, 62 | "opponent_total_shots": null, 63 | "team_sets_won": 0, 64 | "opponent_sets_won": 0, 65 | "last_update": "2022-02-02 02:02:02-05:00", 66 | "api_message": null, 67 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/tennis/wta/scoreboard?lang=en&limit=25&groups=9999", 68 | "private_fast_refresh": false, 69 | "state": "PRE" 70 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test22.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "mma", 3 | "sport_path": "mma", 4 | "league": "UFC", 5 | "league_path": "ufc", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "STRICKLAND", 9 | "opponent_abbr": null, 10 | "event_name": "UFC Fight Night: Cannonier vs. Strickland", 11 | "event_url": "https://www.espn.com/mma/fightcenter/_/id/600023847", 12 | "date": "2022-12-18T00:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "UFC APEX", 16 | "location": "Las Vegas, NV, USA", 17 | "tv_network": "ESPN+", 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Sean Strickland", 21 | "team_long_name": "Sean Strickland", 22 | "team_id": "3093653", 23 | "team_record": "25-4-0", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": null, 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/countries/500/usa.png", 28 | "team_url": null, 29 | "team_colors": null, 30 | "team_score": null, 31 | "team_win_probability": null, 32 | "team_winner": false, 33 | "team_timeouts": null, 34 | "opponent_name": "Jared Cannonier", 35 | "opponent_long_name": "Jared Cannonier", 36 | "opponent_id": "3154860", 37 | "opponent_record": "15-6-0", 38 | "opponent_rank": null, 39 | "opponent_conference_id": null, 40 | "opponent_homeaway": null, 41 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/countries/500/usa.png", 42 | "opponent_url": null, 43 | "opponent_colors": null, 44 | "opponent_score": null, 45 | "opponent_win_probability": null, 46 | "opponent_winner": false, 47 | "opponent_timeouts": null, 48 | "quarter": 0, 49 | "clock": "12/17 - 7:00 PM EST", 50 | "possession": null, 51 | "last_play": "1. *S. MOROZOV v. J. Newson (Dec: 3-0); 2. *M. KAPE v. D. Dvorak (Dec: 3-0); 3. *R. FAKHRETDINOV v. B. Battle (Dec: 3-0); 4. *R. GARCIA v. Maheshate (Dec: 3-0); 5. J. Matthews v. M. SEMELSBERGER* (Dec: 0-3); 6. *C. MCKENNA v. C. Vlismas (Dec: 3-0); 7. *M. OLEKSIEJCZUK v. C. Brundage (KO/TKO/Sub: R1@3:16); ", 52 | "down_distance_text": null, 53 | "outs": null, 54 | "balls": null, 55 | "strikes": null, 56 | "on_first": null, 57 | "on_second": null, 58 | "on_third": null, 59 | "team_shots_on_target": null, 60 | "team_total_shots": null, 61 | "opponent_shots_on_target": null, 62 | "opponent_total_shots": null, 63 | "team_sets_won": null, 64 | "opponent_sets_won": null, 65 | "last_update": "2022-02-02 02:02:02-05:00", 66 | "api_message": null, 67 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/mma/ufc/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 68 | "private_fast_refresh": false, 69 | "state": "PRE" 70 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test23.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "mma", 3 | "sport_path": "mma", 4 | "league": "UFC", 5 | "league_path": "ufc", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "CACERES", 9 | "opponent_abbr": null, 10 | "event_name": "UFC Fight Night: Cannonier vs. Strickland", 11 | "event_url": "https://www.espn.com/mma/fightcenter/_/id/600023847", 12 | "date": "2022-12-18T00:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "UFC APEX", 16 | "location": "Las Vegas, NV, USA", 17 | "tv_network": "ESPN+", 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Alex Caceres", 21 | "team_long_name": "Alex Caceres", 22 | "team_id": "2552906", 23 | "team_record": "19-13-0", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": null, 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/countries/500/usa.png", 28 | "team_url": null, 29 | "team_colors": null, 30 | "team_score": null, 31 | "team_win_probability": 0.0, 32 | "team_winner": false, 33 | "team_timeouts": null, 34 | "opponent_name": "Julian Erosa", 35 | "opponent_long_name": "Julian Erosa", 36 | "opponent_id": "3955577", 37 | "opponent_record": "28-10-0", 38 | "opponent_rank": null, 39 | "opponent_conference_id": null, 40 | "opponent_homeaway": null, 41 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/countries/500/usa.png", 42 | "opponent_url": null, 43 | "opponent_colors": null, 44 | "opponent_score": null, 45 | "opponent_win_probability": 0.0, 46 | "opponent_winner": false, 47 | "opponent_timeouts": null, 48 | "quarter": 1, 49 | "clock": "R1, 2:27", 50 | "possession": null, 51 | "last_play": "1. *S. MOROZOV v. J. Newson (Dec: 3-0); 2. *M. KAPE v. D. Dvorak (Dec: 3-0); 3. *R. FAKHRETDINOV v. B. Battle (Dec: 3-0); 4. *R. GARCIA v. Maheshate (Dec: 3-0); 5. J. Matthews v. M. SEMELSBERGER* (Dec: 0-3); 6. *C. MCKENNA v. C. Vlismas (Dec: 3-0); 7. *M. OLEKSIEJCZUK v. C. Brundage (KO/TKO/Sub: R1@3:16); ", 52 | "down_distance_text": null, 53 | "outs": null, 54 | "balls": null, 55 | "strikes": null, 56 | "on_first": null, 57 | "on_second": null, 58 | "on_third": null, 59 | "team_shots_on_target": null, 60 | "team_total_shots": null, 61 | "opponent_shots_on_target": null, 62 | "opponent_total_shots": null, 63 | "team_sets_won": null, 64 | "opponent_sets_won": null, 65 | "last_update": "2022-02-02 02:02:02-05:00", 66 | "api_message": null, 67 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/mma/ufc/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 68 | "private_fast_refresh": true, 69 | "state": "IN" 70 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test24.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "mma", 3 | "sport_path": "mma", 4 | "league": "UFC", 5 | "league_path": "ufc", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "FAKHRETDINOV", 9 | "opponent_abbr": null, 10 | "event_name": "UFC Fight Night: Cannonier vs. Strickland", 11 | "event_url": "https://www.espn.com/mma/fightcenter/_/id/600023847", 12 | "date": "2022-12-17T21:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "UFC APEX", 16 | "location": "Las Vegas, NV, USA", 17 | "tv_network": "ESPN+", 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Rinat Fakhretdinov", 21 | "team_long_name": "Rinat Fakhretdinov", 22 | "team_id": "4712980", 23 | "team_record": "20-1-0", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": null, 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/countries/500/rus.png", 28 | "team_url": null, 29 | "team_colors": null, 30 | "team_score": 3, 31 | "team_win_probability": null, 32 | "team_winner": true, 33 | "team_timeouts": null, 34 | "opponent_name": "Bryan Battle", 35 | "opponent_long_name": "Bryan Battle", 36 | "opponent_id": "4815998", 37 | "opponent_record": "9-2-0", 38 | "opponent_rank": null, 39 | "opponent_conference_id": null, 40 | "opponent_homeaway": null, 41 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/countries/500/usa.png", 42 | "opponent_url": null, 43 | "opponent_colors": null, 44 | "opponent_score": 0, 45 | "opponent_win_probability": null, 46 | "opponent_winner": false, 47 | "opponent_timeouts": null, 48 | "quarter": 3, 49 | "clock": "Final", 50 | "possession": null, 51 | "last_play": "1. *S. MOROZOV v. J. Newson (Dec: 3-0); 2. *M. KAPE v. D. Dvorak (Dec: 3-0); 3. *R. FAKHRETDINOV v. B. Battle (Dec: 3-0); 4. *R. GARCIA v. Maheshate (Dec: 3-0); 5. J. Matthews v. M. SEMELSBERGER* (Dec: 0-3); 6. *C. MCKENNA v. C. Vlismas (Dec: 3-0); 7. *M. OLEKSIEJCZUK v. C. Brundage (KO/TKO/Sub: R1@3:16); ", 52 | "down_distance_text": null, 53 | "outs": null, 54 | "balls": null, 55 | "strikes": null, 56 | "on_first": null, 57 | "on_second": null, 58 | "on_third": null, 59 | "team_shots_on_target": null, 60 | "team_total_shots": null, 61 | "opponent_shots_on_target": null, 62 | "opponent_total_shots": null, 63 | "team_sets_won": null, 64 | "opponent_sets_won": null, 65 | "last_update": "2022-02-02 02:02:02-05:00", 66 | "api_message": null, 67 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/mma/ufc/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 68 | "private_fast_refresh": false, 69 | "state": "POST" 70 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test25.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "golf", 3 | "sport_path": "golf", 4 | "league": "PGA", 5 | "league_path": "pga", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": null, 8 | "team_abbr": "CONNERS", 9 | "opponent_abbr": null, 10 | "event_name": "ZOZO CHAMPIONSHIP", 11 | "event_url": "https://www.espn.com/golf/leaderboard?tournamentId=401465500", 12 | "date": "2022-10-13T04:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": null, 16 | "location": null, 17 | "tv_network": null, 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Corey Conners", 21 | "team_long_name": "Corey Conners", 22 | "team_id": "9126", 23 | "team_record": null, 24 | "team_rank": "T31", 25 | "team_conference_id": null, 26 | "team_homeaway": null, 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/countries/500/can.png", 28 | "team_url": null, 29 | "team_colors": null, 30 | "team_score": "-4", 31 | "team_win_probability": 0.0, 32 | "team_winner": null, 33 | "team_timeouts": null, 34 | "opponent_name": "Rickie Fowler", 35 | "opponent_long_name": "Rickie Fowler", 36 | "opponent_id": "3702", 37 | "opponent_record": null, 38 | "opponent_rank": "1", 39 | "opponent_conference_id": null, 40 | "opponent_homeaway": null, 41 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/countries/500/usa.png", 42 | "opponent_url": null, 43 | "opponent_colors": null, 44 | "opponent_score": "-14", 45 | "opponent_win_probability": 0.0, 46 | "opponent_winner": null, 47 | "opponent_timeouts": null, 48 | "quarter": 4, 49 | "clock": "Round 4 - In Progress", 50 | "possession": null, 51 | "last_play": "1. R. Fowler (-14), 2. K. Bradley (-13), 3. A. Putnam (-12), 4. V. Hovland (-11), T5. M. McNealy (-10), T5. H. Buckley (-10), T5. C. Chump (-10), T8. T. Moore (-9), T8. S. Theegala (-9), T8. R. Hisatsune (-9), ", 52 | "down_distance_text": null, 53 | "outs": null, 54 | "balls": null, 55 | "strikes": null, 56 | "on_first": null, 57 | "on_second": null, 58 | "on_third": null, 59 | "team_shots_on_target": 2, 60 | "team_total_shots": 7, 61 | "opponent_shots_on_target": 0, 62 | "opponent_total_shots": 0, 63 | "team_sets_won": null, 64 | "opponent_sets_won": null, 65 | "last_update": "2022-02-02 02:02:02-05:00", 66 | "api_message": null, 67 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/golf/pga/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 68 | "private_fast_refresh": true, 69 | "state": "IN" 70 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test26.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "cricket", 3 | "sport_path": "cricket", 4 | "league": "XXX", 5 | "league_path": "1324623", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": null, 8 | "team_abbr": "BH", 9 | "opponent_abbr": "SS", 10 | "event_name": "BH v SS", 11 | "event_url": "https://www.espn.in/cricket/series/8044/game/1324648/brisbane-heat-vs-sydney-sixers-25th-match-big-bash-league-2022-23", 12 | "date": "2023-01-01T08:15Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Brisbane Cricket Ground, Woolloongabba, Brisbane", 16 | "location": "Brisbane, Australia", 17 | "tv_network": null, 18 | "odds": "Twenty20", 19 | "overunder": null, 20 | "team_name": "BH", 21 | "team_long_name": "Brisbane Heat", 22 | "team_id": "509668", 23 | "team_record": "", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "home", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/cricket/500/509668.png", 28 | "team_url": "https://www.espn.in/cricket/team/_/id/509668/brisbane-heat", 29 | "team_colors": [ 30 | "#", 31 | "#" 32 | ], 33 | "team_score": "", 34 | "team_win_probability": null, 35 | "team_winner": false, 36 | "team_timeouts": null, 37 | "opponent_name": "SS", 38 | "opponent_long_name": "Sydney Sixers", 39 | "opponent_id": "509673", 40 | "opponent_record": "", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "away", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/cricket/500/509673.png", 45 | "opponent_url": "https://www.espn.in/cricket/team/_/id/509673/sydney-sixers", 46 | "opponent_colors": [ 47 | "#", 48 | "#" 49 | ], 50 | "opponent_score": "", 51 | "opponent_win_probability": null, 52 | "opponent_winner": false, 53 | "opponent_timeouts": null, 54 | "quarter": null, 55 | "clock": "Scheduled", 56 | "possession": null, 57 | "last_play": "Starts at 18:15 local time", 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/cricket/1324623/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": false, 75 | "state": "PRE" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test27.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "cricket", 3 | "sport_path": "cricket", 4 | "league": "XXX", 5 | "league_path": "1324623", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": null, 8 | "team_abbr": "MR", 9 | "opponent_abbr": "PS", 10 | "event_name": "MR v PS", 11 | "event_url": "https://www.espn.in/cricket/series/8044/game/1324647/melbourne-renegades-vs-perth-scorchers-24th-match-big-bash-league-2022-23", 12 | "date": "2023-01-01T02:40Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Docklands Stadium, Melbourne", 16 | "location": "Melbourne, Australia", 17 | "tv_network": null, 18 | "odds": "Twenty20", 19 | "overunder": null, 20 | "team_name": "MR", 21 | "team_long_name": "Melbourne Renegades", 22 | "team_id": "509671", 23 | "team_record": "", 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "home", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/cricket/500/509671.png", 28 | "team_url": "https://www.espn.in/cricket/team/_/id/509671/melbourne-renegades", 29 | "team_colors": [ 30 | "#", 31 | "#" 32 | ], 33 | "team_score": "155/6", 34 | "team_win_probability": 0.0, 35 | "team_winner": false, 36 | "team_timeouts": null, 37 | "opponent_name": "PS", 38 | "opponent_long_name": "Perth Scorchers", 39 | "opponent_id": "509670", 40 | "opponent_record": "", 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "away", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/cricket/500/509670.png", 45 | "opponent_url": "https://www.espn.in/cricket/team/_/id/509670/perth-scorchers", 46 | "opponent_colors": [ 47 | "#", 48 | "#" 49 | ], 50 | "opponent_score": "125/3 (16/20 ov, target 156)", 51 | "opponent_win_probability": 0.0, 52 | "opponent_winner": false, 53 | "opponent_timeouts": null, 54 | "quarter": "", 55 | "clock": "Live", 56 | "possession": "509670", 57 | "last_play": "Scorchers require 31 runs", 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/cricket/1324623/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": true, 75 | "state": "IN" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test28.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "cricket", 3 | "sport_path": "cricket", 4 | "league": "XXX", 5 | "league_path": "21108", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": null, 8 | "team_abbr": "IND", 9 | "opponent_abbr": "BAN", 10 | "event_name": "BAN v IND", 11 | "event_url": "https://www.espn.in/cricket/series/21108/scorecard/1340849/bangladesh-vs-india-2nd-test-india-in-bangladesh-2022-23", 12 | "date": "2022-12-22T03:30Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Shere Bangla National Stadium, Mirpur, Dhaka", 16 | "location": "Dhaka, Bangladesh", 17 | "tv_network": null, 18 | "odds": "Test", 19 | "overunder": null, 20 | "team_name": "IND", 21 | "team_long_name": "India", 22 | "team_id": "6", 23 | "team_record": null, 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": "away", 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/cricket/500/6.png", 28 | "team_url": "https://www.espn.in/cricket/team/_/id/6/india", 29 | "team_colors": [ 30 | "##137dd2", 31 | "##137dd2" 32 | ], 33 | "team_score": "314 & 145/7 (47 ov, target 145)", 34 | "team_win_probability": null, 35 | "team_winner": true, 36 | "team_timeouts": null, 37 | "opponent_name": "BAN", 38 | "opponent_long_name": "Bangladesh", 39 | "opponent_id": "25", 40 | "opponent_record": null, 41 | "opponent_rank": null, 42 | "opponent_conference_id": null, 43 | "opponent_homeaway": "home", 44 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/cricket/500/25.png", 45 | "opponent_url": "https://www.espn.in/cricket/team/_/id/25/bangladesh", 46 | "opponent_colors": [ 47 | "##006a4e", 48 | "##006a4e" 49 | ], 50 | "opponent_score": "227 & 231", 51 | "opponent_win_probability": null, 52 | "opponent_winner": false, 53 | "opponent_timeouts": null, 54 | "quarter": null, 55 | "clock": "Result", 56 | "possession": "6", 57 | "last_play": "India won by 3 wkts", 58 | "down_distance_text": null, 59 | "outs": null, 60 | "balls": null, 61 | "strikes": null, 62 | "on_first": null, 63 | "on_second": null, 64 | "on_third": null, 65 | "team_shots_on_target": null, 66 | "team_total_shots": null, 67 | "opponent_shots_on_target": null, 68 | "opponent_total_shots": null, 69 | "team_sets_won": null, 70 | "opponent_sets_won": null, 71 | "last_update": "2022-02-02 02:02:02-05:00", 72 | "api_message": null, 73 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/cricket/21108/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 74 | "private_fast_refresh": false, 75 | "state": "POST" 76 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test29.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "racing", 3 | "sport_path": "racing", 4 | "league": "F1", 5 | "league_path": "f1", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "SAINTZ", 9 | "opponent_abbr": null, 10 | "event_name": "Azerbaijan GP", 11 | "event_url": "https://www.espn.com/f1/race/_/id/600026751", 12 | "date": "2023-04-30T11:00Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Baku City Circuit", 16 | "location": "Baku, Azerbaijan", 17 | "tv_network": "ESPN", 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Carlos Saintz", 21 | "team_long_name": "Carlos Saintz", 22 | "team_id": "4686", 23 | "team_record": null, 24 | "team_rank": 4, 25 | "team_conference_id": null, 26 | "team_homeaway": null, 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/countries/500/esp.png", 28 | "team_url": null, 29 | "team_colors": null, 30 | "team_score": 4, 31 | "team_win_probability": null, 32 | "team_winner": false, 33 | "team_timeouts": null, 34 | "opponent_name": "Cherles Leclerc", 35 | "opponent_long_name": "Cherles Leclerc", 36 | "opponent_id": "5498", 37 | "opponent_record": null, 38 | "opponent_rank": 1, 39 | "opponent_conference_id": null, 40 | "opponent_homeaway": null, 41 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/countries/500/mon.png", 42 | "opponent_url": null, 43 | "opponent_colors": null, 44 | "opponent_score": 1, 45 | "opponent_win_probability": null, 46 | "opponent_winner": false, 47 | "opponent_timeouts": null, 48 | "quarter": "Race", 49 | "clock": "4/30 - 7:00 AM EDT", 50 | "possession": null, 51 | "last_play": "0. C. Leclerc, 1. M. Verstappen, 2. S. P\u00e9rez, 3. C. Saintz, 4. L. Hamilton, 5. F. Alonso, 6. L. Norris, 7. Y. Tsunoda, 8. L. Stroll, 9. O. Piastri, ", 52 | "down_distance_text": null, 53 | "outs": null, 54 | "balls": null, 55 | "strikes": null, 56 | "on_first": null, 57 | "on_second": null, 58 | "on_third": null, 59 | "team_shots_on_target": null, 60 | "team_total_shots": 0, 61 | "opponent_shots_on_target": null, 62 | "opponent_total_shots": null, 63 | "team_sets_won": null, 64 | "opponent_sets_won": null, 65 | "last_update": "2022-02-02 02:02:02-05:00", 66 | "api_message": null, 67 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/racing/f1/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 68 | "private_fast_refresh": false, 69 | "state": "PRE" 70 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test30.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "racing", 3 | "sport_path": "racing", 4 | "league": "F1", 5 | "league_path": "f1", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "VERSTAPPEN", 9 | "opponent_abbr": null, 10 | "event_name": "Azerbaijan GP", 11 | "event_url": "https://www.espn.com/f1/race/_/id/600026751", 12 | "date": "2023-04-29T13:30Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Baku City Circuit", 16 | "location": "Baku, Azerbaijan", 17 | "tv_network": "ESPN", 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Max Verstappen", 21 | "team_long_name": "Max Verstappen", 22 | "team_id": "4665", 23 | "team_record": null, 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": null, 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/countries/500/ned.png", 28 | "team_url": null, 29 | "team_colors": null, 30 | "team_score": 4, 31 | "team_win_probability": 0.0, 32 | "team_winner": false, 33 | "team_timeouts": null, 34 | "opponent_name": "Cherles Leclerc", 35 | "opponent_long_name": "Cherles Leclerc", 36 | "opponent_id": "5498", 37 | "opponent_record": null, 38 | "opponent_rank": null, 39 | "opponent_conference_id": null, 40 | "opponent_homeaway": null, 41 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/countries/500/mon.png", 42 | "opponent_url": null, 43 | "opponent_colors": null, 44 | "opponent_score": 1, 45 | "opponent_win_probability": 0.0, 46 | "opponent_winner": false, 47 | "opponent_timeouts": null, 48 | "quarter": "SQ", 49 | "clock": "In Progress", 50 | "possession": null, 51 | "last_play": "1. C. Leclerc, 2. S. P\u00e9rez, 3. G. Russell, 4. M. Verstappen, 5. C. Sainz, 6. L. Hamilton, 7. F. Alonso, 8. A. Albon, 9. L. Norris, 10. L. Stroll, ", 52 | "down_distance_text": null, 53 | "outs": null, 54 | "balls": null, 55 | "strikes": null, 56 | "on_first": null, 57 | "on_second": null, 58 | "on_third": null, 59 | "team_shots_on_target": null, 60 | "team_total_shots": 3, 61 | "opponent_shots_on_target": null, 62 | "opponent_total_shots": null, 63 | "team_sets_won": null, 64 | "opponent_sets_won": null, 65 | "last_update": "2022-02-02 02:02:02-05:00", 66 | "api_message": null, 67 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/racing/f1/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 68 | "private_fast_refresh": true, 69 | "state": "IN" 70 | } -------------------------------------------------------------------------------- /tests/tt/results/test_tt_all_test31.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "racing", 3 | "sport_path": "racing", 4 | "league": "F1", 5 | "league_path": "f1", 6 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 7 | "season": "regular-season", 8 | "team_abbr": "STROLLZ", 9 | "opponent_abbr": null, 10 | "event_name": "Azerbaijan GP", 11 | "event_url": "https://www.espn.com/f1/race/_/id/600026751", 12 | "date": "2023-04-28T09:30Z", 13 | "kickoff_in": "{test} days", 14 | "series_summary": null, 15 | "venue": "Baku City Circuit", 16 | "location": "Baku, Azerbaijan", 17 | "tv_network": "ESPN2", 18 | "odds": null, 19 | "overunder": null, 20 | "team_name": "Lance Strollz", 21 | "team_long_name": "Lance Strollz", 22 | "team_id": "4775", 23 | "team_record": null, 24 | "team_rank": null, 25 | "team_conference_id": null, 26 | "team_homeaway": null, 27 | "team_logo": "https://a.espncdn.com/i/teamlogos/countries/500/can.png", 28 | "team_url": null, 29 | "team_colors": null, 30 | "team_score": 7, 31 | "team_win_probability": null, 32 | "team_winner": false, 33 | "team_timeouts": null, 34 | "opponent_name": "Max Verstappen", 35 | "opponent_long_name": "Max Verstappen", 36 | "opponent_id": "4665", 37 | "opponent_record": null, 38 | "opponent_rank": null, 39 | "opponent_conference_id": null, 40 | "opponent_homeaway": null, 41 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/countries/500/ned.png", 42 | "opponent_url": null, 43 | "opponent_colors": null, 44 | "opponent_score": 1, 45 | "opponent_win_probability": null, 46 | "opponent_winner": false, 47 | "opponent_timeouts": null, 48 | "quarter": "FP1", 49 | "clock": "Final", 50 | "possession": null, 51 | "last_play": "1. M. Verstappen, 2. C. Leclerc, 3. S. P\u00e9rez, 4. C. Sainz, 5. L. Norris, 6. N. de Vries, 7. L. Strollz, 8. F. Alonso, 9. A. Albon, 10. Z. Guanyu, ", 52 | "down_distance_text": null, 53 | "outs": null, 54 | "balls": null, 55 | "strikes": null, 56 | "on_first": null, 57 | "on_second": null, 58 | "on_third": null, 59 | "team_shots_on_target": null, 60 | "team_total_shots": 22, 61 | "opponent_shots_on_target": null, 62 | "opponent_total_shots": null, 63 | "team_sets_won": null, 64 | "opponent_sets_won": null, 65 | "last_update": "2022-02-02 02:02:02-05:00", 66 | "api_message": null, 67 | "api_url": "http://site.api.espn.com/apis/site/v2/sports/racing/f1/scoreboard?lang=en&limit=25&dates=20250417-20250423&groups=9999", 68 | "private_fast_refresh": false, 69 | "state": "POST" 70 | } -------------------------------------------------------------------------------- /tests/tt/results/test_ttm_teste00.json: -------------------------------------------------------------------------------- 1 | { 2 | "sport": "football", 3 | "league": "NCAAF", 4 | "league_logo": "https://a.espncdn.com/i/teamlogos/leagues/500/mlb.png", 5 | "team_abbr": "MICH", 6 | "opponent_abbr": "OSU", 7 | "event_name": "OSU @ MICH", 8 | "event_url": "https://www.espn.com/college-football/game?gameId=401404125", 9 | "date": "2022-09-10T18:30Z", 10 | "kickoff_in": "{test} days", 11 | "series_summary": null, 12 | "venue": "Notre Dame Stadium", 13 | "location": "Notre Dame, IN", 14 | "tv_network": "NBC", 15 | "odds": null, 16 | "overunder": null, 17 | "team_name": "Wolverines", 18 | "team_long_name": "Michigan Wolverines", 19 | "team_id": "87", 20 | "team_record": "0-1", 21 | "team_rank": 8, 22 | "team_homeaway": "home", 23 | "team_logo": "https://a.espncdn.com/i/teamlogos/ncaa/500/130.png", 24 | "team_url": "https://www.espn.com/college-football/team/_/id/87/notre-dame-fighting-irish", 25 | "team_colors": [ 26 | "#00122b", 27 | "#ae9142" 28 | ], 29 | "team_score": "7", 30 | "team_win_probability": 0.3, 31 | "team_winner": null, 32 | "team_timeouts": 3, 33 | "opponent_name": "Buckeyes", 34 | "opponent_long_name": "Ohio State Buckeyes", 35 | "opponent_id": "276", 36 | "opponent_record": "1-0", 37 | "opponent_rank": null, 38 | "opponent_homeaway": "away", 39 | "opponent_logo": "https://a.espncdn.com/i/teamlogos/ncaa/500/194.png", 40 | "opponent_url": "https://www.espn.com/college-football/team/_/id/276/marshall-thundering-herd", 41 | "opponent_colors": [ 42 | "#de3121", 43 | "#666666" 44 | ], 45 | "opponent_score": "9", 46 | "opponent_win_probability": 0.7, 47 | "opponent_winner": null, 48 | "opponent_timeouts": 3, 49 | "quarter": 2, 50 | "clock": "Halftime", 51 | "possession": null, 52 | "last_play": "End of 2nd Quarter, and Michigan still sucks", 53 | "down_distance_text": null, 54 | "outs": null, 55 | "balls": null, 56 | "strikes": null, 57 | "on_first": null, 58 | "on_second": null, 59 | "on_third": null, 60 | "team_shots_on_target": null, 61 | "team_total_shots": null, 62 | "opponent_shots_on_target": null, 63 | "opponent_total_shots": null, 64 | "team_sets_won": null, 65 | "opponent_sets_won": null, 66 | "last_update": "2022-02-02 02:02:02-05:00", 67 | "api_message": null, 68 | "private_fast_refresh": true, 69 | "state": "IN" 70 | } -------------------------------------------------------------------------------- /tests/tt/test-case-list.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Test Cases 3 | # all.json - a mix of all sports in a single file. See below for details 4 | # nfl.json - base NFL games 5 | # mlb.json - base MLB games 6 | # mls.json - base MLS games 7 | # ncaaf.json - base NCAAF games 8 | # ncaavbw - base volleyball games 9 | # 10 | # Expected output 11 | # all-dump.txt 12 | # nfl-dump.txt 13 | # mlb-dump.txt 14 | # mls-dump.txt 15 | # ncaaf-dump.txt 16 | # ncaavb-dump.txt 17 | # 18 | 19 | # Test Cases in all.json file 20 | 21 | # Baseball 22 | # PRE - MLB, MIA 23 | - platform: teamtracker 24 | league_id: MLB 25 | team_id: MIA 26 | conference_id: 9999 27 | name: yaml_test01 28 | # IN - MLB, MIL 29 | - platform: teamtracker 30 | league_id: MLB 31 | team_id: MIL 32 | conference_id: 9999 33 | name: yaml_test02 34 | # POST - MLB, CIN 35 | - platform: teamtracker 36 | league_id: MLB 37 | team_id: CIN 38 | conference_id: 9999 39 | name: yaml_test03 40 | 41 | # Football 42 | # PRE - NFL, CHI 43 | - platform: teamtracker 44 | league_id: NFL 45 | team_id: CHI 46 | conference_id: 9999 47 | name: yaml_test04 48 | # IN - NCAAF, LOU 49 | - platform: teamtracker 50 | league_id: NCAAF 51 | team_id: LOU 52 | conference_id: 9999 53 | name: yaml_test05 54 | # POST - NFL, BUF 55 | - platform: teamtracker 56 | league_id: NFL 57 | team_id: BUF 58 | conference_id: 9999 59 | name: yaml_test06 60 | 61 | # Soccer 62 | # PRE - NWSL, ORL 63 | - platform: teamtracker 64 | league_id: NWSL 65 | team_id: ORL 66 | conference_id: 9999 67 | name: yaml_test07 68 | # IN - MLS, CLB 69 | - platform: teamtracker 70 | league_id: MLS 71 | team_id: CLB 72 | conference_id: 9999 73 | name: yaml_test08 74 | # POST - BUND, FCA 75 | - platform: teamtracker 76 | league_id: BUND 77 | team_id: FCA 78 | conference_id: 9999 79 | name: yaml_test09 80 | 81 | # Basketball 82 | # PRE - NBA, GS 83 | - platform: teamtracker 84 | league_id: NBA 85 | team_id: GS 86 | conference_id: 9999 87 | name: yaml_test10 88 | # IN - WNBA, ? 89 | - platform: teamtracker 90 | league_id: NBA 91 | team_id: TBD 92 | conference_id: 9999 93 | name: yaml_test11 94 | # POST - WNBA,? 95 | - platform: teamtracker 96 | league_id: NBA 97 | team_id: TBD 98 | conference_id: 9999 99 | name: yaml_test12 100 | 101 | # Hockey 102 | # PRE - 103 | - platform: teamtracker 104 | league_id: NHL 105 | team_id: TBD 106 | conference_id: 9999 107 | name: yaml_test13 108 | # IN - 109 | - platform: teamtracker 110 | league_id: NHL 111 | team_id: TBD 112 | conference_id: 9999 113 | name: yaml_test14 114 | # POST - 115 | - platform: teamtracker 116 | league_id: NHL 117 | team_id: TBD 118 | conference_id: 9999 119 | name: yaml_test15 120 | 121 | # 122 | # Volleyball 123 | # PRE - NCAAVBW, PEPP 124 | - platform: teamtracker 125 | league_id: NCAAVBW 126 | team_id: PEPP 127 | conference_id: 9999 128 | name: yaml_test16 129 | # IN - NCAAVBW, MSST 130 | - platform: teamtracker 131 | league_id: NCAAVBW 132 | team_id: MSST 133 | conference_id: 9999 134 | name: yaml_test17 135 | # POST - NCAAVBW, ARMY 136 | - platform: teamtracker 137 | league_id: NCAAVBW 138 | team_id: ARMY 139 | conference_id: 9999 140 | name: yaml_test18 141 | 142 | # Error Conditions 143 | # Invalid League - 144 | - platform: teamtracker 145 | league_id: ABC 146 | team_id: XYZ 147 | name: yaml_teste01 148 | # Invalid Team - 149 | - platform: teamtracker 150 | league_id: NFL 151 | team_id: ABC 152 | name: yaml_teste02 --------------------------------------------------------------------------------