From 1181fd6d5397bd24f7d3b5161c0366d5da877938 Mon Sep 17 00:00:00 2001 From: Aaron Godfrey Date: Mon, 16 Nov 2020 21:00:15 -0800 Subject: [PATCH] Fixed #7 Also added some additional logging to try to track some errors seen in the logs. Bumped minimum HA version to 0.108 to get rid of some code. --- custom_components/steam_wishlist/entities.py | 15 +++- .../steam_wishlist/sensor_manager.py | 69 ++++++++++--------- hacs.json | 2 +- 3 files changed, 52 insertions(+), 34 deletions(-) diff --git a/custom_components/steam_wishlist/entities.py b/custom_components/steam_wishlist/entities.py index 79e53d0..5f2e8f7 100644 --- a/custom_components/steam_wishlist/entities.py +++ b/custom_components/steam_wishlist/entities.py @@ -92,7 +92,9 @@ class SteamGameEntity(BinarySensorEntity): entity_id = None def __init__( - self, manager, game: SteamGame, + self, + manager, + game: SteamGame, ): super().__init__() self.game = game @@ -108,7 +110,16 @@ def unique_id(self) -> str: @property def is_on(self): """Return True if the binary sensor is on.""" - pricing = self.coordinator.data[self.game["steam_id"]] + try: + pricing = self.coordinator.data[self.game["steam_id"]] + except KeyError: + _LOGGER.warning( + "%s not found in self.coordinator.data keys (%s), assuming False. Data was %s", + self.game, + list(self.coordinator.data.keys()), + self.coordinator.data, + ) + return False try: pricing: dict = self.coordinator.data[self.game["steam_id"]]["subs"][0] discount_pct = pricing["discount_pct"] diff --git a/custom_components/steam_wishlist/sensor_manager.py b/custom_components/steam_wishlist/sensor_manager.py index 3e7a8be..cfd6a62 100644 --- a/custom_components/steam_wishlist/sensor_manager.py +++ b/custom_components/steam_wishlist/sensor_manager.py @@ -1,5 +1,5 @@ import logging -from typing import Callable, Dict, List, Union +from typing import Any, Callable, Dict, List, Union from homeassistant import core from homeassistant.core import callback @@ -38,36 +38,33 @@ def __init__(self, hass: core.HomeAssistant, url: str): update_interval=SCAN_INTERVAL, ) - async def _async_fetch_data(self): + async def _async_fetch_data(self) -> Dict[str, Dict[str, Any]]: """Fetch the data for the coordinator.""" - async with self.http_session.get(self.url) as resp: - data = await resp.json() - return data - - @callback - def async_add_listener( - self, update_callback: core.CALLBACK_TYPE - ) -> Callable[[], None]: - """Listen for data updates. - - @NOTE: this is copied from an unreleased version of HA (v0.108.0). After that - Release we may be able to use this (and set the minimum version in hacs.json to - 0.108.0) - """ - schedule_refresh = not self._listeners - - self._listeners.append(update_callback) + data: Dict[str, Dict[str, Any]] = {} + # Attempt to look up to 10 pages of data. There does not appear to be a static + # number of results returned, it seems random. There also isn't any indication + # in the response that to let us know there are more pages to fetch. An empty + # array will be returned if we request a page of results when a user doesn't + # have that many. + for page in range(10): + url = f"{self.url}?p={page}" + async with self.http_session.get(url) as resp: + result = await resp.json() + if not isinstance(result, dict): + _LOGGER.warning( + "async_fetch_data data was not a dict its %s. status code: %s", + result, + resp.status, + ) + break + data.update(result) + if len(result) <= 50: + # Even though we don't know the number of results per page, it seems + # to be well over 50, likely between 70-100. So don't bother a 2nd + # request if the result is this small. + break - # This is the first listener, set up interval. - if schedule_refresh: - self._schedule_refresh() - - @callback - def remove_listener() -> None: - """Remove update listener.""" - self.async_remove_listener(update_callback) - - return remove_listener + return data async def async_remove_games( @@ -139,8 +136,18 @@ def async_update_items(self): new_sensors.append(self.current_wishlist[WISHLIST_ID]) new_binary_sensors: List[SteamGameEntity] = [] - # {"success": 2} This indicates an empty wishlist. - if "success" not in self.coordinator.data: + + process_data = True + if not isinstance(self.coordinator.data, dict): + # This seems to happen when we get an unexpected response. This typically + # means an intermittent request failure. Data is then an empty dict. Should + # something different happen here? + _LOGGER.warning( + "Coordinator data unexpectedly not a dict: %s", self.coordinator.data + ) + process_data = False + # {"success": 2} This CAN (but not always) indicate an empty wishlist. + if "success" not in self.coordinator.data and process_data: for game_id, game in self.coordinator.data.items(): existing = self.current_wishlist.get(game_id) if existing is not None: diff --git a/hacs.json b/hacs.json index 18381af..79ab6d6 100644 --- a/hacs.json +++ b/hacs.json @@ -3,7 +3,7 @@ "config_flow": true, "documentation": "https://github.com/boralyl/steam-wishlist", "domains": ["binary_sensor", "sensor"], - "homeassistant": "0.106.0", + "homeassistant": "0.108.0", "iot_class": "Cloud Polling", "name": "Steam Wishlist", "render_readme": true