From 9caca6dff052861f1bb03364d9ef52e9efb66f80 Mon Sep 17 00:00:00 2001 From: Jon Seager Date: Fri, 16 Aug 2024 14:40:52 +0100 Subject: [PATCH] init: initial commit --- .github/dependabot.yml | 7 + .github/workflows/test.yaml | 60 + .gitignore | 27 + LICENSE | 202 ++ README.md | 109 + pyproject.toml | 60 + pytouchlinesl/__init__.py | 6 + pytouchlinesl/client/__init__.py | 4 + pytouchlinesl/client/base.py | 38 + pytouchlinesl/client/client.py | 196 ++ pytouchlinesl/client/models/__init__.py | 20 + pytouchlinesl/client/models/models.py | 173 ++ pytouchlinesl/module.py | 179 ++ pytouchlinesl/touchlinesl.py | 75 + pytouchlinesl/zone.py | 123 + tests/.gitkeep | 0 tests/__init__.py | 1 + tests/conftest.py | 25 + tests/fake_client.py | 40 + tests/sample-data/module.json | 2724 +++++++++++++++++++++++ tests/sample-data/modules.json | 23 + tests/test_module.py | 194 ++ tests/test_touchlinesl.py | 54 + tests/test_zone.py | 15 + uv.lock | 698 ++++++ 25 files changed, 5053 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/test.yaml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 pyproject.toml create mode 100644 pytouchlinesl/__init__.py create mode 100644 pytouchlinesl/client/__init__.py create mode 100644 pytouchlinesl/client/base.py create mode 100644 pytouchlinesl/client/client.py create mode 100644 pytouchlinesl/client/models/__init__.py create mode 100644 pytouchlinesl/client/models/models.py create mode 100644 pytouchlinesl/module.py create mode 100644 pytouchlinesl/touchlinesl.py create mode 100644 pytouchlinesl/zone.py create mode 100644 tests/.gitkeep create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/fake_client.py create mode 100644 tests/sample-data/module.json create mode 100644 tests/sample-data/modules.json create mode 100644 tests/test_module.py create mode 100644 tests/test_touchlinesl.py create mode 100644 tests/test_zone.py create mode 100644 uv.lock diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..169e893 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every month + interval: "monthly" diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..ea03b01 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,60 @@ +name: Test + +on: + pull_request: + push: + branches: ["main"] + +jobs: + lint: + name: lint + runs-on: ubuntu-latest + + steps: + - name: Checkout the code + uses: actions/checkout@v4 + + - name: Install `uv` + run: | + curl -LsSf https://astral.sh/uv/install.sh | sh + + - name: Lint with `ruff` + run: | + uv run ruff check + + - name: Format with `ruff` + run: | + uv run ruff format --check + + test: + name: pytouchline-${{ matrix.os }} + + strategy: + matrix: + python-version: + - "3.8" + - "3.9" + - "3.10" + - "3.11" + - "3.12" + + fail-fast: false + + runs-on: ubuntu-latest + + steps: + - name: Checkout the code + uses: actions/checkout@v4 + + - name: Install `uv` + run: | + curl -LsSf https://astral.sh/uv/install.sh | sh + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Run tests + run: | + uv run pytest -v diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e49dba8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Files used in development +*.env +test.py +dump.json + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +dist/ +parts/ +sdist/ +wheels/ +*.egg-info/ +*.egg +MANIFEST + +.coverage* +.tox/ +.venv \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..38d2ef1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright jnsgruk + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4d14858 --- /dev/null +++ b/README.md @@ -0,0 +1,109 @@ +# A Python Library for Roth's TouchlineSL System + + + +Roth TouchlineSL is a control system for underfloor heating, cooling and radiator control. They +have a public API which is documented on [their +website](https://api-documentation.roth-touchlinesl.com/). + +This project provides a Python client for interacting with the API, and controlling heating +systems. It does not have complete coverage of the API, and currently provides the facility to: + +- Authenticate with a https://roth-touchlinesl.com account +- List modules associated with an account +- Get details of individual zones +- Get details of global heating schedules +- Set a constant target temperature for a zone +- Assign a zone to a specific global schedule + +The library was designed primarily to support the development of a [Home +Assistant](https://home-assistant.io/) integration. + +## Design + +Roth's API design makes operations on individual zones or schedules quite difficult. Only one +endpoint is provided for fetching the configuration of zones, and it's the same endpoint that +returns data for _all_ zones attached to a module. + +As a result, this client implements some basic caching. Each time the modules endpoint is queried, +the result is cached for 30 seconds. Any POST requests made (setting temperatures, assigning zones +to schedules) will invalidate the cache, and all GET methods have a `refresh` argument that can be +used to force a refresh of the underlying data. + +## Example Usage + +```python +import asyncio +import os + +from pytouchlinesl import TouchlineSL + +async def touchlinesl_example(): + tsl = TouchlineSL( + username=os.getenv("TOUCHLINESL_LOGIN"), + password=os.getenv("TOUCHLINESL_PASSWORD"), + ) + + # Fetch a list of modules associated with the account + modules = await tsl.modules() + module = await tsl.module(module_id=modules[0].id) + + # Fetch a zone by name, set a constant target temperature of 17.0 + utility = await module.zone_by_name("Utility Room") + await utility.set_temperature(17.0) + + # Fetch a zone by ID, assign it to a global schedule named "Living Spaces" + kitchen = await module.zone(2411) + living_spaces = await module.schedule_by_name("Living Spaces") + await kitchen.set_schedule(living_spaces.id) + +if __name__ == "__main__": + asyncio.set_event_loop(asyncio.new_event_loop()) + asyncio.run(touchlinesl_example()) +``` + +## Contributing / Hacking + +Contributions in either code or documentation are welcome. The set of features is limited at the +moment, but I'm happy to expand as the need arises. + +The project does not have many dependencies; just `asyncio` and `pytest`/`pytest-asyncio` for +testing. + +Dependencies are managed using [`uv`](https://github.com/astral-sh/uv). You can get started like +so: + +```bash +# Clone the repository +git clone https://github.com/jnsgruk/pytouchlinesl +cd pytouchlinesl + +# Run the tests +uv run pytest +# Lint the code +uv run ruff check --fix +# Format the code +uv run ruff format +``` + +If you'd rather use standard Python tooling, you can do so: + +```python +# Clone the repository +git clone https://github.com/jnsgruk/pytouchlinesl +cd pytouchlinesl + +# Create a virtual environment +python3 -m venv .venv +source .venv/bin/activate + +# Install dev/project dependencies +pip install -e '.[dev]' + +# Run the tests +pytest -s +# Lint the code +ruff check --fix +# Format the code +ruff format +``` diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ea84fb9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,60 @@ +[project] +name = "pytouchlinesl" +version = "0.1.0" +authors = [{ name = "Jon Seager", email = "jon@sgrs.uk" }] +description = "An API client for Roth's TouchlineSL control system." +readme = "README.md" +requires-python = ">=3.8" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", +] +dependencies = ["aiohttp", "pydantic>=2.8.2"] + +[project.optional-dependencies] +dev = ["pytest", "pytest-asyncio", "ruff"] + +[project.urls] +Homepage = "https://github.com/jnsgruk/pytouchlinesl" +Issues = "https://github.com/jnsgruk/pytouchlinesl/issues" + +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[tool.uv] +dev-dependencies = ["pytest>=8.3.2", "pytest-asyncio>=0.24.0", "ruff>=0.6.2"] + +[tool.pytest.ini_options] +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "module" + +# Linting tools configuration +[tool.ruff] +line-length = 99 +extend-exclude = ["__pycache__", "*.egg_info"] + +[tool.ruff.lint] +per-file-ignores = { "tests/**" = [ + "D", +], "__init__.py" = [ + "F401", +], "pytouchlinesl/client/models/models.py" = [ + "D101", +] } +select = ["E", "W", "F", "C", "N", "D", "I001"] +ignore = ["E501", "D107"] +extend-ignore = [ + "D203", + "D204", + "D213", + "D215", + "D400", + "D404", + "D406", + "D407", + "D408", + "D409", + "D413", +] diff --git a/pytouchlinesl/__init__.py b/pytouchlinesl/__init__.py new file mode 100644 index 0000000..dfd5d8b --- /dev/null +++ b/pytouchlinesl/__init__.py @@ -0,0 +1,6 @@ +"""A Python client for controlling Roth TouchlineSL underfloor heating modules.""" + +from . import client +from .module import Module +from .touchlinesl import TouchlineSL +from .zone import Zone diff --git a/pytouchlinesl/client/__init__.py b/pytouchlinesl/client/__init__.py new file mode 100644 index 0000000..bd1bb96 --- /dev/null +++ b/pytouchlinesl/client/__init__.py @@ -0,0 +1,4 @@ +"""A Python API implementation of the Roth TouchlineSL API.""" + +from .base import BaseClient +from .client import RothAPI, RothAPIError diff --git a/pytouchlinesl/client/base.py b/pytouchlinesl/client/base.py new file mode 100644 index 0000000..96b47ee --- /dev/null +++ b/pytouchlinesl/client/base.py @@ -0,0 +1,38 @@ +"""Provides an abstract API client implementation for clients working with the Roth API.""" + +from abc import ABC, abstractmethod + +from pytouchlinesl.client.models.models import AccountModuleModel + +from .models import ModuleModel + + +class BaseClient(ABC): + """Base API client implementation for Roth API clients.""" + + @abstractmethod + async def authenticated(self) -> bool: + """Report whether or not the client is authenticated with the API.""" + pass + + @abstractmethod + async def modules(self) -> list[AccountModuleModel]: + """Return a list of TouchlineSL modules associated with the authenticated account.""" + pass + + @abstractmethod + async def module(self, module_id: str) -> ModuleModel: + """Return a specific module, by ID, that's associated with the authenticated account.""" + pass + + @abstractmethod + async def set_zone_temperature( + self, module_id: str, mode_id: int, zone_id: int, temperature: float + ): + """Set a constant target temperature for a specific zone, in a specific module.""" + pass + + @abstractmethod + async def set_zone_schedule(self, module_id: str, zone_id: int, schedule_id: int): + """Assign a specific zone to a specific global schedule in the module's configuration.""" + pass diff --git a/pytouchlinesl/client/client.py b/pytouchlinesl/client/client.py new file mode 100644 index 0000000..fa686bb --- /dev/null +++ b/pytouchlinesl/client/client.py @@ -0,0 +1,196 @@ +"""An API client for the public Roth TouchlineSL API. + +API Documentation: https://api-documentation.roth-touchlinesl.com/ +""" + +import json +import logging +from typing import Any + +import aiohttp + +from pytouchlinesl.client.models import GlobalScheduleModel + +from .base import BaseClient +from .models import AccountModuleModel, ModuleModel + +API_URL = "https://roth-touchlinesl.com/api/v1" + +logger = logging.getLogger(__name__) + + +class RothAPIError(Exception): + """An exception raised when an unsuccessful request is made to the Roth API.""" + + def __init__(self, status: int, data: Any = None): + """Construct an instance of the exception.""" + super().__init__() + self._status = status + self._data = data + + @property + def status(self) -> int: + """Return the HTTP status code of the failed request.""" + return self._status + + @property + def data(self) -> Any: + """Return any data included in the response of the failed request.""" + return self._data + + def __repr__(self) -> str: # noqa: D105 + return f"{self.__class__.__name__}({self.__str__()})" + + def __str__(self) -> str: # noqa: D105 + msg = f"{self.status}" + + if self.data is not None: + msg += " " + json.dumps(self.data) + + return msg + + +class RothAPI(BaseClient): + """An implementation of the BaseClient which interacts with Roth's public API.""" + + def __init__(self, username, password): + """Construct an instance of the client. + + Args: + username: Roth TouchlineSL account email. + password: Roth TouchlineSL account password. + """ + self._username = username + self._password = password + self._user_id = None + self._token = None + + async def _login(self): + """Login to the Roth API, and store the received bearer token and user id.""" + data = {"username": self._username, "password": self._password} + resp = await self._post("/authentication", json=data) + + self._user_id = resp.get("user_id") + self._token = resp.get("token") + + async def authenticated(self) -> bool: + """Report whether or not the client is authenticated with the API.""" + if self._token is None: + return False + + resp = await self._authed_get("/authentication") + return resp.get("authenticated", False) + + async def modules(self) -> list[AccountModuleModel]: + """Return a list of TouchlineSL modules associated with the authenticated account.""" + if self._token is None: + await self._login() + + resp = await self._authed_get(f"/users/{self._user_id}/modules") + return [AccountModuleModel(**m) for m in resp] + + async def module(self, module_id: str) -> ModuleModel: + """Return a specific module, by ID, that's associated with the authenticated account.""" + if self._token is None: + await self._login() + + resp = await self._authed_get(f"/users/{self._user_id}/modules/{module_id}") + return ModuleModel(**resp) + + async def set_zone_temperature( + self, module_id: str, mode_id: int, zone_id: int, temperature: float + ): + """Set a constant target temperature for a specific zone, in a specific module.""" + data = { + "mode": { + "id": mode_id, + "parentId": zone_id, + "mode": "constantTemp", + "constTempTime": 0, + "setTemperature": int(temperature * 10), + "scheduleIndex": 0, + } + } + + await self._authd_post(url=f"/users/{self._user_id}/modules/{module_id}/zones", data=data) + + async def set_zone_schedule(self, module_id: str, zone_id: int, schedule_id: int): + """Assign a specific zone to a specific global schedule in the module's configuration.""" + module: ModuleModel = await self.module(module_id=module_id) + schedules = module.zones.global_schedules.elements + schedule = next((s for s in schedules if s.id == schedule_id), None) + assert isinstance(schedule, GlobalScheduleModel) + + # Get a list of zones which are already assigned to the schedule. + # The Roth API is relatively limited, and adding a zone to a schedule means + # completely re-specifying the schedule, including intervals and assigned zones. + zones_on_schedule = [ + z + for z in module.zones.elements + if (z.mode.mode == "globalSchedule" and z.mode.schedule_index == schedule.index) + or z.zone.id == zone_id + ] + + # TODO: This be simplified with Pydantic models + data = { + "scheduleName": schedule.name, + "setInZones": [{"zoneId": z.zone.id, "modeId": z.mode.id} for z in zones_on_schedule], + "schedule": { + "id": schedule.id, + "index": schedule.index, + "p0Days": schedule.p0_days, + "p0SetbackTemp": schedule.p0_setback_temp, + # Empty intervals start/end with a value of 6100, which is invalid + # if sent in this request. + "p0Intervals": [ + i.model_dump(by_alias=True) for i in schedule.p0_intervals if i.start != 6100 + ], + "p1Days": schedule.p1_days, + "p1SetbackTemp": schedule.p1_setback_temp, + "p1Intervals": [ + i.model_dump(by_alias=True) for i in schedule.p1_intervals if i.start != 6100 + ], + }, + } + + await self._authd_post( + url=f"/users/{self._user_id}/modules/{module_id}/zones/{zone_id}/global_schedule", + data=data, + ) + + async def _authed_get(self, url: str, headers: dict[str, str] = {}) -> dict: + """Perform an authenticated GET request to the Roth TouchlineSL API.""" + headers.update({"authorization": f"Bearer {self._token}"}) + return await self._get(url, headers) + + async def _get(self, url: str, headers: dict[str, str] = {}) -> dict: + """Perform a GET request to the Roth TouchlineSL API.""" + logger.debug("GET %s%s", API_URL, url) + + async with aiohttp.ClientSession() as session: + req = await session.get(f"{API_URL}{url}", headers=headers) + resp = await req.json() + + if not req.ok: + raise RothAPIError(status=req.status, data=resp) + + return resp + + async def _authd_post(self, url: str, headers: dict[str, str] = {}, data: dict = {}) -> dict: + """Perform an authenticated POST request to the Roth TouchlineSL API.""" + headers.update({"authorization": f"Bearer {self._token}"}) + return await self._post(url, headers, data) + + async def _post(self, url: str, headers: dict[str, str] = {}, json: dict = {}) -> dict: + """Perform a POST request to the Roth TouchlineSL API.""" + logger.debug("POST %s%s", API_URL, url) + + async with aiohttp.ClientSession() as session: + req = await session.post(f"{API_URL}{url}", headers=headers, json=json) + + resp = await req.json() + + if not req.ok: + raise RothAPIError(status=req.status, data=resp) + + return resp diff --git a/pytouchlinesl/client/models/__init__.py b/pytouchlinesl/client/models/__init__.py new file mode 100644 index 0000000..c4d427b --- /dev/null +++ b/pytouchlinesl/client/models/__init__.py @@ -0,0 +1,20 @@ +"""Provides Pydantic models representing responses from the Roth TouchlineSL API.""" + +from .models import ( + AccountModuleModel, + ControllerModeModel, + ControllerParametersModel, + GlobalScheduleModel, + GlobalSchedulesModel, + LocalScheduleModel, + ModuleModel, + ParamsModel, + ScheduleIntervalModel, + TileModel, + ZoneAttributesModel, + ZoneDescriptionModel, + ZoneFlagsModel, + ZoneModel, + ZoneModeModel, + ZonesModel, +) diff --git a/pytouchlinesl/client/models/models.py b/pytouchlinesl/client/models/models.py new file mode 100644 index 0000000..9cd50eb --- /dev/null +++ b/pytouchlinesl/client/models/models.py @@ -0,0 +1,173 @@ +"""Provides models that represent the data structures returned by the Roth API. + +These are mostly auto-generated using responses received from the API, with some +tweaks to types and names. +""" + +from typing import Any, Dict, List, Literal, Optional + +from pydantic import BaseModel, Field + + +class ZoneFlagsModel(BaseModel): + relay_state: str = Field(..., alias="relayState") + min_one_window_open: bool = Field(..., alias="minOneWindowOpen") + algorithm: str + floor_sensor: int = Field(..., alias="floorSensor") + humidity_algorytm: int = Field(..., alias="humidityAlgorytm") + zone_excluded: int = Field(..., alias="zoneExcluded") + + +class ZoneAttributesModel(BaseModel): + id: int + parent_id: int = Field(..., alias="parentId") + time: str + during_change: bool = Field(..., alias="duringChange") + index: int + current_temperature: Optional[int] = Field(..., alias="currentTemperature") + set_temperature: int = Field(..., alias="setTemperature") + flags: ZoneFlagsModel + zone_state: Literal["zoneOff", "noAlarm"] = Field(..., alias="zoneState") + signal_strength: Optional[int] = Field(..., alias="signalStrength") + battery_level: Optional[int] = Field(..., alias="batteryLevel") + actuators_open: int = Field(..., alias="actuatorsOpen") + humidity: int + visibility: bool + + +class ZoneDescriptionModel(BaseModel): + id: int + parent_id: int = Field(..., alias="parentId") + name: str + style_id: int = Field(..., alias="styleId") + style_icon: str = Field(..., alias="styleIcon") + during_change: bool = Field(..., alias="duringChange") + + +class ZoneModeModel(BaseModel): + id: int + parent_id: int = Field(..., alias="parentId") + mode: Literal["constantTemp", "globalSchedule", "localSchedule", "timeLimit"] + const_temp_time: int = Field(..., alias="constTempTime") + set_temperature: int = Field(..., alias="setTemperature") + schedule_index: int = Field(..., alias="scheduleIndex") + + +class ScheduleIntervalModel(BaseModel): + start: int + stop: int + temp: int + + +class LocalScheduleModel(BaseModel): + id: int + parent_id: int = Field(..., alias="parentId") + index: int + p0_days: List[str] = Field(..., alias="p0Days") + p0_intervals: List[ScheduleIntervalModel] = Field(..., alias="p0Intervals") + p0_setback_temp: int = Field(..., alias="p0SetbackTemp") + p1_days: List[str] = Field(..., alias="p1Days") + p1_intervals: List[ScheduleIntervalModel] = Field(..., alias="p1Intervals") + p1_setback_temp: int = Field(..., alias="p1SetbackTemp") + + +class ZoneModel(BaseModel): + zone: ZoneAttributesModel + description: ZoneDescriptionModel + mode: ZoneModeModel + schedule: LocalScheduleModel + actuators: List + underfloor: Dict[str, Any] + windows_sensors: List = Field(..., alias="windowsSensors") + additional_contacts: List = Field(..., alias="additionalContacts") + + +class GlobalScheduleModel(BaseModel): + id: int + parent_id: int = Field(..., alias="parentId") + index: int + name: str + p0_days: List[str] = Field(..., alias="p0Days") + p0_setback_temp: int = Field(..., alias="p0SetbackTemp") + p0_intervals: List[ScheduleIntervalModel] = Field(..., alias="p0Intervals") + p1_days: List[str] = Field(..., alias="p1Days") + p1_setback_temp: int = Field(..., alias="p1SetbackTemp") + p1_intervals: List[ScheduleIntervalModel] = Field(..., alias="p1Intervals") + + +class GlobalSchedulesModel(BaseModel): + time: str + during_change: bool = Field(..., alias="duringChange") + elements: List[GlobalScheduleModel] + + +class ControllerModeModel(BaseModel): + id: int + parent_id: int = Field(..., alias="parentId") + type: int + txt_id: int = Field(..., alias="txtId") + icon_id: int = Field(..., alias="iconId") + value: int + menu_id: int = Field(..., alias="menuId") + + +class ControllerParametersModel(BaseModel): + controller_mode: ControllerModeModel = Field(..., alias="controllerMode") + global_schedules_number: Dict[str, Any] = Field(..., alias="globalSchedulesNumber") + + +class ZonesModel(BaseModel): + transaction_time: str + elements: List[ZoneModel] + global_schedules: GlobalSchedulesModel = Field(..., alias="globalSchedules") + controller_parameters: ControllerParametersModel = Field(..., alias="controllerParameters") + + +class ParamsModel(BaseModel): + description: str + working_status: Optional[bool] = Field(None, alias="workingStatus") + txt_id: int = Field(..., alias="txtId") + icon_id: int = Field(..., alias="iconId") + version: Optional[str] = None + company_id: Optional[int] = Field(None, alias="companyId") + controller_name: Optional[str] = Field(None, alias="controllerName") + main_controller_id: Optional[int] = Field(None, alias="mainControllerId") + + +class TileModel(BaseModel): + id: int + parent_id: int = Field(..., alias="parentId") + type: int + menu_id: int = Field(..., alias="menuId") + order_id: Any = Field(..., alias="orderId") + visibility: bool + params: ParamsModel + + +class ModuleModel(BaseModel): + zones: ZonesModel + tiles: List[TileModel] + tiles_order: Any = Field(..., alias="tilesOrder") + tiles_last_update: str = Field(..., alias="tilesLastUpdate") + + +class AccountModuleModel(BaseModel): + id: int + default: bool + name: str + email: str + type: str + controller_status: str = Field(..., alias="controllerStatus") + module_status: str = Field(..., alias="moduleStatus") + additional_information: str = Field(..., alias="additionalInformation") + phone_number: Any = Field(..., alias="phoneNumber") + zip_code: str = Field(..., alias="zipCode") + tag: str + country: str + gmt_id: int = Field(..., alias="gmtId") + gmt_time: str = Field(..., alias="gmtTime") + postcode_policy_accepted: bool = Field(..., alias="postcodePolicyAccepted") + style: str + version: str + company: str + udid: str diff --git a/pytouchlinesl/module.py b/pytouchlinesl/module.py new file mode 100644 index 0000000..68f3973 --- /dev/null +++ b/pytouchlinesl/module.py @@ -0,0 +1,179 @@ +"""Provides a representation of a single Roth TouchlineSL module. + +In reality, this usually represents the underfloor heating for +a specific building. It masks the upstream Roth API, which provides +relatively few methods for fetching details about particular zones +and schedules by providing methods to query them directly. + +Because of the upstream API design, there is an inbuilt caching +mechanism that's used to prevent making too many calls to the +modules endpoint. The cache can be invalidated to ensure that +new data is fetched using the `invalidate_cache` method. + +Typical usage: + + import os + from pytouchlinesl import TouchlineSL + + tsl = TouchlineSL(username"foo@bar.com", password="deadbeef") + + modules = await tsl.modules() + if modules: + module = modules[0] + zone = await module.zone_by_name("Kitchen") +""" + +import time + +from pytouchlinesl.client import BaseClient +from pytouchlinesl.client.models import GlobalScheduleModel, ModuleModel +from pytouchlinesl.client.models.models import AccountModuleModel +from pytouchlinesl.zone import Zone + + +class Module: + """A representation of a single Roth TouchlineSL module.""" + + def __init__( + self, + *, + client: BaseClient, + module_data: AccountModuleModel, + cache_validity: int = 30, + ): + """Construct an instance of a module. + + Args: + client: The underlying API client to use for fetching data. + module_data: A raw representation of the Module returned from a client instance. + cache_validity: (Optional) The number of seconds for which module data should be cached. + + """ + self._client: BaseClient = client + + # Information about the module itself + self.id = module_data.udid + self.name = module_data.name + self.email = module_data.email + self.type = module_data.type + self.version = module_data.version + + # Raw data about the zones, schedules, tiles in the module + self._raw_data: ModuleModel + # Unix timestamp representing the last time the _raw_data was fetched + self._last_fetched = 0 + self._cache_validity = cache_validity + + self._zones: list[Zone] = [] + self._schedules: list[GlobalScheduleModel] = [] + + async def _data(self, *, refresh: bool = False) -> ModuleModel: + """Get the raw representation of the module from the upstream API. + + If the data has never been fetched from upstream, or the data is older + than the cache validity period, then the data is refreshed using the + upstream API. + + Args: + refresh: (Optional): Force the data to be refreshed using the API. + """ + if refresh or (round(time.time() * 1000) - self._last_fetched) > self._cache_validity: + self._raw_data = await self._client.module(self.id) + self._last_fetched = round(time.time() * 1000) + + return self._raw_data + + def invalidate_cache(self): + """Reset the cache, ensuring new data is fetched the next time its accessed.""" + self._last_fetched = 0 + self._zones = [] + self._schedules = [] + + async def zones(self, *, include_off: bool = False, refresh: bool = False) -> list[Zone]: + """Return a list of zones, optionally including those which are disabled. + + Args: + include_off: (Optional) Include zones which are switched off in the results. + refresh: (Optional) Force the data to be refreshed using the API. + """ + if not self._zones or refresh: + data = await self._data(refresh=refresh) + + for z in data.zones.elements: + schedule = await self.schedule_by_idx(z.mode.schedule_index) + zone = Zone(module=self, client=self._client, zone_data=z, schedule=schedule) + self._zones.append(zone) + + if include_off: + return self._zones + + return [z for z in self._zones if z.enabled] + + async def zone(self, zone_id: int, *, refresh: bool = False) -> Zone | None: + """Return a specific zone, by ID. + + Args: + zone_id: The ID of the desired zone. + refresh: (Optional) Force the data to be refreshed using the API. + """ + zones = await self.zones(include_off=True, refresh=refresh) + return next((z for z in zones if z.id == zone_id), None) + + async def zone_by_name(self, zone_name: str, *, refresh: bool = False) -> Zone | None: + """Return a specific zone, by name. + + Args: + zone_name: The name of the desired zone. + include_off: (Optional) Include zones which are switched off in the results. + refresh: (Optional) Force the data to be refreshed using the API. + """ + zones = await self.zones(include_off=True, refresh=refresh) + return next((z for z in zones if z.name == zone_name), None) + + async def schedules(self, *, refresh: bool = False) -> list[GlobalScheduleModel]: + """Return a list of the module's global schedules. + + Args: + refresh: (Optional) Force the data to be refreshed using the API. + """ + if not self._schedules or refresh: + data = await self._data(refresh=refresh) + self._schedules = data.zones.global_schedules.elements + + return self._schedules + + async def schedule( + self, schedule_id: int, *, refresh: bool = False + ) -> GlobalScheduleModel | None: + """Return a specific global schedule, by ID. + + Args: + schedule_id: The ID of the desired schedule. + refresh: (Optional) Force the data to be refreshed using the API. + """ + schedules = await self.schedules(refresh=refresh) + return next((s for s in schedules if s.id == schedule_id), None) + + async def schedule_by_idx( + self, schedule_idx: int, *, refresh: bool = False + ) -> GlobalScheduleModel | None: + """Return a specific global schedule, by index. + + Args: + schedule_idx: The index of the desired schedule. + refresh: (Optional) Force the data to be refreshed using the API. + """ + schedules = await self.schedules(refresh=refresh) + return next((s for s in schedules if s.index == schedule_idx), None) + + async def schedule_by_name( + self, schedule_name: str, *, refresh: bool = False + ) -> GlobalScheduleModel | None: + """Return a specific global schedule, by name. + + Args: + schedule_name: The name of the desired schedule. + refresh: (Optional) Force the data to be refreshed using the API. + """ + schedules = await self.schedules(refresh=refresh) + return next((s for s in schedules if s.name == schedule_name), None) diff --git a/pytouchlinesl/touchlinesl.py b/pytouchlinesl/touchlinesl.py new file mode 100644 index 0000000..4d54845 --- /dev/null +++ b/pytouchlinesl/touchlinesl.py @@ -0,0 +1,75 @@ +"""Provides an API client for a specific Roth TouchlineSL account. + +This is the top-level construct expected to be used for interacting +with the TouchlineSL API. It provides a way to list the heating +modules associated with a given account, the authentication details +for which are provided by a RothAPI instance. + +Typical usage example: + + import os + from pytouchlinesl import TouchlineSL + + tsl = TouchlineSL(username"foo@bar.com", password="deadbeef") + modules = await tsl.modules() +""" + +import logging + +from pytouchlinesl.client import BaseClient, RothAPI +from pytouchlinesl.module import Module + +logger = logging.getLogger(__name__) + + +class TouchlineSL: + """An API client for a specific Roth TouchlineSL account.""" + + def __init__( + self, + *, + username: str | None = None, + password: str | None = None, + client: BaseClient | None = None, + ): + """Construct the instance with either credentials or an authenticated client. + + Args: + username: (Optional) Username for TouchlineSL account. Ignored if client is passed. + password: (Optional) Password for TouchlineSL account. Ignored if client is passed. + client: (Optional) An instance of a RothAPI class. + """ + self._modules: list[Module] = [] + + if client: + if username or password: + logger.warning( + "username and password arguments will be ignored because a client was passed" + ) + self._client = client + else: + if username is None or password is None: + raise TypeError("username and password must be strings if no client is provided") + self._client = RothAPI(username=username, password=password) + + async def modules(self, *, refresh: bool = False) -> list[Module]: + """Report a list of Roth TouchlineSL modules associated with the account. + + Args: + refresh: Force the API client to refresh cached module data + """ + if not self._modules or refresh: + data = await self._client.modules() + self._modules = [Module(client=self._client, module_data=m) for m in data] + + return self._modules + + async def module(self, *, module_id: str, refresh: bool = False) -> Module | None: + """Fetch a specific module, by module ID. + + Args: + module_id: The unique identifier of the TouchlineSL module to fetch + refresh: Force the API client to refresh cached module data + """ + modules = await self.modules(refresh=refresh) + return next((m for m in modules if m.id == module_id), None) diff --git a/pytouchlinesl/zone.py b/pytouchlinesl/zone.py new file mode 100644 index 0000000..a8d0e9c --- /dev/null +++ b/pytouchlinesl/zone.py @@ -0,0 +1,123 @@ +"""A representation of a single Roth TouchlineSL zone. + +In most cases, a Zone represents a specific room in a building. +Zones can report on both temperature and humidity. + +Each zone can either be set with a constant target temperature, +set to a specific global schedule, or set onto a local schedule. + +Typical Usage: + + import os + from pytouchlinesl import TouchlineSL + + tsl = TouchlineSL(username"foo@bar.com", password="deadbeef") + + # Get the module from the account + module = await tsl.modules() + module = modules[0] + + # Get a specific zone from the module + zone = await module.zone_by_name("Kitchen") + temp = zone.temperature + + # Set the zone to a constant target temp of 22.5 + await zone.set_temperature(22.5) + + # Set the zone onto a specific global schedule + schedule = module.schedule_by_name("Living Spaces") + await zone.set_schedule(schedule.id) +""" + +from typing import TYPE_CHECKING, Literal + +from .client import BaseClient +from .client.models import GlobalScheduleModel, LocalScheduleModel, ZoneModel + +if TYPE_CHECKING: + from .module import Module + + +class Zone: + """A representation of a single Roth TouchlineSL zone.""" + + def __init__( + self, + module: "Module", + client: BaseClient, + zone_data: ZoneModel, + schedule: GlobalScheduleModel | LocalScheduleModel | None, + ): + """Construct an instance of a zone. + + Args: + module: The Module this Zone belongs to. + client: The underlying API client to use for fetching data. + zone_data: A raw representation of the Zone returned from a client instance. + schedule: None if Zone is set to constant temp, otherwise instance of the global + schedule assigned to the zone + """ + self._module = module + self._client = client + self._raw_data = zone_data + self._schedule = schedule + + @property + def name(self) -> str: + """Return the configured name of the zone.""" + return self._raw_data.description.name + + @property + def id(self) -> int: + """Return the ID of the zone.""" + return self._raw_data.zone.id + + @property + def temperature(self) -> float | None: + """Return the current temperature of the zone, if it's enabled, otherwise None.""" + if self._raw_data.zone.current_temperature: + return float(int(self._raw_data.zone.current_temperature) / 10) + return None + + @property + def target_temperature(self) -> float: + """Return the target temperature of the zone.""" + return float(int(self._raw_data.zone.set_temperature) / 10) + + @property + def humidity(self) -> float: + """Return the humidity of the zone.""" + return float(int(self._raw_data.zone.humidity)) + + @property + def mode(self) -> Literal["constantTemp", "globalSchedule", "localSchedule", "timeLimit"]: + """Return the mode of the zone.""" + return self._raw_data.mode.mode + + @property + def enabled(self) -> bool: + """Return whether or not the zone is enabled.""" + return self._raw_data.zone.zone_state != "zoneOff" + + @property + def schedule(self) -> GlobalScheduleModel | LocalScheduleModel | None: + """Return the zone's current schedule. If a constant temp is set, returns None.""" + if self.mode == "constantTemp": + return None + + return self._schedule + + async def set_temperature(self, temperature: float): + """Set a constant target temperature for the zone.""" + await self._client.set_zone_temperature( + module_id=self._module.id, + zone_id=self.id, + mode_id=self._raw_data.mode.id, + temperature=temperature, + ) + self._module.invalidate_cache() + + async def set_schedule(self, schedule_id: int): + """Assign the zone to a specific global schedule.""" + await self._client.set_zone_schedule(self._module.id, self.id, schedule_id) + self._module.invalidate_cache() diff --git a/tests/.gitkeep b/tests/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..4817c03 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +from .fake_client import FakeRothAPI diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..f66a81b --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,25 @@ +import pytest + +from pytouchlinesl import Module, TouchlineSL, Zone + +from .fake_client import FakeRothAPI + + +@pytest.fixture +def test_touchlinesl() -> TouchlineSL: + client = FakeRothAPI() + return TouchlineSL(client=client) + + +@pytest.fixture +async def test_module(test_touchlinesl: TouchlineSL) -> Module: + m = await test_touchlinesl.module(module_id="1234a5678a9123a456a7891234a56789") + assert isinstance(m, Module) + return m + + +@pytest.fixture +async def test_zone(test_module: Module) -> Zone: + z = await test_module.zone_by_name("Kitchen") + assert isinstance(z, Zone) + return z diff --git a/tests/fake_client.py b/tests/fake_client.py new file mode 100644 index 0000000..973302d --- /dev/null +++ b/tests/fake_client.py @@ -0,0 +1,40 @@ +import json +import os +from pathlib import Path + +from pytouchlinesl.client import BaseClient +from pytouchlinesl.client.models import AccountModuleModel, ModuleModel + +data_dir = Path(os.path.realpath(__file__)).parent / "sample-data" + + +class FakeRothAPI(BaseClient): + def __init__(self): + self.user_id = None + self.token = None + + async def login(self): + self.token = "deadbeef" + + async def authenticated(self) -> bool: + return True if self.token else False + + async def modules(self) -> list[AccountModuleModel]: + with open(data_dir / "modules.json", "r") as f: + data = json.load(f) + + return [AccountModuleModel(**m) for m in data] + + async def module(self, module_id: str) -> ModuleModel: + with open(data_dir / "module.json", "r") as f: + data = json.load(f) + + return ModuleModel(**data) + + async def set_zone_temperature( + self, module_id: str, mode_id: int, zone_id: int, temperature: float + ): + raise NotImplementedError + + async def set_zone_schedule(self, module_id: str, zone_id: int, schedule_id: int): + raise NotImplementedError diff --git a/tests/sample-data/module.json b/tests/sample-data/module.json new file mode 100644 index 0000000..eb00eb9 --- /dev/null +++ b/tests/sample-data/module.json @@ -0,0 +1,2724 @@ +{ + "zones": { + "transaction_time": "2024-08-22T15:46:33.843796+02:00", + "elements": [ + { + "zone": { + "id": 2335, + "parentId": 2333, + "time": "2024-08-22T15:44:00.936163", + "duringChange": false, + "index": 0, + "currentTemperature": 229, + "setTemperature": 170, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "noAlarm", + "signalStrength": 23, + "batteryLevel": 100, + "actuatorsOpen": 0, + "humidity": 58, + "visibility": true + }, + "description": { + "id": 2336, + "parentId": 2335, + "name": "Playroom", + "styleId": 3, + "styleIcon": "children", + "duringChange": false + }, + "mode": { + "id": 2337, + "parentId": 2335, + "mode": "globalSchedule", + "constTempTime": 60, + "setTemperature": 170, + "scheduleIndex": 3 + }, + "schedule": { + "id": 2339, + "parentId": 2335, + "index": -1, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2354, + "parentId": 2333, + "time": "2024-08-12T21:40:43.451932", + "duringChange": false, + "index": 1, + "currentTemperature": null, + "setTemperature": 169, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2355, + "parentId": 2354, + "name": "Zone 2", + "styleId": 1, + "styleIcon": "bathroom", + "duringChange": false + }, + "mode": { + "id": 2356, + "parentId": 2354, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2358, + "parentId": 2354, + "index": -2, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2373, + "parentId": 2333, + "time": "2024-08-22T15:42:46.320969", + "duringChange": false, + "index": 2, + "currentTemperature": 222, + "setTemperature": 180, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "noAlarm", + "signalStrength": 30, + "batteryLevel": 100, + "actuatorsOpen": 0, + "humidity": 59, + "visibility": true + }, + "description": { + "id": 2374, + "parentId": 2373, + "name": "Lounge", + "styleId": 2, + "styleIcon": "kitchen", + "duringChange": false + }, + "mode": { + "id": 2375, + "parentId": 2373, + "mode": "globalSchedule", + "constTempTime": 60, + "setTemperature": 180, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2377, + "parentId": 2373, + "index": -3, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2392, + "parentId": 2333, + "time": "2024-08-12T21:41:17.584356", + "duringChange": false, + "index": 3, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2393, + "parentId": 2392, + "name": "Zone 4", + "styleId": 3, + "styleIcon": "children", + "duringChange": false + }, + "mode": { + "id": 2394, + "parentId": 2392, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2396, + "parentId": 2392, + "index": -4, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2411, + "parentId": 2333, + "time": "2024-08-22T15:18:44.608665", + "duringChange": false, + "index": 4, + "currentTemperature": 221, + "setTemperature": 180, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "noAlarm", + "signalStrength": 53, + "batteryLevel": 100, + "actuatorsOpen": 0, + "humidity": 61, + "visibility": true + }, + "description": { + "id": 2412, + "parentId": 2411, + "name": "Kitchen", + "styleId": 0, + "styleIcon": "living_room", + "duringChange": false + }, + "mode": { + "id": 2413, + "parentId": 2411, + "mode": "globalSchedule", + "constTempTime": 60, + "setTemperature": 180, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2415, + "parentId": 2411, + "index": -5, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2430, + "parentId": 2333, + "time": "2024-08-22T15:32:21.208915", + "duringChange": false, + "index": 5, + "currentTemperature": 231, + "setTemperature": 170, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "noAlarm", + "signalStrength": 100, + "batteryLevel": 100, + "actuatorsOpen": 0, + "humidity": 58, + "visibility": true + }, + "description": { + "id": 2431, + "parentId": 2430, + "name": "Utility Room", + "styleId": 5, + "styleIcon": "wardrobe", + "duringChange": false + }, + "mode": { + "id": 2432, + "parentId": 2430, + "mode": "constantTemp", + "constTempTime": 60, + "setTemperature": 170, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2434, + "parentId": 2430, + "index": -6, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2448, + "parentId": 2333, + "time": "2024-08-22T15:45:26.259793", + "duringChange": false, + "index": 6, + "currentTemperature": 225, + "setTemperature": 170, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "noAlarm", + "signalStrength": 24, + "batteryLevel": 100, + "actuatorsOpen": 0, + "humidity": 58, + "visibility": true + }, + "description": { + "id": 2449, + "parentId": 2448, + "name": "Laura Office", + "styleId": 0, + "styleIcon": "living_room", + "duringChange": false + }, + "mode": { + "id": 2450, + "parentId": 2448, + "mode": "constantTemp", + "constTempTime": 60, + "setTemperature": 170, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2452, + "parentId": 2448, + "index": -7, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2466, + "parentId": 2333, + "time": "2024-08-22T15:44:26.475553", + "duringChange": false, + "index": 7, + "currentTemperature": 246, + "setTemperature": 170, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "noAlarm", + "signalStrength": 31, + "batteryLevel": 100, + "actuatorsOpen": 0, + "humidity": 52, + "visibility": true + }, + "description": { + "id": 2467, + "parentId": 2466, + "name": "Jon Office", + "styleId": 0, + "styleIcon": "living_room", + "duringChange": false + }, + "mode": { + "id": 2468, + "parentId": 2466, + "mode": "constantTemp", + "constTempTime": 60, + "setTemperature": 170, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2470, + "parentId": 2466, + "index": -8, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2484, + "parentId": 2333, + "time": "2024-08-22T15:36:08.168114", + "duringChange": false, + "index": 8, + "currentTemperature": 221, + "setTemperature": 170, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "noAlarm", + "signalStrength": 87, + "batteryLevel": 100, + "actuatorsOpen": 0, + "humidity": 59, + "visibility": true + }, + "description": { + "id": 2485, + "parentId": 2484, + "name": "Guest Bed", + "styleId": 4, + "styleIcon": "bedroom", + "duringChange": false + }, + "mode": { + "id": 2486, + "parentId": 2484, + "mode": "globalSchedule", + "constTempTime": 60, + "setTemperature": 170, + "scheduleIndex": 2 + }, + "schedule": { + "id": 2488, + "parentId": 2484, + "index": -9, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2503, + "parentId": 2333, + "time": "2024-08-22T15:44:30.953926", + "duringChange": false, + "index": 9, + "currentTemperature": 228, + "setTemperature": 170, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "noAlarm", + "signalStrength": 42, + "batteryLevel": 100, + "actuatorsOpen": 0, + "humidity": 58, + "visibility": true + }, + "description": { + "id": 2504, + "parentId": 2503, + "name": "Snug", + "styleId": 0, + "styleIcon": "living_room", + "duringChange": false + }, + "mode": { + "id": 2505, + "parentId": 2503, + "mode": "globalSchedule", + "constTempTime": 60, + "setTemperature": 170, + "scheduleIndex": 4 + }, + "schedule": { + "id": 2507, + "parentId": 2503, + "index": -10, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2522, + "parentId": 2333, + "time": "2024-08-22T15:41:17.679043", + "duringChange": false, + "index": 10, + "currentTemperature": 224, + "setTemperature": 170, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "noAlarm", + "signalStrength": 13, + "batteryLevel": 100, + "actuatorsOpen": 0, + "humidity": 61, + "visibility": true + }, + "description": { + "id": 2523, + "parentId": 2522, + "name": "Master Bed", + "styleId": 4, + "styleIcon": "bedroom", + "duringChange": false + }, + "mode": { + "id": 2524, + "parentId": 2522, + "mode": "globalSchedule", + "constTempTime": 60, + "setTemperature": 170, + "scheduleIndex": 2 + }, + "schedule": { + "id": 2526, + "parentId": 2522, + "index": -11, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2541, + "parentId": 2333, + "time": "2024-08-22T15:46:11.13502", + "duringChange": false, + "index": 11, + "currentTemperature": 215, + "setTemperature": 170, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "noAlarm", + "signalStrength": 97, + "batteryLevel": 100, + "actuatorsOpen": 0, + "humidity": 61, + "visibility": true + }, + "description": { + "id": 2542, + "parentId": 2541, + "name": "Attic Bed", + "styleId": 4, + "styleIcon": "bedroom", + "duringChange": false + }, + "mode": { + "id": 2543, + "parentId": 2541, + "mode": "globalSchedule", + "constTempTime": 60, + "setTemperature": 170, + "scheduleIndex": 3 + }, + "schedule": { + "id": 2545, + "parentId": 2541, + "index": -12, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2560, + "parentId": 2333, + "time": "2024-08-12T21:43:23.776791", + "duringChange": false, + "index": 12, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2561, + "parentId": 2560, + "name": "Zone 13", + "styleId": 4, + "styleIcon": "bedroom", + "duringChange": false + }, + "mode": { + "id": 2562, + "parentId": 2560, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2564, + "parentId": 2560, + "index": -13, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2579, + "parentId": 2333, + "time": "2024-08-12T21:43:41.601669", + "duringChange": false, + "index": 13, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2580, + "parentId": 2579, + "name": "Zone 14", + "styleId": 5, + "styleIcon": "wardrobe", + "duringChange": false + }, + "mode": { + "id": 2581, + "parentId": 2579, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2583, + "parentId": 2579, + "index": -14, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2597, + "parentId": 2333, + "time": "2024-08-12T21:43:50.660486", + "duringChange": false, + "index": 14, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2598, + "parentId": 2597, + "name": "Zone 15", + "styleId": 6, + "styleIcon": "corridor", + "duringChange": false + }, + "mode": { + "id": 2599, + "parentId": 2597, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2601, + "parentId": 2597, + "index": -15, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2615, + "parentId": 2333, + "time": "2024-08-12T21:44:08.621444", + "duringChange": false, + "index": 15, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2616, + "parentId": 2615, + "name": "Zone 16", + "styleId": 7, + "styleIcon": "garage", + "duringChange": false + }, + "mode": { + "id": 2617, + "parentId": 2615, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2619, + "parentId": 2615, + "index": -16, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2633, + "parentId": 2333, + "time": "2024-08-12T21:44:17.644989", + "duringChange": false, + "index": 16, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2634, + "parentId": 2633, + "name": "Zone 17", + "styleId": 0, + "styleIcon": "living_room", + "duringChange": false + }, + "mode": { + "id": 2635, + "parentId": 2633, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2637, + "parentId": 2633, + "index": -17, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2652, + "parentId": 2333, + "time": "2024-08-12T21:44:35.685728", + "duringChange": false, + "index": 17, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2653, + "parentId": 2652, + "name": "Zone 18", + "styleId": 1, + "styleIcon": "bathroom", + "duringChange": false + }, + "mode": { + "id": 2654, + "parentId": 2652, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2656, + "parentId": 2652, + "index": -18, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2671, + "parentId": 2333, + "time": "2024-08-12T21:44:44.671977", + "duringChange": false, + "index": 18, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2672, + "parentId": 2671, + "name": "Zone 19", + "styleId": 2, + "styleIcon": "kitchen", + "duringChange": false + }, + "mode": { + "id": 2673, + "parentId": 2671, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2675, + "parentId": 2671, + "index": -19, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2690, + "parentId": 2333, + "time": "2024-08-12T21:45:02.78159", + "duringChange": false, + "index": 19, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2691, + "parentId": 2690, + "name": "Zone 20", + "styleId": 3, + "styleIcon": "children", + "duringChange": false + }, + "mode": { + "id": 2692, + "parentId": 2690, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2694, + "parentId": 2690, + "index": -20, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2709, + "parentId": 2333, + "time": "2024-08-12T21:45:11.667288", + "duringChange": false, + "index": 20, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2710, + "parentId": 2709, + "name": "Zone 21", + "styleId": 4, + "styleIcon": "bedroom", + "duringChange": false + }, + "mode": { + "id": 2711, + "parentId": 2709, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2713, + "parentId": 2709, + "index": -21, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2728, + "parentId": 2333, + "time": "2024-08-12T21:45:29.630532", + "duringChange": false, + "index": 21, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2729, + "parentId": 2728, + "name": "Zone 22", + "styleId": 5, + "styleIcon": "wardrobe", + "duringChange": false + }, + "mode": { + "id": 2730, + "parentId": 2728, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2732, + "parentId": 2728, + "index": -22, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2746, + "parentId": 2333, + "time": "2024-08-12T21:45:38.660197", + "duringChange": false, + "index": 22, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2747, + "parentId": 2746, + "name": "Zone 23", + "styleId": 6, + "styleIcon": "corridor", + "duringChange": false + }, + "mode": { + "id": 2748, + "parentId": 2746, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2750, + "parentId": 2746, + "index": -23, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2764, + "parentId": 2333, + "time": "2024-08-12T21:45:57.446097", + "duringChange": false, + "index": 23, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2765, + "parentId": 2764, + "name": "Zone 24", + "styleId": 7, + "styleIcon": "garage", + "duringChange": false + }, + "mode": { + "id": 2766, + "parentId": 2764, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2768, + "parentId": 2764, + "index": -24, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2782, + "parentId": 2333, + "time": "2024-08-12T21:46:05.679477", + "duringChange": false, + "index": 24, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2783, + "parentId": 2782, + "name": "Zone 25", + "styleId": 0, + "styleIcon": "living_room", + "duringChange": false + }, + "mode": { + "id": 2784, + "parentId": 2782, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2786, + "parentId": 2782, + "index": -25, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2801, + "parentId": 2333, + "time": "2024-08-12T21:46:23.808992", + "duringChange": false, + "index": 25, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2802, + "parentId": 2801, + "name": "Zone 26", + "styleId": 1, + "styleIcon": "bathroom", + "duringChange": false + }, + "mode": { + "id": 2803, + "parentId": 2801, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2805, + "parentId": 2801, + "index": -26, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2820, + "parentId": 2333, + "time": "2024-08-12T21:46:32.95147", + "duringChange": false, + "index": 26, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2821, + "parentId": 2820, + "name": "Zone 27", + "styleId": 2, + "styleIcon": "kitchen", + "duringChange": false + }, + "mode": { + "id": 2822, + "parentId": 2820, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2824, + "parentId": 2820, + "index": -27, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2839, + "parentId": 2333, + "time": "2024-08-12T21:46:50.726384", + "duringChange": false, + "index": 27, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2840, + "parentId": 2839, + "name": "Zone 28", + "styleId": 3, + "styleIcon": "children", + "duringChange": false + }, + "mode": { + "id": 2841, + "parentId": 2839, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2843, + "parentId": 2839, + "index": -28, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2858, + "parentId": 2333, + "time": "2024-08-12T21:46:59.781723", + "duringChange": false, + "index": 28, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2859, + "parentId": 2858, + "name": "Zone 29", + "styleId": 4, + "styleIcon": "bedroom", + "duringChange": false + }, + "mode": { + "id": 2860, + "parentId": 2858, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2862, + "parentId": 2858, + "index": -29, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2877, + "parentId": 2333, + "time": "2024-08-12T21:47:17.701659", + "duringChange": false, + "index": 29, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2878, + "parentId": 2877, + "name": "Zone 30", + "styleId": 5, + "styleIcon": "wardrobe", + "duringChange": false + }, + "mode": { + "id": 2879, + "parentId": 2877, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2881, + "parentId": 2877, + "index": -30, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2895, + "parentId": 2333, + "time": "2024-08-12T21:47:26.773943", + "duringChange": false, + "index": 30, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2896, + "parentId": 2895, + "name": "Zone 31", + "styleId": 6, + "styleIcon": "corridor", + "duringChange": false + }, + "mode": { + "id": 2897, + "parentId": 2895, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2899, + "parentId": 2895, + "index": -31, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2913, + "parentId": 2333, + "time": "2024-08-12T21:47:44.682668", + "duringChange": false, + "index": 31, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2914, + "parentId": 2913, + "name": "Zone 32", + "styleId": 7, + "styleIcon": "garage", + "duringChange": false + }, + "mode": { + "id": 2915, + "parentId": 2913, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2917, + "parentId": 2913, + "index": -32, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2931, + "parentId": 2333, + "time": "2024-08-12T21:47:53.747407", + "duringChange": false, + "index": 32, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2932, + "parentId": 2931, + "name": "Zone 33", + "styleId": 0, + "styleIcon": "living_room", + "duringChange": false + }, + "mode": { + "id": 2933, + "parentId": 2931, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2935, + "parentId": 2931, + "index": -33, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2950, + "parentId": 2333, + "time": "2024-08-12T21:48:11.683631", + "duringChange": false, + "index": 33, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2951, + "parentId": 2950, + "name": "Zone 34", + "styleId": 1, + "styleIcon": "bathroom", + "duringChange": false + }, + "mode": { + "id": 2952, + "parentId": 2950, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2954, + "parentId": 2950, + "index": -34, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2969, + "parentId": 2333, + "time": "2024-08-12T21:48:20.737711", + "duringChange": false, + "index": 34, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2970, + "parentId": 2969, + "name": "Zone 35", + "styleId": 2, + "styleIcon": "kitchen", + "duringChange": false + }, + "mode": { + "id": 2971, + "parentId": 2969, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2973, + "parentId": 2969, + "index": -35, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 2988, + "parentId": 2333, + "time": "2024-08-12T21:48:38.693448", + "duringChange": false, + "index": 35, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 2989, + "parentId": 2988, + "name": "Zone 36", + "styleId": 3, + "styleIcon": "children", + "duringChange": false + }, + "mode": { + "id": 2990, + "parentId": 2988, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 2992, + "parentId": 2988, + "index": -36, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 3007, + "parentId": 2333, + "time": "2024-08-12T21:48:43.218415", + "duringChange": false, + "index": 36, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 3008, + "parentId": 3007, + "name": "Zone 37", + "styleId": 4, + "styleIcon": "bedroom", + "duringChange": false + }, + "mode": { + "id": 3009, + "parentId": 3007, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 3011, + "parentId": 3007, + "index": -37, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 3026, + "parentId": 2333, + "time": "2024-08-12T21:48:52.184392", + "duringChange": false, + "index": 37, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 3027, + "parentId": 3026, + "name": "Zone 38", + "styleId": 5, + "styleIcon": "wardrobe", + "duringChange": false + }, + "mode": { + "id": 3028, + "parentId": 3026, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 3030, + "parentId": 3026, + "index": -38, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 3044, + "parentId": 2333, + "time": "2024-08-12T21:48:56.733603", + "duringChange": false, + "index": 38, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 3045, + "parentId": 3044, + "name": "Zone 39", + "styleId": 6, + "styleIcon": "corridor", + "duringChange": false + }, + "mode": { + "id": 3046, + "parentId": 3044, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 3048, + "parentId": 3044, + "index": -39, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + }, + { + "zone": { + "id": 3062, + "parentId": 2333, + "time": "2024-08-12T21:49:05.772826", + "duringChange": false, + "index": 39, + "currentTemperature": null, + "setTemperature": 190, + "flags": { + "relayState": "off", + "minOneWindowOpen": false, + "algorithm": "heating", + "floorSensor": 0, + "humidityAlgorytm": 0, + "zoneExcluded": 0 + }, + "zoneState": "zoneOff", + "signalStrength": null, + "batteryLevel": null, + "actuatorsOpen": 0, + "humidity": 0, + "visibility": true + }, + "description": { + "id": 3063, + "parentId": 3062, + "name": "Zone 40", + "styleId": 7, + "styleIcon": "garage", + "duringChange": false + }, + "mode": { + "id": 3064, + "parentId": 3062, + "mode": "timeLimit", + "constTempTime": 60, + "setTemperature": 200, + "scheduleIndex": 0 + }, + "schedule": { + "id": 3066, + "parentId": 3062, + "index": -40, + "p0Days": ["1", "1", "1", "1", "1", "0", "0"], + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 240 }, + { "start": 660, "stop": 1380, "temp": 240 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p0SetbackTemp": 260, + "p1Days": ["0", "0", "0", "0", "0", "1", "1"], + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1SetbackTemp": 150 + }, + "actuators": [], + "underfloor": {}, + "windowsSensors": [], + "additionalContacts": [] + } + ], + "globalSchedules": { + "time": "2024-08-20T18:39:19.845888", + "duringChange": false, + "elements": [ + { + "id": 2948, + "parentId": 2333, + "index": 0, + "name": "Living Spaces", + "p0Days": ["1", "1", "1", "1", "1", "1", "1"], + "p0SetbackTemp": 180, + "p0Intervals": [ + { "start": 300, "stop": 540, "temp": 200 }, + { "start": 1020, "stop": 1200, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1Days": ["0", "0", "0", "0", "0", "0", "0"], + "p1SetbackTemp": 175, + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ] + }, + { + "id": 2967, + "parentId": 2333, + "index": 1, + "name": "Holiday", + "p0Days": ["1", "1", "1", "1", "1", "1", "1"], + "p0SetbackTemp": 150, + "p0Intervals": [ + { "start": 300, "stop": 600, "temp": 170 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1Days": ["0", "0", "0", "0", "0", "0", "0"], + "p1SetbackTemp": 260, + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ] + }, + { + "id": 2986, + "parentId": 2333, + "index": 2, + "name": "Bedrooms", + "p0Days": ["1", "1", "1", "1", "1", "1", "1"], + "p0SetbackTemp": 170, + "p0Intervals": [ + { "start": 300, "stop": 540, "temp": 190 }, + { "start": 1200, "stop": 1320, "temp": 190 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1Days": ["0", "0", "0", "0", "0", "0", "0"], + "p1SetbackTemp": 190, + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ] + }, + { + "id": 3005, + "parentId": 2333, + "index": 3, + "name": "Public Rooms", + "p0Days": ["1", "1", "1", "1", "1", "1", "1"], + "p0SetbackTemp": 170, + "p0Intervals": [ + { "start": 300, "stop": 540, "temp": 190 }, + { "start": 1020, "stop": 1200, "temp": 190 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1Days": ["0", "0", "0", "0", "0", "0", "0"], + "p1SetbackTemp": 150, + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ] + }, + { + "id": 3024, + "parentId": 2333, + "index": 4, + "name": "Snug", + "p0Days": ["1", "1", "1", "1", "1", "1", "1"], + "p0SetbackTemp": 170, + "p0Intervals": [ + { "start": 1020, "stop": 1260, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ], + "p1Days": ["0", "0", "0", "0", "0", "0", "0"], + "p1SetbackTemp": 190, + "p1Intervals": [ + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 }, + { "start": 6100, "stop": 6100, "temp": 200 } + ] + } + ] + }, + "controllerParameters": { + "controllerMode": { + "id": 2334, + "parentId": 0, + "type": 108, + "txtId": 0, + "iconId": 0, + "value": 0, + "menuId": 1000 + }, + "globalSchedulesNumber": {} + } + }, + "tiles": [ + { + "id": 2326, + "parentId": 0, + "type": 11, + "menuId": 0, + "orderId": null, + "visibility": true, + "params": { "description": "Relay", "workingStatus": false, "txtId": 1736, "iconId": 5 } + }, + { + "id": 2328, + "parentId": 0, + "type": 11, + "menuId": 0, + "orderId": null, + "visibility": true, + "params": { "description": "Relay", "workingStatus": false, "txtId": 3089, "iconId": 17 } + }, + { + "id": 3080, + "parentId": 0, + "type": 50, + "menuId": 0, + "orderId": null, + "visibility": true, + "params": { + "description": "Controller software version", + "version": "1.0.6", + "txtId": 873, + "iconId": 29, + "companyId": 97, + "controllerName": "L-11", + "mainControllerId": 87 + } + } + ], + "tilesOrder": null, + "tilesLastUpdate": "2024-08-22 15:46:33.921131+02" +} diff --git a/tests/sample-data/modules.json b/tests/sample-data/modules.json new file mode 100644 index 0000000..704b254 --- /dev/null +++ b/tests/sample-data/modules.json @@ -0,0 +1,23 @@ +[ + { + "id": 0, + "default": false, + "name": "Test Module", + "email": "foo@bar.com", + "type": "zones_controller", + "controllerStatus": "active", + "moduleStatus": "active", + "additionalInformation": "foo@bar.com", + "phoneNumber": null, + "zipCode": "12345", + "tag": "ROTH", + "country": "United Kingdom", + "gmtId": 30, + "gmtTime": "1", + "postcodePolicyAccepted": true, + "style": "roth", + "version": "ROTH: L-11 (v.1.0.6)", + "company": "roth", + "udid": "1234a5678a9123a456a7891234a56789" + } +] diff --git a/tests/test_module.py b/tests/test_module.py new file mode 100644 index 0000000..58caa72 --- /dev/null +++ b/tests/test_module.py @@ -0,0 +1,194 @@ +import asyncio + +import pytest + +from pytouchlinesl.client.models import GlobalScheduleModel +from pytouchlinesl.zone import Zone + + +@pytest.mark.asyncio +async def test_zones(test_module): + zones = await test_module.zones() + # There should be 10 enabled zones + assert len(zones) == 10 + # Ensure that each of the zones has been correctly constructed by the + # modules method. + for z in zones: + assert isinstance(z, Zone) + if z.mode == "constantTemp": + assert z.schedule is None + else: + assert isinstance(z.schedule, GlobalScheduleModel) + + +@pytest.mark.asyncio +async def test_zones_include_off(test_module): + zones = await test_module.zones(include_off=True) + # There should be 40 total zones, 10 enabled, 30 disabled + assert len(zones) == 40 + + +@pytest.mark.asyncio +async def test_zones_include_off_and_refresh(test_module): + zones = await test_module.zones(include_off=True, refresh=True) + # There should be 40 total zones, 10 enabled, 30 disabled + assert len(zones) == 40 + + +@pytest.mark.asyncio +async def test_zones_cache(test_module): + await test_module.zones() + initial_fetch_time = test_module._last_fetched + await asyncio.sleep(0.5) + + await test_module.zones() + assert initial_fetch_time == test_module._last_fetched + + +@pytest.mark.asyncio +async def test_zones_force_refresh(test_module): + await test_module.zones() + initial_fetch_time = test_module._last_fetched + await asyncio.sleep(0.5) + await test_module.zones(refresh=True) + assert initial_fetch_time != test_module._last_fetched + + +@pytest.mark.asyncio +@pytest.mark.parametrize("id", [2335, 2354]) # test both enabled and disabled zones +async def test_zone(id, test_module): + z = await test_module.zone(zone_id=id) + assert z.id == id + + +@pytest.mark.asyncio +async def test_zone_non_existent(test_module): + z = await test_module.zone(zone_id=1234) + assert z is None + + +@pytest.mark.asyncio +async def test_zone_refresh(test_module): + await test_module.zones() + last_fetched = test_module._last_fetched + await asyncio.sleep(0.5) + await test_module.zone(zone_id=2335, refresh=True) + assert last_fetched != test_module._last_fetched + + +@pytest.mark.asyncio +@pytest.mark.parametrize("name", ["Kitchen", "Zone 2"]) # test both enabled and disabled zones +async def test_zone_by_name(name, test_module): + z = await test_module.zone_by_name(zone_name=name) + assert z.name == name + + +@pytest.mark.asyncio +async def test_zone_by_name_non_existent(test_module): + z = await test_module.zone_by_name(zone_name="Foobar") + assert z is None + + +@pytest.mark.asyncio +async def test_zone_by_name_refresh(test_module): + await test_module.zones() + last_fetched = test_module._last_fetched + await asyncio.sleep(0.5) + await test_module.zone_by_name(zone_name="Kitchen", refresh=True) + assert last_fetched != test_module._last_fetched + + +@pytest.mark.asyncio +async def test_schedules(test_module): + schedules = await test_module.schedules() + # There should be 10 enabled schedules + assert len(schedules) == 5 + # Ensure that each of the schedules has been correctly constructed by the + # modules method. + for z in schedules: + assert isinstance(z, GlobalScheduleModel) + + +@pytest.mark.asyncio +async def test_schedules_cache(test_module): + await test_module.schedules() + initial_fetch_time = test_module._last_fetched + await asyncio.sleep(0.5) + + await test_module.schedules() + assert initial_fetch_time == test_module._last_fetched + + +@pytest.mark.asyncio +async def test_schedules_force_refresh(test_module): + await test_module.schedules() + initial_fetch_time = test_module._last_fetched + await asyncio.sleep(0.5) + await test_module.schedules(refresh=True) + assert initial_fetch_time != test_module._last_fetched + + +@pytest.mark.asyncio +@pytest.mark.parametrize("id", [2948, 2967]) # test both enabled and disabled zones +async def test_schedule(id, test_module): + z = await test_module.schedule(schedule_id=id) + assert z.id == id + + +@pytest.mark.asyncio +async def test_schedule_non_existent(test_module): + z = await test_module.schedule(schedule_id=1234) + assert z is None + + +@pytest.mark.asyncio +async def test_schedule_refresh(test_module): + await test_module.schedules() + last_fetched = test_module._last_fetched + await asyncio.sleep(0.5) + await test_module.schedule(schedule_id=2948, refresh=True) + assert last_fetched != test_module._last_fetched + + +@pytest.mark.asyncio +@pytest.mark.parametrize("name", ["Living Spaces", "Bedrooms"]) +async def test_schedule_by_name(name, test_module): + z = await test_module.schedule_by_name(schedule_name=name) + assert z.name == name + + +@pytest.mark.asyncio +async def test_schedule_by_name_non_existent(test_module): + z = await test_module.schedule_by_name(schedule_name="Foobar") + assert z is None + + +@pytest.mark.asyncio +async def test_schedule_by_name_refresh(test_module): + await test_module.schedules() + last_fetched = test_module._last_fetched + await asyncio.sleep(0.5) + await test_module.schedule_by_name(schedule_name="Holiday", refresh=True) + assert last_fetched != test_module._last_fetched + + +@pytest.mark.asyncio +@pytest.mark.parametrize("idx", [0, 1]) +async def test_schedule_by_idx(idx, test_module): + z = await test_module.schedule_by_idx(schedule_idx=idx) + assert z.index == idx + + +@pytest.mark.asyncio +async def test_schedule_by_idx_non_existent(test_module): + z = await test_module.schedule_by_idx(schedule_idx=10) + assert z is None + + +@pytest.mark.asyncio +async def test_schedule_by_idx_refresh(test_module): + await test_module.schedules() + last_fetched = test_module._last_fetched + await asyncio.sleep(0.5) + await test_module.schedule_by_idx(schedule_idx="Holiday", refresh=True) + assert last_fetched != test_module._last_fetched diff --git a/tests/test_touchlinesl.py b/tests/test_touchlinesl.py new file mode 100644 index 0000000..4aa0c06 --- /dev/null +++ b/tests/test_touchlinesl.py @@ -0,0 +1,54 @@ +import pytest + +from pytouchlinesl import Module, TouchlineSL +from pytouchlinesl.client import RothAPI + +from . import FakeRothAPI + + +def test_construct_touchlinesl_credentials(): + TouchlineSL(username="foo", password="bar") + + +def test_construct_touchlinesl_client(): + fake_client = FakeRothAPI() + TouchlineSL(client=fake_client) + + real_client = RothAPI(username="foo", password="bar") + TouchlineSL(client=real_client) + + +def test_construct_touchlinesl_client_and_creds(caplog): + TouchlineSL(username="foo", password="bar", client=FakeRothAPI()) + assert ( + "username and password arguments will be ignored because a client was passed" + in caplog.text + ) + + +@pytest.mark.parametrize("username,password", [(None, None), ("Foo", None), (None, "Foo")]) +def test_construct_touchlinesl_bad_args(username, password): + try: + TouchlineSL(username=username, password=password) + except Exception as e: + assert isinstance(e, TypeError) + assert str(e) == "username and password must be strings if no client is provided" + + +@pytest.mark.asyncio +async def test_modules(test_touchlinesl: TouchlineSL): + modules = await test_touchlinesl.modules() + for m in modules: + assert isinstance(m, Module) + + +@pytest.mark.asyncio +async def test_module_bad_module_id(test_touchlinesl: TouchlineSL): + module = await test_touchlinesl.module(module_id="123") + assert module is None + + +@pytest.mark.asyncio +async def test_module_good_module_id(test_touchlinesl: TouchlineSL): + module = await test_touchlinesl.module(module_id="1234a5678a9123a456a7891234a56789") + assert isinstance(module, Module) diff --git a/tests/test_zone.py b/tests/test_zone.py new file mode 100644 index 0000000..4fe697d --- /dev/null +++ b/tests/test_zone.py @@ -0,0 +1,15 @@ +import pytest + +test_attrs = [ + ("temperature", 22.1), + ("target_temperature", 18.0), + ("humidity", 61.0), + ("mode", "globalSchedule"), + ("enabled", True), +] + + +@pytest.mark.asyncio +@pytest.mark.parametrize("attr,val", test_attrs) +async def test_zone_attributes(attr, val, test_zone): + assert getattr(test_zone, attr) == val diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..49c24cc --- /dev/null +++ b/uv.lock @@ -0,0 +1,698 @@ +version = 1 +requires-python = ">=3.8" +resolution-markers = [ + "python_full_version < '3.13'", + "python_full_version >= '3.13'", +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f7/22bba300a16fd1cad99da1a23793fe43963ee326d012fdf852d0b4035955/aiohappyeyeballs-2.4.0.tar.gz", hash = "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2", size = 16786 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/b6/58ea188899950d759a837f9a58b2aee1d1a380ea4d6211ce9b1823748851/aiohappyeyeballs-2.4.0-py3-none-any.whl", hash = "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd", size = 12155 }, +] + +[[package]] +name = "aiohttp" +version = "3.10.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "async-timeout", marker = "python_full_version < '3.11'" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/28/ca549838018140b92a19001a8628578b0f2a3b38c16826212cc6f706e6d4/aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691", size = 7524360 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/4a/b27dd9b88fe22dde88742b341fd10251746a6ffcfe1c0b8b15b4a8cbd7c1/aiohttp-3.10.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3", size = 587010 }, + { url = "https://files.pythonhosted.org/packages/de/a9/0f7e2b71549c9d641086c423526ae7a10de3b88d03ba104a3df153574d0d/aiohttp-3.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6", size = 397698 }, + { url = "https://files.pythonhosted.org/packages/3b/52/26baa486e811c25b0cd16a494038260795459055568713f841e78f016481/aiohttp-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699", size = 389052 }, + { url = "https://files.pythonhosted.org/packages/33/df/71ba374a3e925539cb2f6e6d4f5326e7b6b200fabbe1b3cc5e6368f07ce7/aiohttp-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6", size = 1248615 }, + { url = "https://files.pythonhosted.org/packages/67/02/bb89c1eba08a27fc844933bee505d63d480caf8e2816c06961d2941cd128/aiohttp-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1", size = 1282930 }, + { url = "https://files.pythonhosted.org/packages/db/36/07d8cfcc37f39c039f93a4210cc71dadacca003609946c63af23659ba656/aiohttp-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f", size = 1317250 }, + { url = "https://files.pythonhosted.org/packages/9a/44/cabeac994bef8ba521b552ae996928afc6ee1975a411385a07409811b01f/aiohttp-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb", size = 1243212 }, + { url = "https://files.pythonhosted.org/packages/5a/11/23f1e31f5885ac72be52fd205981951dd2e4c87c5b1487cf82fde5bbd46c/aiohttp-3.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91", size = 1213401 }, + { url = "https://files.pythonhosted.org/packages/3f/e7/6e69a0b0d896fbaf1192d492db4c21688e6c0d327486da610b0e8195bcc9/aiohttp-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f", size = 1212450 }, + { url = "https://files.pythonhosted.org/packages/a9/7f/a42f51074c723ea848254946aec118f1e59914a639dc8ba20b0c9247c195/aiohttp-3.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c", size = 1211324 }, + { url = "https://files.pythonhosted.org/packages/d5/43/c2f9d2f588ccef8f028f0a0c999b5ceafecbda50b943313faee7e91f3e03/aiohttp-3.10.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69", size = 1266838 }, + { url = "https://files.pythonhosted.org/packages/c1/a7/ff9f067ecb06896d859e4f2661667aee4bd9c616689599ff034b63cbd9d7/aiohttp-3.10.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3", size = 1285301 }, + { url = "https://files.pythonhosted.org/packages/9a/e3/dd56bb4c67d216046ce61d98dec0f3023043f1de48f561df1bf93dd47aea/aiohttp-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683", size = 1235806 }, + { url = "https://files.pythonhosted.org/packages/a7/64/90dcd42ac21927a49ba4140b2e4d50e1847379427ef6c43eb338ef9960e3/aiohttp-3.10.5-cp310-cp310-win32.whl", hash = "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef", size = 360162 }, + { url = "https://files.pythonhosted.org/packages/f3/45/145d8b4853fc92c0c8509277642767e7726a085e390ce04353dc68b0f5b5/aiohttp-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088", size = 379173 }, + { url = "https://files.pythonhosted.org/packages/f1/90/54ccb1e4eadfb6c95deff695582453f6208584431d69bf572782e9ae542b/aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2", size = 586455 }, + { url = "https://files.pythonhosted.org/packages/c3/7a/95e88c02756e7e718f054e1bb3ec6ad5d0ee4a2ca2bb1768c5844b3de30a/aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf", size = 397255 }, + { url = "https://files.pythonhosted.org/packages/07/4f/767387b39990e1ee9aba8ce642abcc286d84d06e068dc167dab983898f18/aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e", size = 388973 }, + { url = "https://files.pythonhosted.org/packages/61/46/0df41170a4d228c07b661b1ba9d87101d99a79339dc93b8b1183d8b20545/aiohttp-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77", size = 1326126 }, + { url = "https://files.pythonhosted.org/packages/af/20/da0d65e07ce49d79173fed41598f487a0a722e87cfbaa8bb7e078a7c1d39/aiohttp-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061", size = 1364538 }, + { url = "https://files.pythonhosted.org/packages/aa/20/b59728405114e57541ba9d5b96033e69d004e811ded299537f74237629ca/aiohttp-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697", size = 1399896 }, + { url = "https://files.pythonhosted.org/packages/2a/92/006690c31b830acbae09d2618e41308fe4c81c0679b3b33a3af859e0b7bf/aiohttp-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7", size = 1312914 }, + { url = "https://files.pythonhosted.org/packages/d4/71/1a253ca215b6c867adbd503f1e142117527ea8775e65962bc09b2fad1d2c/aiohttp-3.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0", size = 1271301 }, + { url = "https://files.pythonhosted.org/packages/0a/ab/5d1d9ff9ce6cce8fa54774d0364e64a0f3cd50e512ff09082ced8e5217a1/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5", size = 1291652 }, + { url = "https://files.pythonhosted.org/packages/75/5f/f90510ea954b9ae6e7a53d2995b97a3e5c181110fdcf469bc9238445871d/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e", size = 1286289 }, + { url = "https://files.pythonhosted.org/packages/be/9e/1f523414237798660921817c82b9225a363af436458caf584d2fa6a2eb4a/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1", size = 1341848 }, + { url = "https://files.pythonhosted.org/packages/f6/36/443472ddaa85d7d80321fda541d9535b23ecefe0bf5792cc3955ea635190/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277", size = 1361619 }, + { url = "https://files.pythonhosted.org/packages/19/f6/3ecbac0bc4359c7d7ba9e85c6b10f57e20edaf1f97751ad2f892db231ad0/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058", size = 1320869 }, + { url = "https://files.pythonhosted.org/packages/34/7e/ed74ffb36e3a0cdec1b05d8fbaa29cb532371d5a20058b3a8052fc90fe7c/aiohttp-3.10.5-cp311-cp311-win32.whl", hash = "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072", size = 359271 }, + { url = "https://files.pythonhosted.org/packages/98/1b/718901f04bc8c886a742be9e83babb7b93facabf7c475cc95e2b3ab80b4d/aiohttp-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff", size = 379143 }, + { url = "https://files.pythonhosted.org/packages/d9/1c/74f9dad4a2fc4107e73456896283d915937f48177b99867b63381fadac6e/aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487", size = 583468 }, + { url = "https://files.pythonhosted.org/packages/12/29/68d090551f2b58ce76c2b436ced8dd2dfd32115d41299bf0b0c308a5483c/aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a", size = 394066 }, + { url = "https://files.pythonhosted.org/packages/8f/f7/971f88b4cdcaaa4622925ba7d86de47b48ec02a9040a143514b382f78da4/aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d", size = 389098 }, + { url = "https://files.pythonhosted.org/packages/f1/5a/fe3742efdce551667b2ddf1158b27c5b8eb1edc13d5e14e996e52e301025/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75", size = 1332742 }, + { url = "https://files.pythonhosted.org/packages/1a/52/a25c0334a1845eb4967dff279151b67ca32a948145a5812ed660ed900868/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178", size = 1372134 }, + { url = "https://files.pythonhosted.org/packages/96/3d/33c1d8efc2d8ec36bff9a8eca2df9fdf8a45269c6e24a88e74f2aa4f16bd/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e", size = 1414413 }, + { url = "https://files.pythonhosted.org/packages/64/74/0f1ddaa5f0caba1d946f0dd0c31f5744116e4a029beec454ec3726d3311f/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f", size = 1328107 }, + { url = "https://files.pythonhosted.org/packages/0a/32/c10118f0ad50e4093227234f71fd0abec6982c29367f65f32ee74ed652c4/aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73", size = 1280126 }, + { url = "https://files.pythonhosted.org/packages/c6/c9/77e3d648d97c03a42acfe843d03e97be3c5ef1b4d9de52e5bd2d28eed8e7/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf", size = 1292660 }, + { url = "https://files.pythonhosted.org/packages/7e/5d/99c71f8e5c8b64295be421b4c42d472766b263a1fe32e91b64bf77005bf2/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820", size = 1300988 }, + { url = "https://files.pythonhosted.org/packages/8f/2c/76d2377dd947f52fbe8afb19b18a3b816d66c7966755c04030f93b1f7b2d/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca", size = 1339268 }, + { url = "https://files.pythonhosted.org/packages/fd/e6/3d9d935cc705d57ed524d82ec5d6b678a53ac1552720ae41282caa273584/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91", size = 1366993 }, + { url = "https://files.pythonhosted.org/packages/fe/c2/f7eed4d602f3f224600d03ab2e1a7734999b0901b1c49b94dc5891340433/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6", size = 1329459 }, + { url = "https://files.pythonhosted.org/packages/ce/8f/27f205b76531fc592abe29e1ad265a16bf934a9f609509c02d765e6a8055/aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12", size = 356968 }, + { url = "https://files.pythonhosted.org/packages/39/8c/4f6c0b2b3629f6be6c81ab84d9d577590f74f01d4412bfc4067958eaa1e1/aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc", size = 377650 }, + { url = "https://files.pythonhosted.org/packages/7b/b9/03b4327897a5b5d29338fa9b514f1c2f66a3e4fc88a4e40fad478739314d/aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092", size = 576994 }, + { url = "https://files.pythonhosted.org/packages/67/1b/20c2e159cd07b8ed6dde71c2258233902fdf415b2fe6174bd2364ba63107/aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77", size = 390684 }, + { url = "https://files.pythonhosted.org/packages/4d/6b/ff83b34f157e370431d8081c5d1741963f4fb12f9aaddb2cacbf50305225/aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385", size = 386176 }, + { url = "https://files.pythonhosted.org/packages/4d/a1/6e92817eb657de287560962df4959b7ddd22859c4b23a0309e2d3de12538/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972", size = 1303310 }, + { url = "https://files.pythonhosted.org/packages/04/29/200518dc7a39c30ae6d5bc232d7207446536e93d3d9299b8e95db6e79c54/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16", size = 1340445 }, + { url = "https://files.pythonhosted.org/packages/8e/20/53f7bba841ba7b5bb5dea580fea01c65524879ba39cb917d08c845524717/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6", size = 1385121 }, + { url = "https://files.pythonhosted.org/packages/f1/b4/d99354ad614c48dd38fb1ee880a1a54bd9ab2c3bcad3013048d4a1797d3a/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa", size = 1299669 }, + { url = "https://files.pythonhosted.org/packages/51/39/ca1de675f2a5729c71c327e52ac6344e63f036bd37281686ae5c3fb13bfb/aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689", size = 1252638 }, + { url = "https://files.pythonhosted.org/packages/54/cf/a3ae7ff43138422d477348e309ef8275779701bf305ff6054831ef98b782/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57", size = 1266889 }, + { url = "https://files.pythonhosted.org/packages/6e/7a/c6027ad70d9fb23cf254a26144de2723821dade1a624446aa22cd0b6d012/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f", size = 1266249 }, + { url = "https://files.pythonhosted.org/packages/64/fd/ed136d46bc2c7e3342fed24662b4827771d55ceb5a7687847aae977bfc17/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599", size = 1311036 }, + { url = "https://files.pythonhosted.org/packages/76/9a/43eeb0166f1119256d6f43468f900db1aed7fbe32069d2a71c82f987db4d/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5", size = 1338756 }, + { url = "https://files.pythonhosted.org/packages/d5/bc/d01ff0810b3f5e26896f76d44225ed78b088ddd33079b85cd1a23514318b/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987", size = 1299976 }, + { url = "https://files.pythonhosted.org/packages/3e/c9/50a297c4f7ab57a949f4add2d3eafe5f3e68bb42f739e933f8b32a092bda/aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04", size = 355609 }, + { url = "https://files.pythonhosted.org/packages/65/28/aee9d04fb0b3b1f90622c338a08e54af5198e704a910e20947c473298fd0/aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022", size = 375697 }, + { url = "https://files.pythonhosted.org/packages/bf/38/af64b5bdc7f1dd0a2cb58642c1cad07327986b8ca0e04805cc1224300f98/aiohttp-3.10.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f6f18898ace4bcd2d41a122916475344a87f1dfdec626ecde9ee802a711bc569", size = 590983 }, + { url = "https://files.pythonhosted.org/packages/c4/4f/075778adb55b5f1f4479676cd3da5da866521b631b771e6aed954bd124ec/aiohttp-3.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5ede29d91a40ba22ac1b922ef510aab871652f6c88ef60b9dcdf773c6d32ad7a", size = 399780 }, + { url = "https://files.pythonhosted.org/packages/13/1a/cefe399a9dd4c822a4ec6bcfdf9487a8a0c742fd4b37f04a3996cccc8404/aiohttp-3.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:673f988370f5954df96cc31fd99c7312a3af0a97f09e407399f61583f30da9bc", size = 390827 }, + { url = "https://files.pythonhosted.org/packages/50/2f/bc787d6a613c259ad91fae0618523b14e95e97a9b4c4c54bebd34e58b818/aiohttp-3.10.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58718e181c56a3c02d25b09d4115eb02aafe1a732ce5714ab70326d9776457c3", size = 1275191 }, + { url = "https://files.pythonhosted.org/packages/06/aa/2955e4dad1f75a395baecd3d683da18184af2bde3552b359b255aa3710cb/aiohttp-3.10.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b38b1570242fbab8d86a84128fb5b5234a2f70c2e32f3070143a6d94bc854cf", size = 1327255 }, + { url = "https://files.pythonhosted.org/packages/cc/d3/72f1b5fb62f8767a1b05761b9eb64f7a2b9949b9a5ce1c8b7ae154f2048a/aiohttp-3.10.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:074d1bff0163e107e97bd48cad9f928fa5a3eb4b9d33366137ffce08a63e37fe", size = 1362290 }, + { url = "https://files.pythonhosted.org/packages/8b/70/92ef8f2383577a8b2133ed674107c0e1d1586d15fc7d4f3f2a418bff41c7/aiohttp-3.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd31f176429cecbc1ba499d4aba31aaccfea488f418d60376b911269d3b883c5", size = 1270036 }, + { url = "https://files.pythonhosted.org/packages/29/0b/3e7e6499186b5e66123ddeebcdc21ba57af21350fc271594d4173333b585/aiohttp-3.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7384d0b87d4635ec38db9263e6a3f1eb609e2e06087f0aa7f63b76833737b471", size = 1233699 }, + { url = "https://files.pythonhosted.org/packages/04/e5/a2675e9e55caf0925bbc2e45b09ca86e9d80d5e0b045070944762f84b1c1/aiohttp-3.10.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8989f46f3d7ef79585e98fa991e6ded55d2f48ae56d2c9fa5e491a6e4effb589", size = 1236358 }, + { url = "https://files.pythonhosted.org/packages/c2/c8/94839dd4de86ada12235f2bf6048702a971c9b38d0acdef7a9c655da66a7/aiohttp-3.10.5-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c83f7a107abb89a227d6c454c613e7606c12a42b9a4ca9c5d7dad25d47c776ae", size = 1237713 }, + { url = "https://files.pythonhosted.org/packages/e2/c4/ec89f0c607985878926d87ef8c3ecf73b576b98a512f49e6e47fb9299390/aiohttp-3.10.5-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cde98f323d6bf161041e7627a5fd763f9fd829bcfcd089804a5fdce7bb6e1b7d", size = 1304638 }, + { url = "https://files.pythonhosted.org/packages/d9/11/134507580ae63421f90cf3066a4613e499d29ddbc7ac9494c6af12ab1651/aiohttp-3.10.5-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:676f94c5480d8eefd97c0c7e3953315e4d8c2b71f3b49539beb2aa676c58272f", size = 1322702 }, + { url = "https://files.pythonhosted.org/packages/6d/73/c2ed04d8a9b6d0a7d63e0fa03f44c67c06f69a72cdeb03adda672e3ec6ed/aiohttp-3.10.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2d21ac12dc943c68135ff858c3a989f2194a709e6e10b4c8977d7fcd67dfd511", size = 1261787 }, + { url = "https://files.pythonhosted.org/packages/dc/07/dd237b77ba664dd9a864ff23b54f852f537d9e1a28b429eadf5ee0f532a8/aiohttp-3.10.5-cp38-cp38-win32.whl", hash = "sha256:17e997105bd1a260850272bfb50e2a328e029c941c2708170d9d978d5a30ad9a", size = 361659 }, + { url = "https://files.pythonhosted.org/packages/64/22/f494c9a556d05f0c58e9a213ff69c65de624b50582e518e8ef5c89e793ff/aiohttp-3.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:1c19de68896747a2aa6257ae4cf6ef59d73917a36a35ee9d0a6f48cff0f94db8", size = 381398 }, + { url = "https://files.pythonhosted.org/packages/7d/0f/6bcda6528ba2ae65c58e62d63c31ada74b0d761efbb6678d19a447520392/aiohttp-3.10.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7e2fe37ac654032db1f3499fe56e77190282534810e2a8e833141a021faaab0e", size = 588725 }, + { url = "https://files.pythonhosted.org/packages/3b/db/c818fd1c254bcb7af4ca75b69c89ee58807e11d9338348065d1b549a9ee7/aiohttp-3.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5bf3ead3cb66ab990ee2561373b009db5bc0e857549b6c9ba84b20bc462e172", size = 398568 }, + { url = "https://files.pythonhosted.org/packages/6a/56/4a1e41e632c97d2848dfb866a87f6802b9541c0720cc1017a5002cd58873/aiohttp-3.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b2c16a919d936ca87a3c5f0e43af12a89a3ce7ccbce59a2d6784caba945b68b", size = 389860 }, + { url = "https://files.pythonhosted.org/packages/86/d0/c0eb2bbdc2808b80432b6c1a56e2db433fac8c84247f895a30f13be2b68d/aiohttp-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad146dae5977c4dd435eb31373b3fe9b0b1bf26858c6fc452bf6af394067e10b", size = 1253862 }, + { url = "https://files.pythonhosted.org/packages/8e/61/2f46f41bf4491cdfb6d599a486bc426332f103773a4e8003b2b09d2b7b2e/aiohttp-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c5c6fa16412b35999320f5c9690c0f554392dc222c04e559217e0f9ae244b92", size = 1289926 }, + { url = "https://files.pythonhosted.org/packages/88/83/e846ae7546a056e271823c02c3002cc6e722c95bce32582cf3e8578c7b0b/aiohttp-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95c4dc6f61d610bc0ee1edc6f29d993f10febfe5b76bb470b486d90bbece6b22", size = 1325020 }, + { url = "https://files.pythonhosted.org/packages/23/69/200bf165b56c17854d54975f894de10dababc4d0226c07600c9abc679e7e/aiohttp-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da452c2c322e9ce0cfef392e469a26d63d42860f829026a63374fde6b5c5876f", size = 1246350 }, + { url = "https://files.pythonhosted.org/packages/ca/45/d5f6ec14e948d1c72c91f743de5b5f26a476c81151082910002b59c84e0b/aiohttp-3.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:898715cf566ec2869d5cb4d5fb4be408964704c46c96b4be267442d265390f32", size = 1216314 }, + { url = "https://files.pythonhosted.org/packages/9b/c7/2078ebb25cfcd0ebadbc451b508f09fe37e3ca3a2fbe3b2791d00912d31c/aiohttp-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:391cc3a9c1527e424c6865e087897e766a917f15dddb360174a70467572ac6ce", size = 1216766 }, + { url = "https://files.pythonhosted.org/packages/d9/59/25f96afdc4f6ba845feb607026b632181b37fc4e3242e4dce3d71a0afaa1/aiohttp-3.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:380f926b51b92d02a34119d072f178d80bbda334d1a7e10fa22d467a66e494db", size = 1216190 }, + { url = "https://files.pythonhosted.org/packages/f1/cb/2b6e003cdd3388454b183aabb91b15db9ac5b47eb224d3b6436f938e7380/aiohttp-3.10.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce91db90dbf37bb6fa0997f26574107e1b9d5ff939315247b7e615baa8ec313b", size = 1271316 }, + { url = "https://files.pythonhosted.org/packages/71/1c/1ce6e7a0376ebb861521c96ed47eda1e0c2e9c80c0407e431b46cecc4bfb/aiohttp-3.10.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9093a81e18c45227eebe4c16124ebf3e0d893830c6aca7cc310bfca8fe59d857", size = 1288007 }, + { url = "https://files.pythonhosted.org/packages/01/0c/4e8db6e6d8f3b655d762530a083ea729b5d1ed479ddc55881d845bcd4795/aiohttp-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ee40b40aa753d844162dcc80d0fe256b87cba48ca0054f64e68000453caead11", size = 1238304 }, + { url = "https://files.pythonhosted.org/packages/4c/bd/69a87f5fd0070e339eb4f62d0ca61e87f85bde492746401852cd40f5113c/aiohttp-3.10.5-cp39-cp39-win32.whl", hash = "sha256:03f2645adbe17f274444953bdea69f8327e9d278d961d85657cb0d06864814c1", size = 360755 }, + { url = "https://files.pythonhosted.org/packages/8d/e9/cfdf2e0132860976514439c8a50b57fc8d65715d77eeec0e5b150e9c6a96/aiohttp-3.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:d17920f18e6ee090bdd3d0bfffd769d9f2cb4c8ffde3eb203777a3895c128862", size = 379781 }, +] + +[[package]] +name = "aiosignal" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/67/0952ed97a9793b4958e5736f6d2b346b414a2cd63e82d05940032f45b32f/aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc", size = 19422 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/ac/a7305707cb852b7e16ff80eaf5692309bde30e2b1100a1fcacdc8f731d97/aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17", size = 7617 }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "async-timeout" +version = "4.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/d6/21b30a550dafea84b1b8eee21b5e23fa16d010ae006011221f33dcd8d7f8/async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f", size = 8345 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/fa/e01228c2938de91d47b307831c62ab9e4001e747789d0b05baf779a6488c/async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028", size = 5721 }, +] + +[[package]] +name = "attrs" +version = "24.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/0f/aafca9af9315aee06a89ffde799a10a582fe8de76c563ee80bbcdc08b3fb/attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", size = 792678 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2", size = 63001 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, +] + +[[package]] +name = "frozenlist" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/3d/2102257e7acad73efc4a0c306ad3953f68c504c16982bbdfee3ad75d8085/frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b", size = 37820 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/35/1328c7b0f780d34f8afc1d87ebdc2bb065a123b24766a0b475f0d67da637/frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac", size = 94315 }, + { url = "https://files.pythonhosted.org/packages/f4/d6/ca016b0adcf8327714ccef969740688808c86e0287bf3a639ff582f24e82/frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868", size = 53805 }, + { url = "https://files.pythonhosted.org/packages/ae/83/bcdaa437a9bd693ba658a0310f8cdccff26bd78e45fccf8e49897904a5cd/frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776", size = 52163 }, + { url = "https://files.pythonhosted.org/packages/d4/e9/759043ab7d169b74fe05ebfbfa9ee5c881c303ebc838e308346204309cd0/frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a", size = 238595 }, + { url = "https://files.pythonhosted.org/packages/f8/ce/b9de7dc61e753dc318cf0de862181b484178210c5361eae6eaf06792264d/frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad", size = 262428 }, + { url = "https://files.pythonhosted.org/packages/36/ce/dc6f29e0352fa34ebe45421960c8e7352ca63b31630a576e8ffb381e9c08/frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c", size = 258867 }, + { url = "https://files.pythonhosted.org/packages/51/47/159ac53faf8a11ae5ee8bb9db10327575557504e549cfd76f447b969aa91/frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe", size = 229412 }, + { url = "https://files.pythonhosted.org/packages/ec/25/0c87df2e53c0c5d90f7517ca0ff7aca78d050a8ec4d32c4278e8c0e52e51/frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a", size = 239539 }, + { url = "https://files.pythonhosted.org/packages/97/94/a1305fa4716726ae0abf3b1069c2d922fcfd442538cb850f1be543f58766/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98", size = 253379 }, + { url = "https://files.pythonhosted.org/packages/53/82/274e19f122e124aee6d113188615f63b0736b4242a875f482a81f91e07e2/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75", size = 245901 }, + { url = "https://files.pythonhosted.org/packages/b8/28/899931015b8cffbe155392fe9ca663f981a17e1adc69589ee0e1e7cdc9a2/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5", size = 263797 }, + { url = "https://files.pythonhosted.org/packages/6e/4f/b8a5a2f10c4a58c52a52a40cf6cf1ffcdbf3a3b64f276f41dab989bf3ab5/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950", size = 264415 }, + { url = "https://files.pythonhosted.org/packages/b0/2c/7be3bdc59dbae444864dbd9cde82790314390ec54636baf6b9ce212627ad/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc", size = 253964 }, + { url = "https://files.pythonhosted.org/packages/2e/ec/4fb5a88f6b9a352aed45ab824dd7ce4801b7bcd379adcb927c17a8f0a1a8/frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1", size = 44559 }, + { url = "https://files.pythonhosted.org/packages/61/15/2b5d644d81282f00b61e54f7b00a96f9c40224107282efe4cd9d2bf1433a/frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439", size = 50434 }, + { url = "https://files.pythonhosted.org/packages/01/bc/8d33f2d84b9368da83e69e42720cff01c5e199b5a868ba4486189a4d8fa9/frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0", size = 97060 }, + { url = "https://files.pythonhosted.org/packages/af/b2/904500d6a162b98a70e510e743e7ea992241b4f9add2c8063bf666ca21df/frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49", size = 55347 }, + { url = "https://files.pythonhosted.org/packages/5b/9c/f12b69997d3891ddc0d7895999a00b0c6a67f66f79498c0e30f27876435d/frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced", size = 53374 }, + { url = "https://files.pythonhosted.org/packages/ac/6e/e0322317b7c600ba21dec224498c0c5959b2bce3865277a7c0badae340a9/frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0", size = 273288 }, + { url = "https://files.pythonhosted.org/packages/a7/76/180ee1b021568dad5b35b7678616c24519af130ed3fa1e0f1ed4014e0f93/frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106", size = 284737 }, + { url = "https://files.pythonhosted.org/packages/05/08/40159d706a6ed983c8aca51922a93fc69f3c27909e82c537dd4054032674/frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068", size = 280267 }, + { url = "https://files.pythonhosted.org/packages/e0/18/9f09f84934c2b2aa37d539a322267939770362d5495f37783440ca9c1b74/frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2", size = 258778 }, + { url = "https://files.pythonhosted.org/packages/b3/c9/0bc5ee7e1f5cc7358ab67da0b7dfe60fbd05c254cea5c6108e7d1ae28c63/frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19", size = 272276 }, + { url = "https://files.pythonhosted.org/packages/12/5d/147556b73a53ad4df6da8bbb50715a66ac75c491fdedac3eca8b0b915345/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82", size = 272424 }, + { url = "https://files.pythonhosted.org/packages/83/61/2087bbf24070b66090c0af922685f1d0596c24bb3f3b5223625bdeaf03ca/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec", size = 260881 }, + { url = "https://files.pythonhosted.org/packages/a8/be/a235bc937dd803258a370fe21b5aa2dd3e7bfe0287a186a4bec30c6cccd6/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a", size = 282327 }, + { url = "https://files.pythonhosted.org/packages/5d/e7/b2469e71f082948066b9382c7b908c22552cc705b960363c390d2e23f587/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74", size = 281502 }, + { url = "https://files.pythonhosted.org/packages/db/1b/6a5b970e55dffc1a7d0bb54f57b184b2a2a2ad0b7bca16a97ca26d73c5b5/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2", size = 272292 }, + { url = "https://files.pythonhosted.org/packages/1a/05/ebad68130e6b6eb9b287dacad08ea357c33849c74550c015b355b75cc714/frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17", size = 44446 }, + { url = "https://files.pythonhosted.org/packages/b3/21/c5aaffac47fd305d69df46cfbf118768cdf049a92ee6b0b5cb029d449dcf/frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825", size = 50459 }, + { url = "https://files.pythonhosted.org/packages/b4/db/4cf37556a735bcdb2582f2c3fa286aefde2322f92d3141e087b8aeb27177/frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae", size = 93937 }, + { url = "https://files.pythonhosted.org/packages/46/03/69eb64642ca8c05f30aa5931d6c55e50b43d0cd13256fdd01510a1f85221/frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb", size = 53656 }, + { url = "https://files.pythonhosted.org/packages/3f/ab/c543c13824a615955f57e082c8a5ee122d2d5368e80084f2834e6f4feced/frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b", size = 51868 }, + { url = "https://files.pythonhosted.org/packages/a9/b8/438cfd92be2a124da8259b13409224d9b19ef8f5a5b2507174fc7e7ea18f/frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86", size = 280652 }, + { url = "https://files.pythonhosted.org/packages/54/72/716a955521b97a25d48315c6c3653f981041ce7a17ff79f701298195bca3/frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480", size = 286739 }, + { url = "https://files.pythonhosted.org/packages/65/d8/934c08103637567084568e4d5b4219c1016c60b4d29353b1a5b3587827d6/frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09", size = 289447 }, + { url = "https://files.pythonhosted.org/packages/70/bb/d3b98d83ec6ef88f9bd63d77104a305d68a146fd63a683569ea44c3085f6/frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a", size = 265466 }, + { url = "https://files.pythonhosted.org/packages/0b/f2/b8158a0f06faefec33f4dff6345a575c18095a44e52d4f10c678c137d0e0/frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd", size = 281530 }, + { url = "https://files.pythonhosted.org/packages/ea/a2/20882c251e61be653764038ece62029bfb34bd5b842724fff32a5b7a2894/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6", size = 281295 }, + { url = "https://files.pythonhosted.org/packages/4c/f9/8894c05dc927af2a09663bdf31914d4fb5501653f240a5bbaf1e88cab1d3/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1", size = 268054 }, + { url = "https://files.pythonhosted.org/packages/37/ff/a613e58452b60166507d731812f3be253eb1229808e59980f0405d1eafbf/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b", size = 286904 }, + { url = "https://files.pythonhosted.org/packages/cc/6e/0091d785187f4c2020d5245796d04213f2261ad097e0c1cf35c44317d517/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e", size = 290754 }, + { url = "https://files.pythonhosted.org/packages/a5/c2/e42ad54bae8bcffee22d1e12a8ee6c7717f7d5b5019261a8c861854f4776/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8", size = 282602 }, + { url = "https://files.pythonhosted.org/packages/b6/61/56bad8cb94f0357c4bc134acc30822e90e203b5cb8ff82179947de90c17f/frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89", size = 44063 }, + { url = "https://files.pythonhosted.org/packages/3e/dc/96647994a013bc72f3d453abab18340b7f5e222b7b7291e3697ca1fcfbd5/frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5", size = 50452 }, + { url = "https://files.pythonhosted.org/packages/32/c7/cc0db0d69ee0dbd85fb453650ce86436f15c39a8cde4d2b432fddc77a80e/frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d", size = 97416 }, + { url = "https://files.pythonhosted.org/packages/07/eb/71b5531dfb71eb6272b6e2281139d7d46b6adaf43c59850bc8ff64ac1860/frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826", size = 55248 }, + { url = "https://files.pythonhosted.org/packages/a0/9f/255b4d34a4f8ff7f31db79406917c403032aa19b39a651ad0a0d6b467317/frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb", size = 53797 }, + { url = "https://files.pythonhosted.org/packages/3b/75/30ff63c92b4c561803662bb7e75b5a6863a2af434e6ff05be8a514a41dd2/frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6", size = 239543 }, + { url = "https://files.pythonhosted.org/packages/43/ec/362807e1682bb0f6a5f34d15994125d72fc7e52fb9b8e4953b13384dcc94/frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d", size = 256966 }, + { url = "https://files.pythonhosted.org/packages/b8/d5/35bba11c3f32283996611dbd88c5357b3ff7bcea63509f8e35b62fa9525a/frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887", size = 252874 }, + { url = "https://files.pythonhosted.org/packages/90/e4/d205655ac3db4dc1bb96ccb1dd59c0d38d54349408ad840bea85a3dd66e9/frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a", size = 231439 }, + { url = "https://files.pythonhosted.org/packages/45/4d/175b16d42daae8013bb1872f6d0870abd87da93e0a36706da4c9ba655d19/frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b", size = 240857 }, + { url = "https://files.pythonhosted.org/packages/0c/fa/ef6a96b7757c969b3d7be55c3e70951409509464f5177624d62c894656b6/frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701", size = 246935 }, + { url = "https://files.pythonhosted.org/packages/44/7e/f3177ed74571eb55779bc3c9ac486505ffc4306852f48c6ee5bd82baecb0/frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0", size = 239742 }, + { url = "https://files.pythonhosted.org/packages/eb/59/e4d3a794b2d9b7ca86a266b61a949f5cccec7a88d818f3b6ad8b80b3ad65/frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11", size = 257417 }, + { url = "https://files.pythonhosted.org/packages/31/fb/d6dc05b56cc30bf6abef2f2100ff6d6d417c33b956a642d768d5e11b5fdf/frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09", size = 255624 }, + { url = "https://files.pythonhosted.org/packages/dc/c9/21abed93eddf089dd0ddd7e09203f2f3dad5d2b784674603a319b0f0c02c/frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7", size = 248001 }, + { url = "https://files.pythonhosted.org/packages/05/6b/e76e740c826acc2ebb5ad5eb06bac15269cd950fc51bd86bbcdbbc04a863/frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497", size = 44799 }, + { url = "https://files.pythonhosted.org/packages/57/0e/63fba1e3a50f2e55d980aa633b8b58062ec7777333aabf0cc3a07a13eb5e/frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09", size = 50820 }, + { url = "https://files.pythonhosted.org/packages/d3/fb/6f2a22086065bc16797f77168728f0e59d5b89be76dd184e06b404f1e43b/frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e", size = 97291 }, + { url = "https://files.pythonhosted.org/packages/4d/23/7f01123d0e5adcc65cbbde5731378237dea7db467abd19e391f1ddd4130d/frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d", size = 55249 }, + { url = "https://files.pythonhosted.org/packages/8b/c9/a81e9af48291954a883d35686f32308238dc968043143133b8ac9e2772af/frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8", size = 53676 }, + { url = "https://files.pythonhosted.org/packages/57/15/172af60c7e150a1d88ecc832f2590721166ae41eab582172fe1e9844eab4/frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0", size = 239365 }, + { url = "https://files.pythonhosted.org/packages/8c/a4/3dc43e259960ad268ef8f2bf92912c2d2cd2e5275a4838804e03fd6f085f/frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b", size = 265592 }, + { url = "https://files.pythonhosted.org/packages/a0/c1/458cf031fc8cd29a751e305b1ec773785ce486106451c93986562c62a21e/frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0", size = 261274 }, + { url = "https://files.pythonhosted.org/packages/4a/32/21329084b61a119ecce0b2942d30312a34a7a0dccd01dcf7b40bda80f22c/frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897", size = 230787 }, + { url = "https://files.pythonhosted.org/packages/70/b0/6f1ebdabfb604e39a0f84428986b89ab55f246b64cddaa495f2c953e1f6b/frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7", size = 240674 }, + { url = "https://files.pythonhosted.org/packages/a3/05/50c53f1cdbfdf3d2cb9582a4ea5e12cd939ce33bd84403e6d07744563486/frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742", size = 255712 }, + { url = "https://files.pythonhosted.org/packages/b8/3d/cbc6f057f7d10efb7f1f410e458ac090f30526fd110ed2b29bb56ec38fe1/frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea", size = 247618 }, + { url = "https://files.pythonhosted.org/packages/96/86/d5e9cd583aed98c9ee35a3aac2ce4d022ce9de93518e963aadf34a18143b/frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5", size = 266868 }, + { url = "https://files.pythonhosted.org/packages/0f/6e/542af762beb9113f13614a590cafe661e0e060cddddee6107c8833605776/frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9", size = 266439 }, + { url = "https://files.pythonhosted.org/packages/ea/db/8b611e23fda75da5311b698730a598df54cfe6236678001f449b1dedb241/frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6", size = 256677 }, + { url = "https://files.pythonhosted.org/packages/eb/06/732cefc0c46c638e4426a859a372a50e4c9d62e65dbfa7ddcf0b13e6a4f2/frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932", size = 44825 }, + { url = "https://files.pythonhosted.org/packages/29/eb/2110c4be2f622e87864e433efd7c4ee6e4f8a59ff2a93c1aa426ee50a8b8/frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0", size = 50652 }, + { url = "https://files.pythonhosted.org/packages/83/10/466fe96dae1bff622021ee687f68e5524d6392b0a2f80d05001cd3a451ba/frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7", size = 11552 }, +] + +[[package]] +name = "idna" +version = "3.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/ed/f86a79a07470cb07819390452f178b3bef1d375f2ec021ecfc709fc7cf07/idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", size = 189575 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/3e/741d8c82801c347547f8a2a06aa57dbb1992be9e948df2ea0eda2c8b79e8/idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0", size = 66836 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "multidict" +version = "6.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/79/722ca999a3a09a63b35aac12ec27dfa8e5bb3a38b0f857f7a1a209a88836/multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da", size = 59867 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/36/48097b96135017ed1b806c5ea27b6cdc2ed3a6861c5372b793563206c586/multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9", size = 50955 }, + { url = "https://files.pythonhosted.org/packages/d9/48/037440edb5d4a1c65e002925b2f24071d6c27754e6f4734f63037e3169d6/multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604", size = 30361 }, + { url = "https://files.pythonhosted.org/packages/a4/eb/d8e7693c9064554a1585698d1902839440c6c695b0f53c9a8be5d9d4a3b8/multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600", size = 30508 }, + { url = "https://files.pythonhosted.org/packages/f3/7d/fe7648d4b2f200f8854066ce6e56bf51889abfaf859814c62160dd0e32a9/multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c", size = 126318 }, + { url = "https://files.pythonhosted.org/packages/8d/ea/0230b6faa9a5bc10650fd50afcc4a86e6c37af2fe05bc679b74d79253732/multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5", size = 133998 }, + { url = "https://files.pythonhosted.org/packages/36/6d/d2f982fb485175727a193b4900b5f929d461e7aa87d6fb5a91a377fcc9c0/multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f", size = 129150 }, + { url = "https://files.pythonhosted.org/packages/33/62/2c9085e571318d51212a6914566fe41dd0e33d7f268f7e2f23dcd3f06c56/multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae", size = 124266 }, + { url = "https://files.pythonhosted.org/packages/ce/e2/88cdfeaf03eab3498f688a19b62ca704d371cd904cb74b682541ca7b20a7/multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182", size = 116637 }, + { url = "https://files.pythonhosted.org/packages/12/4d/99dfc36872dcc53956879f5da80a6505bbd29214cce90ce792a86e15fddf/multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf", size = 155908 }, + { url = "https://files.pythonhosted.org/packages/c2/5c/1e76b2c742cb9e0248d1e8c4ed420817879230c833fa27d890b5fd22290b/multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442", size = 147111 }, + { url = "https://files.pythonhosted.org/packages/bc/84/9579004267e1cc5968ef2ef8718dab9d8950d99354d85b739dd67b09c273/multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a", size = 160502 }, + { url = "https://files.pythonhosted.org/packages/11/b7/bef33e84e3722bc42531af020d7ae8c31235ce8846bacaa852b6484cf868/multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef", size = 156587 }, + { url = "https://files.pythonhosted.org/packages/26/ce/f745a2d6104e56f7fa0d7d0756bb9ed27b771dd7b8d9d7348cd7f0f7b9de/multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc", size = 151948 }, + { url = "https://files.pythonhosted.org/packages/f1/50/714da64281d2b2b3b4068e84f115e1ef3bd3ed3715b39503ff3c59e8d30d/multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319", size = 25734 }, + { url = "https://files.pythonhosted.org/packages/ef/3d/ba0dc18e96c5d83731c54129819d5892389e180f54ebb045c6124b2e8b87/multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8", size = 28182 }, + { url = "https://files.pythonhosted.org/packages/5f/da/b10ea65b850b54f44a6479177c6987f456bc2d38f8dc73009b78afcf0ede/multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba", size = 50815 }, + { url = "https://files.pythonhosted.org/packages/21/db/3403263f158b0bc7b0d4653766d71cb39498973f2042eead27b2e9758782/multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e", size = 30269 }, + { url = "https://files.pythonhosted.org/packages/02/c1/b15ecceb6ffa5081ed2ed450aea58d65b0e0358001f2b426705f9f41f4c2/multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd", size = 30500 }, + { url = "https://files.pythonhosted.org/packages/3f/e1/7fdd0f39565df3af87d6c2903fb66a7d529fbd0a8a066045d7a5b6ad1145/multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3", size = 130751 }, + { url = "https://files.pythonhosted.org/packages/76/bc/9f593f9e38c6c09bbf0344b56ad67dd53c69167937c2edadee9719a5e17d/multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf", size = 138185 }, + { url = "https://files.pythonhosted.org/packages/28/32/d7799a208701d537b92705f46c777ded812a6dc139c18d8ed599908f6b1c/multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29", size = 133585 }, + { url = "https://files.pythonhosted.org/packages/52/ec/be54a3ad110f386d5bd7a9a42a4ff36b3cd723ebe597f41073a73ffa16b8/multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed", size = 128684 }, + { url = "https://files.pythonhosted.org/packages/36/e1/a680eabeb71e25d4733276d917658dfa1cd3a99b1223625dbc247d266c98/multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733", size = 120994 }, + { url = "https://files.pythonhosted.org/packages/ef/08/08f4f44a8a43ea4cee13aa9cdbbf4a639af8db49310a0637ca389c4cf817/multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f", size = 159689 }, + { url = "https://files.pythonhosted.org/packages/aa/a9/46cdb4cb40bbd4b732169413f56b04a6553460b22bd914f9729c9ba63761/multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4", size = 150611 }, + { url = "https://files.pythonhosted.org/packages/e9/32/35668bb3e6ab2f12f4e4f7f4000f72f714882a94f904d4c3633fbd036753/multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1", size = 164444 }, + { url = "https://files.pythonhosted.org/packages/fa/10/f1388a91552af732d8ec48dab928abc209e732767e9e8f92d24c3544353c/multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc", size = 160158 }, + { url = "https://files.pythonhosted.org/packages/14/c3/f602601f1819983e018156e728e57b3f19726cb424b543667faab82f6939/multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e", size = 156072 }, + { url = "https://files.pythonhosted.org/packages/82/a6/0290af8487326108c0d03d14f8a0b8b1001d71e4494df5f96ab0c88c0b88/multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c", size = 25731 }, + { url = "https://files.pythonhosted.org/packages/88/aa/ea217cb18325aa05cb3e3111c19715f1e97c50a4a900cbc20e54648de5f5/multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea", size = 28176 }, + { url = "https://files.pythonhosted.org/packages/90/9c/7fda9c0defa09538c97b1f195394be82a1f53238536f70b32eb5399dfd4e/multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e", size = 49575 }, + { url = "https://files.pythonhosted.org/packages/be/21/d6ca80dd1b9b2c5605ff7475699a8ff5dc6ea958cd71fb2ff234afc13d79/multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b", size = 29638 }, + { url = "https://files.pythonhosted.org/packages/9c/18/9565f32c19d186168731e859692dfbc0e98f66a1dcf9e14d69c02a78b75a/multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5", size = 29874 }, + { url = "https://files.pythonhosted.org/packages/4e/4e/3815190e73e6ef101b5681c174c541bf972a1b064e926e56eea78d06e858/multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450", size = 129914 }, + { url = "https://files.pythonhosted.org/packages/0c/08/bb47f886457e2259aefc10044e45c8a1b62f0c27228557e17775869d0341/multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496", size = 134589 }, + { url = "https://files.pythonhosted.org/packages/d5/2f/952f79b5f0795cf4e34852fc5cf4dfda6166f63c06c798361215b69c131d/multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a", size = 133259 }, + { url = "https://files.pythonhosted.org/packages/24/1f/af976383b0b772dd351210af5b60ff9927e3abb2f4a103e93da19a957da0/multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226", size = 130779 }, + { url = "https://files.pythonhosted.org/packages/fc/b1/b0a7744be00b0f5045c7ed4e4a6b8ee6bde4672b2c620474712299df5979/multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271", size = 120125 }, + { url = "https://files.pythonhosted.org/packages/d0/bf/2a1d667acf11231cdf0b97a6cd9f30e7a5cf847037b5cf6da44884284bd0/multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb", size = 167095 }, + { url = "https://files.pythonhosted.org/packages/5e/e8/ad6ee74b1a2050d3bc78f566dabcc14c8bf89cbe87eecec866c011479815/multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef", size = 155823 }, + { url = "https://files.pythonhosted.org/packages/45/7c/06926bb91752c52abca3edbfefac1ea90d9d1bc00c84d0658c137589b920/multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24", size = 170233 }, + { url = "https://files.pythonhosted.org/packages/3c/29/3dd36cf6b9c5abba8b97bba84eb499a168ba59c3faec8829327b3887d123/multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6", size = 169035 }, + { url = "https://files.pythonhosted.org/packages/60/47/9a0f43470c70bbf6e148311f78ef5a3d4996b0226b6d295bdd50fdcfe387/multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda", size = 166229 }, + { url = "https://files.pythonhosted.org/packages/1d/23/c1b7ae7a0b8a3e08225284ef3ecbcf014b292a3ee821bc4ed2185fd4ce7d/multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5", size = 25840 }, + { url = "https://files.pythonhosted.org/packages/4a/68/66fceb758ad7a88993940dbdf3ac59911ba9dc46d7798bf6c8652f89f853/multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556", size = 27905 }, + { url = "https://files.pythonhosted.org/packages/a2/82/2641816aa81288a2ead7b982e3805ef8bc494619f574b72edf271bc3a8af/multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54", size = 50607 }, + { url = "https://files.pythonhosted.org/packages/6c/13/97f4a2e0e26b7c6e2469de03f1efc255ce2f984a537c301d957762a23eba/multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d", size = 30209 }, + { url = "https://files.pythonhosted.org/packages/7b/0a/c5a12e908f32ec3844772a8a52322594bfc7ea5786ffdfd4efc70eead079/multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7", size = 30343 }, + { url = "https://files.pythonhosted.org/packages/7d/b6/fd8dd4a1ce1d129a1870143133f449b769ae236e9435ed8d74a8d45acb8e/multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93", size = 130659 }, + { url = "https://files.pythonhosted.org/packages/6a/43/d753dbaa498d42e8e292889cc9a9def30b32631573ae86c9911889977049/multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8", size = 136375 }, + { url = "https://files.pythonhosted.org/packages/e8/4e/51130700c255597ac8e15ceac2e492117ffad44c75610381652b7d2e96a1/multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b", size = 131294 }, + { url = "https://files.pythonhosted.org/packages/61/a3/c307d4af64e695d13e8587d3f996a51b134156c0e8e2e26f4135bb2bf517/multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50", size = 129342 }, + { url = "https://files.pythonhosted.org/packages/dc/04/0dcb48358f8217ae6839075287ce5d4be124e68d4ef7696b23e3f0981b51/multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e", size = 121538 }, + { url = "https://files.pythonhosted.org/packages/fd/e2/8b98715478dc4a3cdf0230886680f33f4eacbc2ab2a4c1604b027e9540eb/multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89", size = 160932 }, + { url = "https://files.pythonhosted.org/packages/c7/72/3f696c93d03f19f8fbefe82e8f415dea8c574fa58ffdb4bc04ebafbd4a05/multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386", size = 151332 }, + { url = "https://files.pythonhosted.org/packages/03/a6/b13e10db5357695645748fae401c94674f612e04e2262c99032ddc638864/multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453", size = 165893 }, + { url = "https://files.pythonhosted.org/packages/eb/da/519f691131f42a25555a903cd6d150285b530786a0d10751ff286aa0e326/multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461", size = 161519 }, + { url = "https://files.pythonhosted.org/packages/3a/85/2d0162c949f7ce7876498d854cba8ce3ae45b1e2212e7a80e0d6ef602a19/multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44", size = 156612 }, + { url = "https://files.pythonhosted.org/packages/ce/7b/7f68ee7e21cf8a7e43fbea41508f9cc0698c497ea54525a5a5a99b4574ef/multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241", size = 25640 }, + { url = "https://files.pythonhosted.org/packages/f7/d6/26f82e3f45802a826c8220af7305ca3b06ad8f25ef2fcdbf1899c7bc01d3/multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c", size = 28138 }, + { url = "https://files.pythonhosted.org/packages/c6/7c/c8f4445389c0bbc5ea85d1e737233c257f314d0f836a6644e097a5ef512f/multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929", size = 50828 }, + { url = "https://files.pythonhosted.org/packages/7d/5c/c364a77b37f580cc28da4194b77ed04286c7631933d3e64fdae40f1972e2/multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9", size = 30315 }, + { url = "https://files.pythonhosted.org/packages/1a/25/f4b60a34dde70c475f4dcaeb4796c256db80d2e03198052d0c3cee5d5fbb/multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a", size = 30451 }, + { url = "https://files.pythonhosted.org/packages/d0/10/2ff646c471e84af25fe8111985ffb8ec85a3f6e1ade8643bfcfcc0f4d2b1/multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1", size = 125880 }, + { url = "https://files.pythonhosted.org/packages/c9/ee/a4775297550dfb127641bd335d00d6d896e4ba5cf0216f78654e5ad6ac80/multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e", size = 133606 }, + { url = "https://files.pythonhosted.org/packages/7d/e9/95746d0c7c40bb0f43fc5424b7d7cf783e8638ce67f05fa677fff9ad76bb/multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046", size = 128720 }, + { url = "https://files.pythonhosted.org/packages/39/a9/1f8d42c8103bcb1da6bb719f1bc018594b5acc8eae56b3fec4720ebee225/multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c", size = 123750 }, + { url = "https://files.pythonhosted.org/packages/b5/f8/c8abbe7c425497d8bf997b1fffd9650ca175325ff397fadc9d63ae5dc027/multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40", size = 116213 }, + { url = "https://files.pythonhosted.org/packages/c2/bb/242664de860cd1201f4d207f0bd2011c1a730877e1dbffbe5d6ec4089e2d/multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527", size = 155410 }, + { url = "https://files.pythonhosted.org/packages/f6/5b/35d20c85b8ccd0c9afc47b8dd46e028b6650ad9660a4b6ad191301d220f5/multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9", size = 146668 }, + { url = "https://files.pythonhosted.org/packages/1b/52/6e984685d048f6728807c3fd9b8a6e3e3d51a06a4d6665d6e0102115455d/multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38", size = 160140 }, + { url = "https://files.pythonhosted.org/packages/76/c0/3aa6238557ed1d235be70d9c3f86d63a835c421b76073b8ce06bf32725e8/multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479", size = 156185 }, + { url = "https://files.pythonhosted.org/packages/85/82/02ed81023b5812582bf7c46e8e2868ffd6a29f8c313af1dd76e82e243c39/multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c", size = 151518 }, + { url = "https://files.pythonhosted.org/packages/d8/00/fd6eef9830046c063939cbf119c101898cbb611ea20301ae911b74caca19/multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b", size = 25732 }, + { url = "https://files.pythonhosted.org/packages/58/a3/4d2c1b4d1859c89d9ce48a4ae410ee019485e324e484b0160afdba8cc42b/multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755", size = 28181 }, + { url = "https://files.pythonhosted.org/packages/fa/a2/17e1e23c6be0a916219c5292f509360c345b5fa6beeb50d743203c27532c/multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7", size = 9729 }, +] + +[[package]] +name = "packaging" +version = "24.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "pydantic" +version = "2.8.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/99/d0a5dca411e0a017762258013ba9905cd6e7baa9a3fd1fe8b6529472902e/pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a", size = 739834 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/fa/b7f815b8c9ad021c07f88875b601222ef5e70619391ade4a49234d12d278/pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8", size = 423875 }, +] + +[[package]] +name = "pydantic-core" +version = "2.20.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/12/e3/0d5ad91211dba310f7ded335f4dad871172b9cc9ce204f5a56d76ccd6247/pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4", size = 388371 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/9d/f30f080f745682e762512f3eef1f6e392c7d74a102e6e96de8a013a5db84/pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3", size = 1837257 }, + { url = "https://files.pythonhosted.org/packages/f2/89/77e7aebdd4a235497ac1e07f0a99e9f40e47f6e0f6783fe30500df08fc42/pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6", size = 1776715 }, + { url = "https://files.pythonhosted.org/packages/18/50/5a4e9120b395108c2a0441a425356c0d26a655d7c617288bec1c28b854ac/pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a", size = 1789023 }, + { url = "https://files.pythonhosted.org/packages/c7/e5/f19e13ba86b968d024b56aa53f40b24828652ac026e5addd0ae49eeada02/pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3", size = 1775598 }, + { url = "https://files.pythonhosted.org/packages/c9/c7/f3c29bed28bd022c783baba5bf9946c4f694cb837a687e62f453c81eb5c6/pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1", size = 1977691 }, + { url = "https://files.pythonhosted.org/packages/41/3e/f62c2a05c554fff34570f6788617e9670c83ed7bc07d62a55cccd1bc0be6/pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953", size = 2693214 }, + { url = "https://files.pythonhosted.org/packages/ae/49/8a6fe79d35e2f3bea566d8ea0e4e6f436d4f749d7838c8e8c4c5148ae706/pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98", size = 2061047 }, + { url = "https://files.pythonhosted.org/packages/51/c6/585355c7c8561e11197dbf6333c57dd32f9f62165d48589b57ced2373d97/pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a", size = 1895106 }, + { url = "https://files.pythonhosted.org/packages/ce/23/829f6b87de0775919e82f8addef8b487ace1c77bb4cb754b217f7b1301b6/pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a", size = 1968506 }, + { url = "https://files.pythonhosted.org/packages/ca/2f/f8ca8f0c40b3ee0a4d8730a51851adb14c5eda986ec09f8d754b2fba784e/pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840", size = 2110217 }, + { url = "https://files.pythonhosted.org/packages/bb/a0/1876656c7b17eb69cc683452cce6bb890dd722222a71b3de57ddb512f561/pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250", size = 1709669 }, + { url = "https://files.pythonhosted.org/packages/be/4a/576524eefa9b301c088c4818dc50ff1c51a88fe29efd87ab75748ae15fd7/pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c", size = 1902386 }, + { url = "https://files.pythonhosted.org/packages/61/db/f6a724db226d990a329910727cfac43539ff6969edc217286dd05cda3ef6/pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312", size = 1834507 }, + { url = "https://files.pythonhosted.org/packages/9b/83/6f2bfe75209d557ae1c3550c1252684fc1827b8b12fbed84c3b4439e135d/pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88", size = 1773527 }, + { url = "https://files.pythonhosted.org/packages/93/ef/513ea76d7ca81f2354bb9c8d7839fc1157673e652613f7e1aff17d8ce05d/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc", size = 1787879 }, + { url = "https://files.pythonhosted.org/packages/31/0a/ac294caecf235f0cc651de6232f1642bb793af448d1cfc541b0dc1fd72b8/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43", size = 1774694 }, + { url = "https://files.pythonhosted.org/packages/46/a4/08f12b5512f095963550a7cb49ae010e3f8f3f22b45e508c2cb4d7744fce/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6", size = 1976369 }, + { url = "https://files.pythonhosted.org/packages/15/59/b2495be4410462aedb399071c71884042a2c6443319cbf62d00b4a7ed7a5/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121", size = 2691250 }, + { url = "https://files.pythonhosted.org/packages/3c/ae/fc99ce1ba791c9e9d1dee04ce80eef1dae5b25b27e3fc8e19f4e3f1348bf/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1", size = 2061462 }, + { url = "https://files.pythonhosted.org/packages/44/bb/eb07cbe47cfd638603ce3cb8c220f1a054b821e666509e535f27ba07ca5f/pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b", size = 1893923 }, + { url = "https://files.pythonhosted.org/packages/ce/ef/5a52400553b8faa0e7f11fd7a2ba11e8d2feb50b540f9e7973c49b97eac0/pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27", size = 1966779 }, + { url = "https://files.pythonhosted.org/packages/4c/5b/fb37fe341344d9651f5c5f579639cd97d50a457dc53901aa8f7e9f28beb9/pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b", size = 2109044 }, + { url = "https://files.pythonhosted.org/packages/70/1a/6f7278802dbc66716661618807ab0dfa4fc32b09d1235923bbbe8b3a5757/pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a", size = 1708265 }, + { url = "https://files.pythonhosted.org/packages/35/7f/58758c42c61b0bdd585158586fecea295523d49933cb33664ea888162daf/pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2", size = 1901750 }, + { url = "https://files.pythonhosted.org/packages/6f/47/ef0d60ae23c41aced42921728650460dc831a0adf604bfa66b76028cb4d0/pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231", size = 1839225 }, + { url = "https://files.pythonhosted.org/packages/6a/23/430f2878c9cd977a61bb39f71751d9310ec55cee36b3d5bf1752c6341fd0/pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9", size = 1768604 }, + { url = "https://files.pythonhosted.org/packages/9e/2b/ec4e7225dee79e0dc80ccc3c35ab33cc2c4bbb8a1a7ecf060e5e453651ec/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f", size = 1789767 }, + { url = "https://files.pythonhosted.org/packages/64/b0/38b24a1fa6d2f96af3148362e10737ec073768cd44d3ec21dca3be40a519/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52", size = 1772061 }, + { url = "https://files.pythonhosted.org/packages/5e/da/bb73274c42cb60decfa61e9eb0c9029da78b3b9af0a9de0309dbc8ff87b6/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237", size = 1974573 }, + { url = "https://files.pythonhosted.org/packages/c8/65/41693110fb3552556180460daffdb8bbeefb87fc026fd9aa4b849374015c/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe", size = 2625596 }, + { url = "https://files.pythonhosted.org/packages/09/b3/a5a54b47cccd1ab661ed5775235c5e06924753c2d4817737c5667bfa19a8/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e", size = 2099064 }, + { url = "https://files.pythonhosted.org/packages/52/fa/443a7a6ea54beaba45ff3a59f3d3e6e3004b7460bcfb0be77bcf98719d3b/pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24", size = 1900345 }, + { url = "https://files.pythonhosted.org/packages/8e/e6/9aca9ffae60f9cdf0183069de3e271889b628d0fb175913fcb3db5618fb1/pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1", size = 1968252 }, + { url = "https://files.pythonhosted.org/packages/46/5e/6c716810ea20a6419188992973a73c2fb4eb99cd382368d0637ddb6d3c99/pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd", size = 2119191 }, + { url = "https://files.pythonhosted.org/packages/06/fc/6123b00a9240fbb9ae0babad7a005d51103d9a5d39c957a986f5cdd0c271/pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688", size = 1717788 }, + { url = "https://files.pythonhosted.org/packages/d5/36/e61ad5a46607a469e2786f398cd671ebafcd9fb17f09a2359985c7228df5/pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d", size = 1898188 }, + { url = "https://files.pythonhosted.org/packages/49/75/40b0e98b658fdba02a693b3bacb4c875a28bba87796c7b13975976597d8c/pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686", size = 1838688 }, + { url = "https://files.pythonhosted.org/packages/75/02/d8ba2d4a266591a6a623c68b331b96523d4b62ab82a951794e3ed8907390/pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a", size = 1768409 }, + { url = "https://files.pythonhosted.org/packages/91/ae/25ecd9bc4ce4993e99a1a3c9ab111c082630c914260e129572fafed4ecc2/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b", size = 1789317 }, + { url = "https://files.pythonhosted.org/packages/7a/80/72057580681cdbe55699c367963d9c661b569a1d39338b4f6239faf36cdc/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19", size = 1771949 }, + { url = "https://files.pythonhosted.org/packages/a2/be/d9bbabc55b05019013180f141fcaf3b14dbe15ca7da550e95b60c321009a/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac", size = 1974392 }, + { url = "https://files.pythonhosted.org/packages/79/2d/7bcd938c6afb0f40293283f5f09988b61fb0a4f1d180abe7c23a2f665f8e/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703", size = 2625565 }, + { url = "https://files.pythonhosted.org/packages/ac/88/ca758e979457096008a4b16a064509028e3e092a1e85a5ed6c18ced8da88/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c", size = 2098784 }, + { url = "https://files.pythonhosted.org/packages/eb/de/2fad6d63c3c42e472e985acb12ec45b7f56e42e6f4cd6dfbc5e87ee8678c/pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83", size = 1900198 }, + { url = "https://files.pythonhosted.org/packages/fe/50/077c7f35b6488dc369a6d22993af3a37901e198630f38ac43391ca730f5b/pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203", size = 1968005 }, + { url = "https://files.pythonhosted.org/packages/5d/1f/f378631574ead46d636b9a04a80ff878b9365d4b361b1905ef1667d4182a/pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0", size = 2118920 }, + { url = "https://files.pythonhosted.org/packages/7a/ea/e4943f17df7a3031d709481fe4363d4624ae875a6409aec34c28c9e6cf59/pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e", size = 1717397 }, + { url = "https://files.pythonhosted.org/packages/13/63/b95781763e8d84207025071c0cec16d921c0163c7a9033ae4b9a0e020dc7/pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20", size = 1898013 }, + { url = "https://files.pythonhosted.org/packages/ba/45/809b121a22a7bf578325be5a35ea98e2fb69c7d497859a4e28659df8f685/pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91", size = 1837631 }, + { url = "https://files.pythonhosted.org/packages/6f/b2/6d84fa138fd98d6af0d68d90284a0866a16d6e128dd9638f92239342f457/pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b", size = 1721947 }, + { url = "https://files.pythonhosted.org/packages/cd/d6/1e99209168d0ac308e40a9bff1f70778d8feb9f14af60a4af0902d919706/pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a", size = 1788759 }, + { url = "https://files.pythonhosted.org/packages/7e/c2/499c4ad8df5006f72ca8b629d6ad71bebddda9399cae47aa3f17ef808dd2/pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f", size = 1775044 }, + { url = "https://files.pythonhosted.org/packages/2d/91/1082e7bc436e79d30f43ee19182bd33bbf4ed5f6c189304438dbc4c59ec3/pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad", size = 1977746 }, + { url = "https://files.pythonhosted.org/packages/49/0f/bd68db4679cf799f7d979684be322df3bfb929096e569320ab15ba617111/pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c", size = 2695540 }, + { url = "https://files.pythonhosted.org/packages/b3/e1/d394444899ff3bbd694151563c23fbde90ddab588482db5e702ca0715edb/pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598", size = 2061613 }, + { url = "https://files.pythonhosted.org/packages/97/90/86d08ec2259e7b83f3883a1328faeeb95fddd0292d62012ea5e1441e73d4/pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd", size = 1894987 }, + { url = "https://files.pythonhosted.org/packages/02/3f/105cba365f6e94079237a3dd793bc1536dd9a1415556433bd6f10d39f7ea/pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa", size = 1968339 }, + { url = "https://files.pythonhosted.org/packages/c8/f8/f7c6b4539f08711e298a0e67b5a9a744f45213a27d197566ebd6ce716ef9/pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987", size = 2110301 }, + { url = "https://files.pythonhosted.org/packages/f2/40/336cb14f0d506314e33c1f9f844dc581c11a4f362c49f082ab409113d1c9/pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a", size = 1709448 }, + { url = "https://files.pythonhosted.org/packages/34/64/3dab2979df4ae6b3edcb732e589451756c6baa987def03ff9cbfaef59d40/pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434", size = 1906155 }, + { url = "https://files.pythonhosted.org/packages/17/c3/803028de61ce9a1fe1643f77ff845807c76298bf1995fa216c4ae853c6b9/pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c", size = 1838087 }, + { url = "https://files.pythonhosted.org/packages/77/f7/25f1fba7ea1ae052e20b234e4c66d54b129e5b3f4d1e6c0da6534dbf57c3/pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6", size = 1722218 }, + { url = "https://files.pythonhosted.org/packages/57/53/fe2e1ae3795b7a69f81913584174f8ed36446b56df734565260830a3632b/pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2", size = 1788970 }, + { url = "https://files.pythonhosted.org/packages/13/80/d9c698486f8fb64b0945e0844c95eef3bcff920941eda30d556deadadbdf/pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a", size = 1775836 }, + { url = "https://files.pythonhosted.org/packages/0f/0c/ab6df185529c0ce1a6d916f9d159de389cc7de44eaa9362efc76495fb821/pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611", size = 1978099 }, + { url = "https://files.pythonhosted.org/packages/0e/9f/3094afeb286c60ec08088d938b661a561f3d23cd2e88a90a92ab0ecfce4f/pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b", size = 2693403 }, + { url = "https://files.pythonhosted.org/packages/9b/f1/a006955715be98093d092aa025f604c7c00721e83fe04bf467c49f31a685/pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006", size = 2061754 }, + { url = "https://files.pythonhosted.org/packages/32/f6/cd2e7bd0a52e2a72841f60c32e62b269995c34bdb13e4d1e799be834338a/pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1", size = 1895490 }, + { url = "https://files.pythonhosted.org/packages/ac/22/34ce27579901fcca525f8adce7747760407cf284c4f0fec6d4542265b451/pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09", size = 1968678 }, + { url = "https://files.pythonhosted.org/packages/2f/3a/80df9b0b5ea5e5b8939285c600dc9ce4a185317f5fb065a37e77a20cbdb3/pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab", size = 2110873 }, + { url = "https://files.pythonhosted.org/packages/ec/26/998c9b8dadcdeafbc833964ef5975cd0c7516b0157575b26300d078ae239/pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2", size = 1709309 }, + { url = "https://files.pythonhosted.org/packages/ed/36/67aeb15996618882c5cfe85dbeffefe09e2806cd86bdd37bca40753e82a1/pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669", size = 1905736 }, + { url = "https://files.pythonhosted.org/packages/73/73/0c7265903f66cce39ed7ca939684fba344210cefc91ccc999cfd5b113fd3/pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906", size = 1828190 }, + { url = "https://files.pythonhosted.org/packages/27/55/60b8b0e58b49ee3ed36a18562dd7c6bc06a551c390e387af5872a238f2ec/pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94", size = 1715252 }, + { url = "https://files.pythonhosted.org/packages/28/3d/d66314bad6bb777a36559195a007b31e916bd9e2c198f7bb8f4ccdceb4fa/pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f", size = 1782641 }, + { url = "https://files.pythonhosted.org/packages/9e/f5/f178f4354d0d6c1431a8f9ede71f3c4269ac4dc55d314fdb7555814276dc/pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482", size = 1928788 }, + { url = "https://files.pythonhosted.org/packages/9c/51/1f5e27bb194df79e30b593b608c66e881ed481241e2b9ed5bdf86d165480/pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6", size = 1886116 }, + { url = "https://files.pythonhosted.org/packages/ac/76/450d9258c58dc7c70b9e3aadf6bebe23ddd99e459c365e2adbde80e238da/pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc", size = 1960125 }, + { url = "https://files.pythonhosted.org/packages/dd/9e/0309a7a4bea51771729515e413b3987be0789837de99087f7415e0db1f9b/pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99", size = 2100407 }, + { url = "https://files.pythonhosted.org/packages/af/93/06d44e08277b3b818b75bd5f25e879d7693e4b7dd3505fde89916fcc9ca2/pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6", size = 1914966 }, + { url = "https://files.pythonhosted.org/packages/ff/d0/639b12bc7c81ebcbbd5f946327e8970089b23fa5b11d7abb56495cbdc0de/pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331", size = 1829108 }, + { url = "https://files.pythonhosted.org/packages/f1/80/3b9d7fb8b4f8d36e24373334740c0b88d9ded08342543a72e9247b4fa410/pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad", size = 1716448 }, + { url = "https://files.pythonhosted.org/packages/2f/c6/f80ea0fac8c241c066245fe918cdc9d105985a1a8726aced9478548c9e37/pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1", size = 1783620 }, + { url = "https://files.pythonhosted.org/packages/d5/3e/9af260156f79347ed3e64149836d69bfe1e0c5efadec6116a879fc31c9ec/pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86", size = 1929929 }, + { url = "https://files.pythonhosted.org/packages/d1/fe/8c3e928e10a97eb8e85b18a53ed3288d039cf0fd7b0fe8d3258f14e8500a/pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e", size = 1886708 }, + { url = "https://files.pythonhosted.org/packages/31/26/b670bd58f1de902c099ff623fe62b9820448a20d70437e7698a57b922d3a/pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0", size = 1960709 }, + { url = "https://files.pythonhosted.org/packages/de/ee/322cad098a0cffc81e985ac2a298d3f29a1da25efe7dc1fb5cd2615c5b04/pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a", size = 2101218 }, + { url = "https://files.pythonhosted.org/packages/07/8b/30233f741e16b35499fa2fad2f4a69eb127eec6c850a1b14af26e7b08b73/pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7", size = 1915399 }, +] + +[[package]] +name = "pytest" +version = "8.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b4/8c/9862305bdcd6020bc7b45b1b5e7397a6caf1a33d3025b9a003b39075ffb2/pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce", size = 1439314 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/f9/cf155cf32ca7d6fa3601bc4c5dd19086af4b320b706919d48a4c79081cf9/pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5", size = 341802 }, +] + +[[package]] +name = "pytest-asyncio" +version = "0.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/6d/c6cf50ce320cf8611df7a1254d86233b3df7cc07f9b5f5cbcb82e08aa534/pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276", size = 49855 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/31/6607dab48616902f76885dfcf62c08d929796fc3b2d2318faf9fd54dbed9/pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b", size = 18024 }, +] + +[[package]] +name = "pytouchlinesl" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "aiohttp" }, + { name = "pydantic" }, +] + +[package.optional-dependencies] +dev = [ + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "ruff" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiohttp" }, + { name = "pydantic", specifier = ">=2.8.2" }, + { name = "pytest", marker = "extra == 'dev'" }, + { name = "pytest-asyncio", marker = "extra == 'dev'" }, + { name = "ruff", marker = "extra == 'dev'" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "pytest", specifier = ">=8.3.2" }, + { name = "pytest-asyncio", specifier = ">=0.24.0" }, + { name = "ruff", specifier = ">=0.6.2" }, +] + +[[package]] +name = "ruff" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/23/f4/279d044f66b79261fd37df76bf72b64471afab5d3b7906a01499c4451910/ruff-0.6.2.tar.gz", hash = "sha256:239ee6beb9e91feb8e0ec384204a763f36cb53fb895a1a364618c6abb076b3be", size = 2460281 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/4b/47dd7a69287afb4069fa42c198e899463605460a58120196711bfcf0446b/ruff-0.6.2-py3-none-linux_armv6l.whl", hash = "sha256:5c8cbc6252deb3ea840ad6a20b0f8583caab0c5ef4f9cca21adc5a92b8f79f3c", size = 9695871 }, + { url = "https://files.pythonhosted.org/packages/ae/c3/8aac62ac4638c14a740ee76a755a925f2d0d04580ab790a9887accb729f6/ruff-0.6.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:17002fe241e76544448a8e1e6118abecbe8cd10cf68fde635dad480dba594570", size = 9459354 }, + { url = "https://files.pythonhosted.org/packages/2f/cf/77fbd8d4617b9b9c503f9bffb8552c4e3ea1a58dc36975e7a9104ffb0f85/ruff-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3dbeac76ed13456f8158b8f4fe087bf87882e645c8e8b606dd17b0b66c2c1158", size = 9163871 }, + { url = "https://files.pythonhosted.org/packages/05/1c/765192bab32b79efbb498b06f0b9dcb3629112b53b8777ae1d19b8209e09/ruff-0.6.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:094600ee88cda325988d3f54e3588c46de5c18dae09d683ace278b11f9d4d534", size = 10096250 }, + { url = "https://files.pythonhosted.org/packages/08/d0/86f3cb0f6934c99f759c232984a5204d67a26745cad2d9edff6248adf7d2/ruff-0.6.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:316d418fe258c036ba05fbf7dfc1f7d3d4096db63431546163b472285668132b", size = 9475376 }, + { url = "https://files.pythonhosted.org/packages/cd/cc/4c8d0e225b559a3fae6092ec310d7150d3b02b4669e9223f783ef64d82c0/ruff-0.6.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d72b8b3abf8a2d51b7b9944a41307d2f442558ccb3859bbd87e6ae9be1694a5d", size = 10295634 }, + { url = "https://files.pythonhosted.org/packages/db/96/d2699cfb1bb5a01c68122af43454c76c31331e1c8a9bd97d653d7c82524b/ruff-0.6.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2aed7e243be68487aa8982e91c6e260982d00da3f38955873aecd5a9204b1d66", size = 11024941 }, + { url = "https://files.pythonhosted.org/packages/8b/a9/6ecd66af8929e0f2a1ed308a4137f3521789f28f0eb97d32c2ca3aa7000c/ruff-0.6.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d371f7fc9cec83497fe7cf5eaf5b76e22a8efce463de5f775a1826197feb9df8", size = 10606894 }, + { url = "https://files.pythonhosted.org/packages/e4/73/2ee4cd19f44992fedac1cc6db9e3d825966072f6dcbd4032f21cbd063170/ruff-0.6.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f310d63af08f583363dfb844ba8f9417b558199c58a5999215082036d795a1", size = 11552886 }, + { url = "https://files.pythonhosted.org/packages/60/4c/c0f1cd35ce4a93c54a6bb1ee6934a3a205fa02198dd076678193853ceea1/ruff-0.6.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7db6880c53c56addb8638fe444818183385ec85eeada1d48fc5abe045301b2f1", size = 10264945 }, + { url = "https://files.pythonhosted.org/packages/c4/89/e45c9359b9cdd4245512ea2b9f2bb128a997feaa5f726fc9e8c7a66afadf/ruff-0.6.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1175d39faadd9a50718f478d23bfc1d4da5743f1ab56af81a2b6caf0a2394f23", size = 10100007 }, + { url = "https://files.pythonhosted.org/packages/06/74/0bd4e0a7ed5f6908df87892f9bf60a2356c0fd74102d8097298bd9b4f346/ruff-0.6.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939f9c86d51635fe486585389f54582f0d65b8238e08c327c1534844b3bb9a", size = 9559267 }, + { url = "https://files.pythonhosted.org/packages/54/03/3dc6dc9419f276f05805bf888c279e3e0b631284abd548d9e87cebb93aec/ruff-0.6.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d0d62ca91219f906caf9b187dea50d17353f15ec9bb15aae4a606cd697b49b4c", size = 9905304 }, + { url = "https://files.pythonhosted.org/packages/5c/5b/d6a72a6a6bbf097c09de468326ef5fa1c9e7aa5e6e45979bc0d984b0dbe7/ruff-0.6.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7438a7288f9d67ed3c8ce4d059e67f7ed65e9fe3aa2ab6f5b4b3610e57e3cb56", size = 10341480 }, + { url = "https://files.pythonhosted.org/packages/79/a9/0f2f21fe15ba537c46598f96aa9ae4a3d4b9ec64926664617ca6a8c772f4/ruff-0.6.2-py3-none-win32.whl", hash = "sha256:279d5f7d86696df5f9549b56b9b6a7f6c72961b619022b5b7999b15db392a4da", size = 7961901 }, + { url = "https://files.pythonhosted.org/packages/b0/80/fff12ffe11853d9f4ea3e5221e6dd2e93640a161c05c9579833e09ad40a7/ruff-0.6.2-py3-none-win_amd64.whl", hash = "sha256:d9f3469c7dd43cd22eb1c3fc16926fb8258d50cb1b216658a07be95dd117b0f2", size = 8783320 }, + { url = "https://files.pythonhosted.org/packages/56/91/577cdd64cce5e74d3f8b5ecb93f29566def569c741eb008aed4f331ef821/ruff-0.6.2-py3-none-win_arm64.whl", hash = "sha256:f28fcd2cd0e02bdf739297516d5643a945cc7caf09bd9bcb4d932540a5ea4fa9", size = 8225886 }, +] + +[[package]] +name = "tomli" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", size = 15164 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "yarl" +version = "1.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/ad/bedcdccbcbf91363fd425a948994f3340924145c2bc8ccb296f4a1e52c28/yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf", size = 141869 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/27/cda5a927df3a894eddfee4efacdd230c2d8486e322fc672194fd651f82c5/yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e", size = 129061 }, + { url = "https://files.pythonhosted.org/packages/d5/fc/40b85bea1f5686092ea37f472c94c023d6347266852ffd55baa01c40f596/yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2", size = 81246 }, + { url = "https://files.pythonhosted.org/packages/81/c6/06938036ea48fa74521713499fba1459b0eb60af9b9afbe8e0e9e1a96c36/yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66", size = 79176 }, + { url = "https://files.pythonhosted.org/packages/30/b5/215d586d5cb17ca9748d7a2d597c07147f210c0c0785257492094d083b65/yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234", size = 297669 }, + { url = "https://files.pythonhosted.org/packages/dd/90/2958ae9f2e12084d616eef95b6a48c8e6d96448add04367c20dc53a33ff2/yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392", size = 311909 }, + { url = "https://files.pythonhosted.org/packages/0b/58/dd3c69651381a57ac991dba54b20ae2da359eb4b03a661e71c451d6525c6/yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551", size = 308690 }, + { url = "https://files.pythonhosted.org/packages/c3/a0/0ade1409d184cbc9e85acd403a386a7c0563b92ff0f26d138ff9e86e48b4/yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455", size = 301580 }, + { url = "https://files.pythonhosted.org/packages/6d/a1/db0bdf8cc48515e9c02daf04ae2916fc27ce6498eca21432fc9ffa63f71b/yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c", size = 291231 }, + { url = "https://files.pythonhosted.org/packages/b2/4f/796b0c73e9ff30a1047a7ee3390e157ab8424d4401b9f32a2624013a5b39/yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53", size = 301079 }, + { url = "https://files.pythonhosted.org/packages/0b/a3/7774786ec6e2dca0bb38b286f12a11af97957546e5fbcce71752a8d2cf07/yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385", size = 295202 }, + { url = "https://files.pythonhosted.org/packages/70/a9/ef6d69ce9a4e82080290bcb6db735bb8a6d6db92f2bbb92b6951bde97e7c/yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863", size = 311784 }, + { url = "https://files.pythonhosted.org/packages/44/ae/fdbc9965ef69e650c3b5b04d60badef90ff0cde21a30770f0700e148b12f/yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b", size = 311134 }, + { url = "https://files.pythonhosted.org/packages/cc/2a/abbaf1460becba856e163f2a1274f5d34b1969d476da8e68a8fc2aeb5661/yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541", size = 304584 }, + { url = "https://files.pythonhosted.org/packages/a3/73/dd7ced8d9731bd2ef4fdff5da23ce2f772ca04e8ddee886a6b15248d9e65/yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d", size = 70175 }, + { url = "https://files.pythonhosted.org/packages/31/d4/2085272a5ccf87af74d4e02787c242c5d60367840a4637b2835565264302/yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b", size = 76402 }, + { url = "https://files.pythonhosted.org/packages/12/65/4c7f3676209a569405c9f0f492df2bc3a387c253f5d906e36944fdd12277/yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099", size = 132836 }, + { url = "https://files.pythonhosted.org/packages/3b/c5/81e3dbf5271ab1510860d2ae7a704ef43f93f7cb9326bf7ebb1949a7260b/yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c", size = 83215 }, + { url = "https://files.pythonhosted.org/packages/20/3d/7dabf580dfc0b588e48830486b488858122b10a61f33325e0d7cf1d6180b/yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0", size = 81237 }, + { url = "https://files.pythonhosted.org/packages/38/45/7c669999f5d350f4f8f74369b94e0f6705918eee18e38610bfe44af93d4f/yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525", size = 324181 }, + { url = "https://files.pythonhosted.org/packages/50/49/aa04effe2876cced8867bf9d89b620acf02b733c62adfe22a8218c35d70b/yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8", size = 339412 }, + { url = "https://files.pythonhosted.org/packages/7d/95/4310771fb9c71599d8466f43347ac18fafd501621e65b93f4f4f16899b1d/yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9", size = 337973 }, + { url = "https://files.pythonhosted.org/packages/9f/ea/94ad7d8299df89844e666e4aa8a0e9b88e02416cd6a7dd97969e9eae5212/yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42", size = 328126 }, + { url = "https://files.pythonhosted.org/packages/6d/be/9d4885e2725f5860833547c9e4934b6e0f44a355b24ffc37957264761e3e/yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe", size = 316677 }, + { url = "https://files.pythonhosted.org/packages/4a/70/5c744d67cad3d093e233cb02f37f2830cb89abfcbb7ad5b5af00ff21d14d/yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce", size = 324243 }, + { url = "https://files.pythonhosted.org/packages/c2/80/8b38d8fed958ac37afb8b81a54bf4f767b107e2c2004dab165edb58fc51b/yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9", size = 318099 }, + { url = "https://files.pythonhosted.org/packages/59/50/715bbc7bda65291f9295e757f67854206f4d8be9746d39187724919ac14d/yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572", size = 334924 }, + { url = "https://files.pythonhosted.org/packages/a8/af/ca9962488027576d7162878a1864cbb1275d298af986ce96bdfd4807d7b2/yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958", size = 335060 }, + { url = "https://files.pythonhosted.org/packages/28/c7/249a3a903d500ca7369eb542e2847a14f12f249638dcc10371db50cd17ff/yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98", size = 326689 }, + { url = "https://files.pythonhosted.org/packages/ec/0c/f02dd0b875a7a460f95dc7cf18983ed43c693283d6ab92e0ad71b9e0de8f/yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31", size = 70407 }, + { url = "https://files.pythonhosted.org/packages/27/41/945ae9a80590e4fb0be166863c6e63d75e4b35789fa3a61ff1dbdcdc220f/yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1", size = 76719 }, + { url = "https://files.pythonhosted.org/packages/7b/cd/a921122610dedfed94e494af18e85aae23e93274c00ca464cfc591c8f4fb/yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81", size = 129561 }, + { url = "https://files.pythonhosted.org/packages/7c/a0/887c93020c788f249c24eaab288c46e5fed4d2846080eaf28ed3afc36e8d/yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142", size = 81595 }, + { url = "https://files.pythonhosted.org/packages/54/99/ed3c92c38f421ba6e36caf6aa91c34118771d252dce800118fa2f44d7962/yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074", size = 79400 }, + { url = "https://files.pythonhosted.org/packages/ea/45/65801be625ef939acc8b714cf86d4a198c0646e80dc8970359d080c47204/yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129", size = 317397 }, + { url = "https://files.pythonhosted.org/packages/06/91/9696601a8ba674c8f0c15035cc9e94ca31f541330364adcfd5a399f598bf/yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2", size = 327246 }, + { url = "https://files.pythonhosted.org/packages/da/3e/bf25177b3618889bf067aacf01ef54e910cd569d14e2f84f5e7bec23bb82/yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78", size = 327321 }, + { url = "https://files.pythonhosted.org/packages/28/1c/bdb3411467b805737dd2720b85fd082e49f59bf0cc12dc1dfcc80ab3d274/yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4", size = 322424 }, + { url = "https://files.pythonhosted.org/packages/41/e9/53bc89f039df2824a524a2aa03ee0bfb8f0585b08949e7521f5eab607085/yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0", size = 310868 }, + { url = "https://files.pythonhosted.org/packages/79/cd/a78c3b0304a4a970b5ae3993f4f5f649443bc8bfa5622f244aed44c810ed/yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51", size = 323452 }, + { url = "https://files.pythonhosted.org/packages/2e/5e/1c78eb05ae0efae08498fd7ab939435a29f12c7f161732e7fe327e5b8ca1/yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff", size = 313554 }, + { url = "https://files.pythonhosted.org/packages/04/e0/0029563a8434472697aebb269fdd2ffc8a19e3840add1d5fa169ec7c56e3/yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7", size = 331029 }, + { url = "https://files.pythonhosted.org/packages/de/1b/7e6b1ad42ccc0ed059066a7ae2b6fd4bce67795d109a99ccce52e9824e96/yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc", size = 333839 }, + { url = "https://files.pythonhosted.org/packages/85/8a/c364d6e2eeb4e128a5ee9a346fc3a09aa76739c0c4e2a7305989b54f174b/yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10", size = 328251 }, + { url = "https://files.pythonhosted.org/packages/ec/9d/0da94b33b9fb89041e10f95a14a55b0fef36c60b6a1d5ff85a0c2ecb1a97/yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7", size = 70195 }, + { url = "https://files.pythonhosted.org/packages/c5/f4/2fdc5a11503bc61818243653d836061c9ce0370e2dd9ac5917258a007675/yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984", size = 76397 }, + { url = "https://files.pythonhosted.org/packages/8a/0a/5e432118ae570f5dbe9e40f8c8ffc41e1947f39f3643dcd0846e8bb9908d/yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be", size = 134240 }, + { url = "https://files.pythonhosted.org/packages/46/ea/8404aa172ffe74da750efeb09aa293bf0247fa6ab4f593aea93f85f74844/yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f", size = 83722 }, + { url = "https://files.pythonhosted.org/packages/fc/ca/33754d12ecbe4ccb677353f4e1c7ce3ea748cc5ab9f435535ebf3bf7ac8c/yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf", size = 82028 }, + { url = "https://files.pythonhosted.org/packages/16/07/719c440f9009c0e7293181f5adb72ba102e4b64312069d03a2bf9b68e97f/yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1", size = 305512 }, + { url = "https://files.pythonhosted.org/packages/79/51/fe155dda79b8564dee3e377a548d7e9fe84dcebc0460d9d1a92f0932d980/yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57", size = 318139 }, + { url = "https://files.pythonhosted.org/packages/af/39/1794787f94b4c75bfc94a4b3e751507ef91d75819411adc8e60520895be3/yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa", size = 314986 }, + { url = "https://files.pythonhosted.org/packages/58/c0/8d9a1c02f217f900f248e0ee31377849f4041aff3d36b71b1f30b4a0fa33/yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130", size = 308756 }, + { url = "https://files.pythonhosted.org/packages/0f/aa/e610398c48bc4a9d250d548cf065560e0192de4191e8568d28568c551963/yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559", size = 298226 }, + { url = "https://files.pythonhosted.org/packages/7a/2e/fd7d98be29db31457930c7490047637991f65a3ad137ac60a020fd7546b2/yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23", size = 314961 }, + { url = "https://files.pythonhosted.org/packages/61/24/bbb3964f849adebe825dc00fff2905289b8bb203cf0f588ece27221cb8f5/yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec", size = 309636 }, + { url = "https://files.pythonhosted.org/packages/75/3c/6d5b2fe70f58528e7e38115da13778623c9a258dfc97f978f9d844948e92/yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78", size = 325489 }, + { url = "https://files.pythonhosted.org/packages/f6/ed/8dc99df4caae1650f82dce04628678f6f987150dd3baab0a62dc697101cc/yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be", size = 324841 }, + { url = "https://files.pythonhosted.org/packages/d2/fa/e401c492c2ebfab5958359b9837e71c20e5c0c6e2d77c5bad1a61d5310fd/yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3", size = 317004 }, + { url = "https://files.pythonhosted.org/packages/8e/d9/807f1ace5f3dfc74f2dc35cdb93838b8d89bd84ae91c9591b72ae5c4f7ee/yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece", size = 70790 }, + { url = "https://files.pythonhosted.org/packages/16/3b/ce8756872540331d841aa8966056a90ee93c88d793f33e29af19f3ff2f3c/yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b", size = 77055 }, + { url = "https://files.pythonhosted.org/packages/34/e7/9d51111429691ffdfb6ce526b2dd2b66fc9d2746df053ecb4062a3969f65/yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27", size = 134018 }, + { url = "https://files.pythonhosted.org/packages/8f/0f/9fa6f044b04267d22ec29df23936ffd4bf4572ccecd889c6b2b1761c2c5c/yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1", size = 83661 }, + { url = "https://files.pythonhosted.org/packages/f9/b0/c213007560d001c9908649ff4b1dd11d1ff388235e773828e19d4637f502/yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91", size = 81842 }, + { url = "https://files.pythonhosted.org/packages/c6/d6/5b30ae1d8a13104ee2ceb649f28f2db5ad42afbd5697fd0fc61528bb112c/yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b", size = 300908 }, + { url = "https://files.pythonhosted.org/packages/d0/50/af41ddf09ff0a6a3f952288ff4ed703a1a6ecc0fbb3a6a9fe50bd33dc7cc/yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5", size = 315681 }, + { url = "https://files.pythonhosted.org/packages/ec/17/376715c245a28f81f01e72e832d84404cffd29576fcc645ee40e3c03f5f4/yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34", size = 312857 }, + { url = "https://files.pythonhosted.org/packages/69/ea/d7e961ea9b1b818a43b155ee512117be6ab9ab67c1e94967b2e64126e8e4/yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136", size = 304255 }, + { url = "https://files.pythonhosted.org/packages/46/8c/02d0c2eed8c6b41de0f8f26aeefc7a285779cbb370cc7bf043285de18a75/yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7", size = 295014 }, + { url = "https://files.pythonhosted.org/packages/c2/5c/093c1fd1d8e95b1de4feb282fa3d9c3172c6d8cb5be2cfa19ca0170f9287/yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e", size = 303653 }, + { url = "https://files.pythonhosted.org/packages/96/f1/c2af401567c7b32f908195c8c1a807670f20ea62e10866b71e1d9e3860a1/yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4", size = 298147 }, + { url = "https://files.pythonhosted.org/packages/65/ac/b5a3cc5bf4675db5d27ec92453e562f3ee4bfef5133ed071880ac07125e9/yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec", size = 314358 }, + { url = "https://files.pythonhosted.org/packages/3d/4d/b8a950fd92a3aa5c95039731d2eda32a701ac0c789663827e67e398c166a/yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c", size = 313842 }, + { url = "https://files.pythonhosted.org/packages/57/68/bfac2e16a15af85234cbd2a5c82abb33caa98e981abbfd6e0f9458b6d1a9/yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0", size = 306771 }, + { url = "https://files.pythonhosted.org/packages/ab/5f/156e00c7bdc6d84efc7615fe0e14b2febf8ea360a8e1307fb3ba9cc14b73/yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575", size = 70773 }, + { url = "https://files.pythonhosted.org/packages/a4/e0/5b4376d7361fe09a46dbb206131e8d85b1cb845da03c212a620d5b6b98d8/yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15", size = 76935 }, + { url = "https://files.pythonhosted.org/packages/4d/05/4d79198ae568a92159de0f89e710a8d19e3fa267b719a236582eee921f4a/yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad", size = 31638 }, +]