From 6bd1760daf6de553a7c0a97cb225b8882a8390c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Tue, 22 Oct 2024 19:50:45 +0200 Subject: [PATCH 1/8] get_historic_data --- tibber/home.py | 48 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/tibber/home.py b/tibber/home.py index 0232b5e..27f1b34 100644 --- a/tibber/home.py +++ b/tibber/home.py @@ -529,21 +529,39 @@ async def get_historic_data( :param production: True to get production data instead of consumption """ cons_or_prod_str = "production" if production else "consumption" - query = HISTORIC_DATA.format( - self.home_id, - cons_or_prod_str, - resolution, - n_data, - "profit" if production else "totalCost cost", - "", - ) - if not (data := await self._tibber_control.execute(query, timeout=30)): - _LOGGER.error("Could not get the data.") - return [] - data = data["viewer"]["home"][cons_or_prod_str] - if data is None: - return [] - return data["nodes"] + cursor = "" + res = [] + if resolution == RESOLUTION_HOURLY: + max_n_data = 24 * 30 + elif resolution == RESOLUTION_DAILY: + max_n_data = 30 + elif resolution == RESOLUTION_WEEKLY: + max_n_data = 52 + elif resolution == RESOLUTION_MONTHLY: + max_n_data = 12 + else: + max_n_data = 1 + for k in range(max(1, n_data // max_n_data)): + _n_data = min(max_n_data, n_data) + query = HISTORIC_DATA.format( + self.home_id, + cons_or_prod_str, + resolution, + _n_data, + "profit" if production else "totalCost cost", + cursor, + ) + if not (data := await self._tibber_control.execute(query)): + _LOGGER.error("Could not get the data.") + continue + data = data["viewer"]["home"][cons_or_prod_str] + if data is None: + continue + res.extend(data["nodes"]) + if len(res) >= n_data: + break + cursor = data["pageInfo"]["startCursor"] + return res async def get_historic_data_date( self, From 027bb20cbca8957609d48df1089598abd894db86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Tue, 22 Oct 2024 20:06:01 +0200 Subject: [PATCH 2/8] Update home.py --- tibber/home.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tibber/home.py b/tibber/home.py index fad4c63..29e34ad 100644 --- a/tibber/home.py +++ b/tibber/home.py @@ -541,7 +541,7 @@ async def get_historic_data( max_n_data = 12 else: max_n_data = 1 - for k in range(max(1, n_data // max_n_data)): + for k in range(n_data // max_n_data + 1): _n_data = min(max_n_data, n_data) query = HISTORIC_DATA.format( self.home_id, From 7146a1470d1251d13f0f2df5563dc913fa9e530c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Tue, 22 Oct 2024 20:10:00 +0200 Subject: [PATCH 3/8] Update home.py --- tibber/home.py | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/tibber/home.py b/tibber/home.py index 29e34ad..2b11bc1 100644 --- a/tibber/home.py +++ b/tibber/home.py @@ -519,6 +519,7 @@ async def get_historic_data( n_data: int, resolution: str = RESOLUTION_HOURLY, production: bool = False, + cursor: str = "", ) -> list[dict[str, Any]]: """Get historic data. @@ -529,7 +530,6 @@ async def get_historic_data( :param production: True to get production data instead of consumption """ cons_or_prod_str = "production" if production else "consumption" - cursor = "" res = [] if resolution == RESOLUTION_HOURLY: max_n_data = 24 * 30 @@ -586,27 +586,13 @@ async def get_historic_data_date( # Calculate the number of days to the end of the month from the given date n_data = (date_from.replace(day=1, month=date_from.month + 1) - date_from).days - cons_or_prod_str = "production" if production else "consumption" - query = HISTORIC_DATA_DATE.format( - self.home_id, - cons_or_prod_str, - resolution, + return await self.get_historic_data( n_data, + resolution, + production, date_from_base64, - "profit production productionUnit" if production else "cost consumption consumptionUnit", ) - if not (data := await self._tibber_control.execute(query, timeout=30)): - _LOGGER.error("Could not get the data.") - return [] - - data = data["viewer"]["home"][cons_or_prod_str] - - if data is None: - return [] - - return data["nodes"] - async def get_historic_price_data( self, resolution: str = RESOLUTION_HOURLY, From 058fed256394eb69d2f373cc7343bbe6ab4a7a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Tue, 22 Oct 2024 20:11:53 +0200 Subject: [PATCH 4/8] Update home.py --- tibber/home.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tibber/home.py b/tibber/home.py index 2b11bc1..4f9476c 100644 --- a/tibber/home.py +++ b/tibber/home.py @@ -543,6 +543,7 @@ async def get_historic_data( max_n_data = 1 for k in range(n_data // max_n_data + 1): _n_data = min(max_n_data, n_data) + _n_data = min(_n_data, n_data - len(res)) query = HISTORIC_DATA.format( self.home_id, cons_or_prod_str, From df49dd4733678c9917761e481995230c194e9f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Tue, 22 Oct 2024 20:15:45 +0200 Subject: [PATCH 5/8] Update home.py --- tibber/home.py | 119 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 92 insertions(+), 27 deletions(-) diff --git a/tibber/home.py b/tibber/home.py index 4f9476c..69e4f14 100644 --- a/tibber/home.py +++ b/tibber/home.py @@ -11,7 +11,13 @@ from gql import gql -from .const import RESOLUTION_HOURLY +from .const import ( + RESOLUTION_ANNUAL, + RESOLUTION_DAILY, + RESOLUTION_HOURLY, + RESOLUTION_MONTHLY, + RESOLUTION_WEEKLY, +) from .gql_queries import ( HISTORIC_DATA, HISTORIC_DATA_DATE, @@ -96,7 +102,8 @@ async def _fetch_data(self, hourly_data: HourlyData) -> None: if ( not hourly_data.data or hourly_data.last_data_timestamp is None - or dt.datetime.fromisoformat(hourly_data.data[0]["from"]) < now - dt.timedelta(hours=n_hours + 24) + or dt.datetime.fromisoformat(hourly_data.data[0]["from"]) + < now - dt.timedelta(hours=n_hours + 24) ): hourly_data.data = [] else: @@ -120,7 +127,9 @@ async def _fetch_data(self, hourly_data: HourlyData) -> None: if not hourly_data.data: hourly_data.data = data else: - hourly_data.data = [entry for entry in hourly_data.data if entry not in data] + hourly_data.data = [ + entry for entry in hourly_data.data if entry not in data + ] hourly_data.data.extend(data) _month_energy = 0 @@ -203,7 +212,9 @@ async def update_info(self) -> None: async def update_info_and_price_info(self) -> None: """Update home info and all price info asynchronously.""" - if data := await self._tibber_control.execute(UPDATE_INFO_PRICE % self._home_id): + if data := await self._tibber_control.execute( + UPDATE_INFO_PRICE % self._home_id + ): self.info = data self._process_price_info(self.info) @@ -245,7 +256,9 @@ def _process_price_info(self, price_info: dict[str, dict[str, Any]]) -> None: self._level_info = {} for key in ["current", "today", "tomorrow"]: try: - price_info_k = price_info["viewer"]["home"]["currentSubscription"]["priceInfo"][key] + price_info_k = price_info["viewer"]["home"]["currentSubscription"][ + "priceInfo" + ][key] except (KeyError, TypeError): _LOGGER.error("Could not find price info for %s.", key) continue @@ -257,9 +270,12 @@ def _process_price_info(self, price_info: dict[str, dict[str, Any]]) -> None: self._level_info[data.get("startsAt")] = data.get("level") if ( not self.last_data_timestamp - or dt.datetime.fromisoformat(data.get("startsAt")) > self.last_data_timestamp + or dt.datetime.fromisoformat(data.get("startsAt")) + > self.last_data_timestamp ): - self.last_data_timestamp = dt.datetime.fromisoformat(data.get("startsAt")) + self.last_data_timestamp = dt.datetime.fromisoformat( + data.get("startsAt") + ) @property def current_price_total(self) -> float | None: @@ -314,7 +330,9 @@ def has_real_time_consumption(self) -> None | bool: def has_production(self) -> bool: """Return true if the home has a production metering point.""" try: - return bool(self.info["viewer"]["home"]["meteringPointData"]["productionEan"]) + return bool( + self.info["viewer"]["home"]["meteringPointData"]["productionEan"] + ) except (KeyError, TypeError): return False @@ -336,7 +354,9 @@ def consumption_unit(self) -> str: def currency(self) -> str: """Return the currency.""" try: - return self.info["viewer"]["home"]["currentSubscription"]["priceInfo"]["current"]["currency"] + return self.info["viewer"]["home"]["currentSubscription"]["priceInfo"][ + "current" + ]["currency"] except (KeyError, TypeError, IndexError): _LOGGER.error("Could not find currency.") return "" @@ -366,7 +386,9 @@ def price_unit(self) -> str: return "" return self.currency + "/" + self.consumption_unit - def current_price_rank(self, price_total: dict[str, float], price_time: dt.datetime | None) -> int | None: + def current_price_rank( + self, price_total: dict[str, float], price_time: dt.datetime | None + ) -> int | None: """Gets the rank (1-24) of how expensive the current price is compared to the other prices today.""" # No price -> no rank if price_time is None: @@ -374,7 +396,9 @@ def current_price_rank(self, price_total: dict[str, float], price_time: dt.datet # Map price_total to a list of tuples (datetime, float) price_items_typed: list[tuple[dt.datetime, float]] = [ ( - dt.datetime.fromisoformat(time).astimezone(self._tibber_control.time_zone), + dt.datetime.fromisoformat(time).astimezone( + self._tibber_control.time_zone + ), price, ) for time, price in price_total.items() @@ -387,21 +411,34 @@ def current_price_rank(self, price_total: dict[str, float], price_time: dt.datet ) # Find the rank of the current price try: - price_rank = next(idx for idx, item in enumerate(prices_today_sorted, start=1) if item[0] == price_time) + price_rank = next( + idx + for idx, item in enumerate(prices_today_sorted, start=1) + if item[0] == price_time + ) except StopIteration: price_rank = None return price_rank - def current_price_data(self) -> tuple[float | None, str | None, dt.datetime | None, int | None]: + def current_price_data( + self, + ) -> tuple[float | None, str | None, dt.datetime | None, int | None]: """Get current price.""" now = dt.datetime.now(self._tibber_control.time_zone) for key, price_total in self.price_total.items(): - price_time = dt.datetime.fromisoformat(key).astimezone(self._tibber_control.time_zone) + price_time = dt.datetime.fromisoformat(key).astimezone( + self._tibber_control.time_zone + ) time_diff = (now - price_time).total_seconds() / MIN_IN_HOUR if 0 <= time_diff < MIN_IN_HOUR: price_rank = self.current_price_rank(self.price_total, price_time) - return round(price_total, 3), self.price_level[key], price_time, price_rank + return ( + round(price_total, 3), + self.price_level[key], + price_time, + price_rank, + ) return None, None, None, None async def rt_subscribe(self, callback: Callable[..., Any]) -> None: @@ -412,13 +449,19 @@ async def rt_subscribe(self, callback: Callable[..., Any]) -> None: def _add_extra_data(data: dict[str, Any]) -> dict[str, Any]: live_data = data["data"]["liveMeasurement"] - _timestamp = dt.datetime.fromisoformat(live_data["timestamp"]).astimezone(self._tibber_control.time_zone) - while self._rt_power and self._rt_power[0][0] < _timestamp - dt.timedelta(minutes=5): + _timestamp = dt.datetime.fromisoformat(live_data["timestamp"]).astimezone( + self._tibber_control.time_zone + ) + while self._rt_power and self._rt_power[0][0] < _timestamp - dt.timedelta( + minutes=5 + ): self._rt_power.pop(0) self._rt_power.append((_timestamp, live_data["power"] / 1000)) if "lastMeterProduction" in live_data: - live_data["lastMeterProduction"] = max(0, live_data["lastMeterProduction"] or 0) + live_data["lastMeterProduction"] = max( + 0, live_data["lastMeterProduction"] or 0 + ) if ( (power_production := live_data.get("powerProduction")) @@ -427,17 +470,26 @@ def _add_extra_data(data: dict[str, Any]) -> dict[str, Any]: ): live_data["power"] = 0 - if live_data.get("power", 0) > 0 and live_data.get("powerProduction") is None: + if ( + live_data.get("power", 0) > 0 + and live_data.get("powerProduction") is None + ): live_data["powerProduction"] = 0 current_hour = live_data["accumulatedConsumptionLastHour"] if current_hour is not None: power = sum(p[1] for p in self._rt_power) / len(self._rt_power) live_data["estimatedHourConsumption"] = round( - current_hour + power * (3600 - (_timestamp.minute * 60 + _timestamp.second)) / 3600, + current_hour + + power + * (3600 - (_timestamp.minute * 60 + _timestamp.second)) + / 3600, 3, ) - if self._hourly_consumption_data.peak_hour and current_hour > self._hourly_consumption_data.peak_hour: + if ( + self._hourly_consumption_data.peak_hour + and current_hour > self._hourly_consumption_data.peak_hour + ): self._hourly_consumption_data.peak_hour = round(current_hour, 2) self._hourly_consumption_data.peak_hour_time = _timestamp return data @@ -471,7 +523,10 @@ async def _start() -> None: self.home_id, data, ) - if self._rt_stopped or not self._tibber_control.realtime.subscription_running: + if ( + self._rt_stopped + or not self._tibber_control.realtime.subscription_running + ): _LOGGER.debug("Stopping rt_subscribe loop") return except Exception: @@ -512,7 +567,9 @@ def rt_subscription_running(self) -> bool: """Is real time subscription running.""" if not self._tibber_control.realtime.subscription_running: return False - return not self._last_rt_data_received < dt.datetime.now(tz=dt.UTC) - dt.timedelta(seconds=60) + return not self._last_rt_data_received < dt.datetime.now( + tz=dt.UTC + ) - dt.timedelta(seconds=60) async def get_historic_data( self, @@ -581,11 +638,15 @@ async def get_historic_data_date( :param production: True to get production data instead of consumption """ - date_from_base64 = base64.b64encode(date_from.strftime("%Y-%m-%d").encode()).decode("utf-8") + date_from_base64 = base64.b64encode( + date_from.strftime("%Y-%m-%d").encode() + ).decode("utf-8") if n_data == 0: # Calculate the number of days to the end of the month from the given date - n_data = (date_from.replace(day=1, month=date_from.month + 1) - date_from).days + n_data = ( + date_from.replace(day=1, month=date_from.month + 1) - date_from + ).days return await self.get_historic_data( n_data, @@ -610,7 +671,9 @@ async def get_historic_price_data( if not (data := await self._tibber_control.execute(query)): _LOGGER.error("Could not get the price data.") return None - return data["viewer"]["home"]["currentSubscription"]["priceRating"][resolution]["entries"] + return data["viewer"]["home"]["currentSubscription"]["priceRating"][resolution][ + "entries" + ] def current_attributes(self) -> dict[str, float]: """Get current attributes.""" @@ -626,7 +689,9 @@ def current_attributes(self) -> dict[str, float]: num = 0.0 now = dt.datetime.now(self._tibber_control.time_zone) for key, _price_total in self.price_total.items(): - price_time = dt.datetime.fromisoformat(key).astimezone(self._tibber_control.time_zone) + price_time = dt.datetime.fromisoformat(key).astimezone( + self._tibber_control.time_zone + ) price_total = round(_price_total, 3) if now.date() == price_time.date(): max_price = max(max_price, price_total) From 7d601a69cf54cfa0019accf6296144609795374a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Tue, 22 Oct 2024 20:16:52 +0200 Subject: [PATCH 6/8] Update home.py --- tibber/home.py | 119 +++++++++++-------------------------------------- 1 file changed, 27 insertions(+), 92 deletions(-) diff --git a/tibber/home.py b/tibber/home.py index 69e4f14..0714f6c 100644 --- a/tibber/home.py +++ b/tibber/home.py @@ -11,13 +11,7 @@ from gql import gql -from .const import ( - RESOLUTION_ANNUAL, - RESOLUTION_DAILY, - RESOLUTION_HOURLY, - RESOLUTION_MONTHLY, - RESOLUTION_WEEKLY, -) +from .const import RESOLUTION_HOURLY, RESOLUTION_MONTHLY, RESOLUTION_WEEKLY, RESOLUTION_DAILY, RESOLUTION_ANNUAL from .gql_queries import ( HISTORIC_DATA, HISTORIC_DATA_DATE, @@ -102,8 +96,7 @@ async def _fetch_data(self, hourly_data: HourlyData) -> None: if ( not hourly_data.data or hourly_data.last_data_timestamp is None - or dt.datetime.fromisoformat(hourly_data.data[0]["from"]) - < now - dt.timedelta(hours=n_hours + 24) + or dt.datetime.fromisoformat(hourly_data.data[0]["from"]) < now - dt.timedelta(hours=n_hours + 24) ): hourly_data.data = [] else: @@ -127,9 +120,7 @@ async def _fetch_data(self, hourly_data: HourlyData) -> None: if not hourly_data.data: hourly_data.data = data else: - hourly_data.data = [ - entry for entry in hourly_data.data if entry not in data - ] + hourly_data.data = [entry for entry in hourly_data.data if entry not in data] hourly_data.data.extend(data) _month_energy = 0 @@ -212,9 +203,7 @@ async def update_info(self) -> None: async def update_info_and_price_info(self) -> None: """Update home info and all price info asynchronously.""" - if data := await self._tibber_control.execute( - UPDATE_INFO_PRICE % self._home_id - ): + if data := await self._tibber_control.execute(UPDATE_INFO_PRICE % self._home_id): self.info = data self._process_price_info(self.info) @@ -256,9 +245,7 @@ def _process_price_info(self, price_info: dict[str, dict[str, Any]]) -> None: self._level_info = {} for key in ["current", "today", "tomorrow"]: try: - price_info_k = price_info["viewer"]["home"]["currentSubscription"][ - "priceInfo" - ][key] + price_info_k = price_info["viewer"]["home"]["currentSubscription"]["priceInfo"][key] except (KeyError, TypeError): _LOGGER.error("Could not find price info for %s.", key) continue @@ -270,12 +257,9 @@ def _process_price_info(self, price_info: dict[str, dict[str, Any]]) -> None: self._level_info[data.get("startsAt")] = data.get("level") if ( not self.last_data_timestamp - or dt.datetime.fromisoformat(data.get("startsAt")) - > self.last_data_timestamp + or dt.datetime.fromisoformat(data.get("startsAt")) > self.last_data_timestamp ): - self.last_data_timestamp = dt.datetime.fromisoformat( - data.get("startsAt") - ) + self.last_data_timestamp = dt.datetime.fromisoformat(data.get("startsAt")) @property def current_price_total(self) -> float | None: @@ -330,9 +314,7 @@ def has_real_time_consumption(self) -> None | bool: def has_production(self) -> bool: """Return true if the home has a production metering point.""" try: - return bool( - self.info["viewer"]["home"]["meteringPointData"]["productionEan"] - ) + return bool(self.info["viewer"]["home"]["meteringPointData"]["productionEan"]) except (KeyError, TypeError): return False @@ -354,9 +336,7 @@ def consumption_unit(self) -> str: def currency(self) -> str: """Return the currency.""" try: - return self.info["viewer"]["home"]["currentSubscription"]["priceInfo"][ - "current" - ]["currency"] + return self.info["viewer"]["home"]["currentSubscription"]["priceInfo"]["current"]["currency"] except (KeyError, TypeError, IndexError): _LOGGER.error("Could not find currency.") return "" @@ -386,9 +366,7 @@ def price_unit(self) -> str: return "" return self.currency + "/" + self.consumption_unit - def current_price_rank( - self, price_total: dict[str, float], price_time: dt.datetime | None - ) -> int | None: + def current_price_rank(self, price_total: dict[str, float], price_time: dt.datetime | None) -> int | None: """Gets the rank (1-24) of how expensive the current price is compared to the other prices today.""" # No price -> no rank if price_time is None: @@ -396,9 +374,7 @@ def current_price_rank( # Map price_total to a list of tuples (datetime, float) price_items_typed: list[tuple[dt.datetime, float]] = [ ( - dt.datetime.fromisoformat(time).astimezone( - self._tibber_control.time_zone - ), + dt.datetime.fromisoformat(time).astimezone(self._tibber_control.time_zone), price, ) for time, price in price_total.items() @@ -411,34 +387,21 @@ def current_price_rank( ) # Find the rank of the current price try: - price_rank = next( - idx - for idx, item in enumerate(prices_today_sorted, start=1) - if item[0] == price_time - ) + price_rank = next(idx for idx, item in enumerate(prices_today_sorted, start=1) if item[0] == price_time) except StopIteration: price_rank = None return price_rank - def current_price_data( - self, - ) -> tuple[float | None, str | None, dt.datetime | None, int | None]: + def current_price_data(self) -> tuple[float | None, str | None, dt.datetime | None, int | None]: """Get current price.""" now = dt.datetime.now(self._tibber_control.time_zone) for key, price_total in self.price_total.items(): - price_time = dt.datetime.fromisoformat(key).astimezone( - self._tibber_control.time_zone - ) + price_time = dt.datetime.fromisoformat(key).astimezone(self._tibber_control.time_zone) time_diff = (now - price_time).total_seconds() / MIN_IN_HOUR if 0 <= time_diff < MIN_IN_HOUR: price_rank = self.current_price_rank(self.price_total, price_time) - return ( - round(price_total, 3), - self.price_level[key], - price_time, - price_rank, - ) + return round(price_total, 3), self.price_level[key], price_time, price_rank return None, None, None, None async def rt_subscribe(self, callback: Callable[..., Any]) -> None: @@ -449,19 +412,13 @@ async def rt_subscribe(self, callback: Callable[..., Any]) -> None: def _add_extra_data(data: dict[str, Any]) -> dict[str, Any]: live_data = data["data"]["liveMeasurement"] - _timestamp = dt.datetime.fromisoformat(live_data["timestamp"]).astimezone( - self._tibber_control.time_zone - ) - while self._rt_power and self._rt_power[0][0] < _timestamp - dt.timedelta( - minutes=5 - ): + _timestamp = dt.datetime.fromisoformat(live_data["timestamp"]).astimezone(self._tibber_control.time_zone) + while self._rt_power and self._rt_power[0][0] < _timestamp - dt.timedelta(minutes=5): self._rt_power.pop(0) self._rt_power.append((_timestamp, live_data["power"] / 1000)) if "lastMeterProduction" in live_data: - live_data["lastMeterProduction"] = max( - 0, live_data["lastMeterProduction"] or 0 - ) + live_data["lastMeterProduction"] = max(0, live_data["lastMeterProduction"] or 0) if ( (power_production := live_data.get("powerProduction")) @@ -470,26 +427,17 @@ def _add_extra_data(data: dict[str, Any]) -> dict[str, Any]: ): live_data["power"] = 0 - if ( - live_data.get("power", 0) > 0 - and live_data.get("powerProduction") is None - ): + if live_data.get("power", 0) > 0 and live_data.get("powerProduction") is None: live_data["powerProduction"] = 0 current_hour = live_data["accumulatedConsumptionLastHour"] if current_hour is not None: power = sum(p[1] for p in self._rt_power) / len(self._rt_power) live_data["estimatedHourConsumption"] = round( - current_hour - + power - * (3600 - (_timestamp.minute * 60 + _timestamp.second)) - / 3600, + current_hour + power * (3600 - (_timestamp.minute * 60 + _timestamp.second)) / 3600, 3, ) - if ( - self._hourly_consumption_data.peak_hour - and current_hour > self._hourly_consumption_data.peak_hour - ): + if self._hourly_consumption_data.peak_hour and current_hour > self._hourly_consumption_data.peak_hour: self._hourly_consumption_data.peak_hour = round(current_hour, 2) self._hourly_consumption_data.peak_hour_time = _timestamp return data @@ -523,10 +471,7 @@ async def _start() -> None: self.home_id, data, ) - if ( - self._rt_stopped - or not self._tibber_control.realtime.subscription_running - ): + if self._rt_stopped or not self._tibber_control.realtime.subscription_running: _LOGGER.debug("Stopping rt_subscribe loop") return except Exception: @@ -567,9 +512,7 @@ def rt_subscription_running(self) -> bool: """Is real time subscription running.""" if not self._tibber_control.realtime.subscription_running: return False - return not self._last_rt_data_received < dt.datetime.now( - tz=dt.UTC - ) - dt.timedelta(seconds=60) + return not self._last_rt_data_received < dt.datetime.now(tz=dt.UTC) - dt.timedelta(seconds=60) async def get_historic_data( self, @@ -638,15 +581,11 @@ async def get_historic_data_date( :param production: True to get production data instead of consumption """ - date_from_base64 = base64.b64encode( - date_from.strftime("%Y-%m-%d").encode() - ).decode("utf-8") + date_from_base64 = base64.b64encode(date_from.strftime("%Y-%m-%d").encode()).decode("utf-8") if n_data == 0: # Calculate the number of days to the end of the month from the given date - n_data = ( - date_from.replace(day=1, month=date_from.month + 1) - date_from - ).days + n_data = (date_from.replace(day=1, month=date_from.month + 1) - date_from).days return await self.get_historic_data( n_data, @@ -671,9 +610,7 @@ async def get_historic_price_data( if not (data := await self._tibber_control.execute(query)): _LOGGER.error("Could not get the price data.") return None - return data["viewer"]["home"]["currentSubscription"]["priceRating"][resolution][ - "entries" - ] + return data["viewer"]["home"]["currentSubscription"]["priceRating"][resolution]["entries"] def current_attributes(self) -> dict[str, float]: """Get current attributes.""" @@ -689,9 +626,7 @@ def current_attributes(self) -> dict[str, float]: num = 0.0 now = dt.datetime.now(self._tibber_control.time_zone) for key, _price_total in self.price_total.items(): - price_time = dt.datetime.fromisoformat(key).astimezone( - self._tibber_control.time_zone - ) + price_time = dt.datetime.fromisoformat(key).astimezone(self._tibber_control.time_zone) price_total = round(_price_total, 3) if now.date() == price_time.date(): max_price = max(max_price, price_total) From fe18fc581380785129500055045086034132ad55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Tue, 22 Oct 2024 20:21:20 +0200 Subject: [PATCH 7/8] Update home.py --- tibber/home.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/tibber/home.py b/tibber/home.py index 0714f6c..15a6063 100644 --- a/tibber/home.py +++ b/tibber/home.py @@ -11,7 +11,7 @@ from gql import gql -from .const import RESOLUTION_HOURLY, RESOLUTION_MONTHLY, RESOLUTION_WEEKLY, RESOLUTION_DAILY, RESOLUTION_ANNUAL +from .const import RESOLUTION_DAILY, RESOLUTION_HOURLY, RESOLUTION_MONTHLY, RESOLUTION_WEEKLY from .gql_queries import ( HISTORIC_DATA, HISTORIC_DATA_DATE, @@ -519,7 +519,6 @@ async def get_historic_data( n_data: int, resolution: str = RESOLUTION_HOURLY, production: bool = False, - cursor: str = "", ) -> list[dict[str, Any]]: """Get historic data. @@ -530,7 +529,7 @@ async def get_historic_data( :param production: True to get production data instead of consumption """ cons_or_prod_str = "production" if production else "consumption" - res = [] + res: list[dict[str, Any]] = [] if resolution == RESOLUTION_HOURLY: max_n_data = 24 * 30 elif resolution == RESOLUTION_DAILY: @@ -541,7 +540,8 @@ async def get_historic_data( max_n_data = 12 else: max_n_data = 1 - for k in range(n_data // max_n_data + 1): + cursor = "" + for _ in range(n_data // max_n_data + 1): _n_data = min(max_n_data, n_data) _n_data = min(_n_data, n_data - len(res)) query = HISTORIC_DATA.format( @@ -587,13 +587,27 @@ async def get_historic_data_date( # Calculate the number of days to the end of the month from the given date n_data = (date_from.replace(day=1, month=date_from.month + 1) - date_from).days - return await self.get_historic_data( - n_data, + cons_or_prod_str = "production" if production else "consumption" + query = HISTORIC_DATA_DATE.format( + self.home_id, + cons_or_prod_str, resolution, - production, + n_data, date_from_base64, + "profit production productionUnit" if production else "cost consumption consumptionUnit", ) + if not (data := await self._tibber_control.execute(query, timeout=30)): + _LOGGER.error("Could not get the data.") + return [] + + data = data["viewer"]["home"][cons_or_prod_str] + + if data is None: + return [] + + return data["nodes"] + async def get_historic_price_data( self, resolution: str = RESOLUTION_HOURLY, From b53d89d132401ab1bd2869d009748a1df29d97bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Tue, 22 Oct 2024 20:23:05 +0200 Subject: [PATCH 8/8] Update home.py --- tibber/home.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tibber/home.py b/tibber/home.py index 15a6063..94f3f8a 100644 --- a/tibber/home.py +++ b/tibber/home.py @@ -542,8 +542,7 @@ async def get_historic_data( max_n_data = 1 cursor = "" for _ in range(n_data // max_n_data + 1): - _n_data = min(max_n_data, n_data) - _n_data = min(_n_data, n_data - len(res)) + _n_data = min(max_n_data, n_data - len(res)) query = HISTORIC_DATA.format( self.home_id, cons_or_prod_str,