diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 4a6d5e6..edf1183 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -24,7 +24,7 @@ jobs: key: pip_cache - name: Install depencency run: | - pip install gql gql[aiohttp] gql[websockets] graphql-core asyncio-atexit pytest pytest-cov + pip install gql==3.5.2 gql[aiohttp] gql[websockets] graphql-core asyncio-atexit pytest pytest-cov - name: Pytest run: | python -m pytest --cov=tibber tests/ --cov-report xml:coverage.xml diff --git a/.github/workflows/code-formatting.yml b/.github/workflows/code-formatting.yml index 00f3ba0..8ac7cb0 100644 --- a/.github/workflows/code-formatting.yml +++ b/.github/workflows/code-formatting.yml @@ -23,13 +23,10 @@ jobs: path: ~/.cache/pip key: pip_cache - name: Install formatting tools - run: pip install black isort flake8 + run: pip install black flake8 - name: Running black run: black --check tibber - - name: Running isort - run: isort --check tibber - - name: Running flake8 run: flake8 --per-file-ignores="./tibber/__init__.py:E402" diff --git a/.github/workflows/pytests.yml b/.github/workflows/pytests.yml index 25ebd5b..455ae33 100644 --- a/.github/workflows/pytests.yml +++ b/.github/workflows/pytests.yml @@ -26,7 +26,7 @@ jobs: key: pip_cache - name: Install depencency run: | - pip install gql gql[aiohttp] gql[websockets] graphql-core asyncio-atexit pytest pytest-timeout + pip install gql==3.5.2 gql[aiohttp] gql[websockets] graphql-core asyncio-atexit pytest pytest-timeout - name: Pytest run: | python -m pytest tests diff --git a/setup.py b/setup.py index ed26643..1a5e14c 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ long_description_content_type='text/markdown', packages=["tibber", "tibber.networking", "tibber.exceptions", "tibber.types"], install_requires=[ - "gql>=3.4.0", + "gql==3.5.3", "gql[aiohttp]>=3.4.0", "gql[websockets]>=3.4.0", "graphql-core>=3.2.3", diff --git a/tests/cached/test_account.py b/tests/cached/test_account.py index cd909ec..818f515 100644 --- a/tests/cached/test_account.py +++ b/tests/cached/test_account.py @@ -24,7 +24,7 @@ def test_getting_user_id(account): assert account.user_id == "dcc2355e-6f55-45c2-beb9-274241fe450c" def test_getting_account_type(account): - assert account.account_type == ["tibber", "customer"] + assert account.account_type == ["customer"] def test_getting_homes(account): assert len(account.homes) == 1 diff --git a/tibber/__init__.py b/tibber/__init__.py index b7beff1..1b64b3a 100644 --- a/tibber/__init__.py +++ b/tibber/__init__.py @@ -1,5 +1,5 @@ __version__ = "0.6.0" -DEMO_TOKEN = "5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE" +DEMO_TOKEN = "3A77EECF61BD445F47241A5A36202185C35AF3AF58609E19B53F3A8872AD7BE1-1" API_ENDPOINT = "https://api.tibber.com/v1-beta/gql" import asyncio diff --git a/tibber/networking/query_builder.py b/tibber/networking/query_builder.py index eb50c9f..bb6004d 100644 --- a/tibber/networking/query_builder.py +++ b/tibber/networking/query_builder.py @@ -283,6 +283,30 @@ def price_rating_entry(cls) -> dict: def single_home(home_id: str) -> dict: return {f"home({home_id})": QueryBuilder.home()} + @classmethod + def price_info_range_query( + cls, resolution: str, first: int, last: int, before: str, after: str + ): + first_arg = f"first: {first}" if first else None + last_arg = f"last: {last}" if last else None + before_arg = f'before: "{before}"' if before else None + after_arg = f'after: "{after}"' if after else None + + args = ", ".join( + [ + arg + for arg in [first_arg, last_arg, before_arg, after_arg] + if arg is not None + ] + ) + return { + f"priceInfoRange(resolution: {resolution}, {args})": { + "pageInfo": QueryBuilder.subscription_price_connection_page_info(), + "edges": QueryBuilder.subscription_price_edge(), + "nodes": QueryBuilder.price(), + } + } + @classmethod def range_query( cls, resolution: str, first: int, last: int, before: str, after: str diff --git a/tibber/types/subscription.py b/tibber/types/subscription.py index b997501..f70bbf0 100644 --- a/tibber/types/subscription.py +++ b/tibber/types/subscription.py @@ -1,11 +1,13 @@ from __future__ import annotations """A class representing the Subscription type from the GraphQL Tibber API.""" -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional +from tibber.networking.query_builder import QueryBuilder from tibber.types.legal_entity import LegalEntity from tibber.types.price_info import PriceInfo from tibber.types.price_rating import PriceRating +from tibber.types.subscription_price_connection import SubscriptionPriceConnection # Import type checking modules if TYPE_CHECKING: @@ -48,6 +50,42 @@ def price_info(self) -> PriceInfo: """Price information related to the subscription""" return PriceInfo(self.cache.get("priceInfo"), self.tibber_client) + def fetch_price_info_range( + self, + resolution: str, + first: Optional[str] = None, + last: Optional[str] = None, + before: Optional[str] = None, + after: Optional[str] = None, + home_id: Optional[str] = None, + ) -> PriceInfo: + """Fetch PriceInfo for a given range. + + The before and after arguments are Base64 encoded ISO 8601 datetimes.""" + price_info_range_query_dict = QueryBuilder.price_info_range_query( + resolution, first, last, before, after + ) + + price_info_range_query = QueryBuilder.create_query( + "viewer", "homes", "currentSubscription", price_info_range_query_dict + ) + full_data = self.tibber_client.execute_query( + self.tibber_client.token, price_info_range_query + ) + + home = full_data["viewer"]["homes"][0] + if home_id: + home_of_id = [ + home for home in full_data["viewer"]["homes"] if home["id"] == home_id + ][0] + + if home_of_id: + home = home_of_id + + return SubscriptionPriceConnection( + home["currentSubscription"]["priceInfoRange"], self.tibber_client + ) + @property def price_rating(self) -> PriceRating: """Price information related to the subscription"""