Skip to content

Commit

Permalink
Attempt to improve reliability:
Browse files Browse the repository at this point in the history
* Retry API calls that fail

* Only mark entities unavailable if they come from an API call that failed - this way if some of the API calls succeed but others fail, we will still get data from the ones that succeeded

* Update user agent to a current version of Chrome (I doubt this actually matters but it's worth a try)
  • Loading branch information
jaydeethree authored Nov 21, 2024
1 parent b3add69 commit 4cd3616
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 38 deletions.
105 changes: 67 additions & 38 deletions custom_components/weatherdotcom/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,53 +117,80 @@ async def get_weather(self):
"""Get weather data."""
headers = {
'Accept-Encoding': 'gzip',
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
}
try:
async with async_timeout.timeout(10):
url = self._build_url(_RESOURCECURRENT)
response = await self._session.get(url, headers=headers)
result_current = await response.json(content_type=None)
if result_current is None:
raise ValueError('NO CURRENT RESULT')
self._check_errors(url, result_current)

async with async_timeout.timeout(10):
url = self._build_url(_RESOURCEFORECASTDAILY)
response = await self._session.get(url, headers=headers)
result_forecast_daily = await response.json(content_type=None)

if result_forecast_daily is None:
raise ValueError('NO DAILY FORECAST RESULT')
self._check_errors(url, result_forecast_daily)

async with async_timeout.timeout(10):
url = self._build_url(_RESOURCEFORECASTHOURLY)
response = await self._session.get(url, headers=headers)
result_forecast_hourly = await response.json(content_type=None)

if result_forecast_hourly is None:
raise ValueError('NO HOURLY FORECAST RESULT')
self._check_errors(url, result_forecast_hourly)

current_error = False
daily_error = False
hourly_error = False
result_current = None
result_forecast_daily = None
result_forecast_hourly = None

for attempt in range(2):
try:
async with async_timeout.timeout(10):
url = self._build_url(_RESOURCECURRENT)
response = await self._session.get(url, headers=headers)
result_current = await response.json(content_type=None)
if result_current is None:
raise ValueError('No weather data found')
self._check_errors(url, result_current)
break
except (ValueError, asyncio.TimeoutError, aiohttp.ClientError) as err:
if attempt == 1:
_LOGGER.error('Error getting current weather: %s', err)
current_error = True
else:
_LOGGER.error('Error getting current weather, will retry: %s', err)
await asyncio.sleep(5)

for attempt in range(2):
try:
async with async_timeout.timeout(10):
url = self._build_url(_RESOURCEFORECASTDAILY)
response = await self._session.get(url, headers=headers)
result_forecast_daily = await response.json(content_type=None)
if result_forecast_daily is None:
raise ValueError('No weather data found')
self._check_errors(url, result_forecast_daily)
break
except (ValueError, asyncio.TimeoutError, aiohttp.ClientError) as err:
if attempt == 1:
_LOGGER.error('Error getting daily weather: %s', err)
daily_error = True
else:
_LOGGER.error('Error getting daily weather, will retry: %s', err)
await asyncio.sleep(5)

for attempt in range(2):
try:
async with async_timeout.timeout(10):
url = self._build_url(_RESOURCEFORECASTHOURLY)
response = await self._session.get(url, headers=headers)
result_forecast_hourly = await response.json(content_type=None)
if result_forecast_hourly is None:
raise ValueError('No weather data found')
self._check_errors(url, result_forecast_hourly)
break
except (ValueError, asyncio.TimeoutError, aiohttp.ClientError) as err:
if attempt == 1:
_LOGGER.error('Error getting hourly weather: %s', err)
hourly_error = True
else:
_LOGGER.error('Error getting hourly weather, will retry: %s', err)
await asyncio.sleep(5)

if current_error and daily_error and hourly_error:
raise UpdateFailed('Failed to get weather data')
else:
result = {
RESULTS_CURRENT: result_current,
RESULTS_FORECAST_DAILY: result_forecast_daily,
RESULTS_FORECAST_HOURLY: result_forecast_hourly,
}

self.data = result

return result

except ValueError as err:
_LOGGER.error("Check Weather.com API %s", err.args)
raise UpdateFailed(err)
except (asyncio.TimeoutError, aiohttp.ClientError) as err:
_LOGGER.error("Error fetching Weather.com data: %s", repr(err))
raise UpdateFailed(err)
# _LOGGER.debug(f'Weather.com data {self.data}')

def _build_url(self, baseurl):
baseurl += '&language={language}'
baseurl += _RESOURCESHARED
Expand All @@ -190,6 +217,8 @@ def _check_errors(self, url: str, response: dict):
)

def get_current(self, field):
if self.data[RESULTS_CURRENT] == None:
return None
return self.data[RESULTS_CURRENT][field]

def get_forecast_daily(self, field, period=0):
Expand Down
2 changes: 2 additions & 0 deletions custom_components/weatherdotcom/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ def _get_sensor_data(
) -> Any:
"""Get sensor data."""
# windGust is often null. When it is, set it to windSpeed instead.
if sensors[RESULTS_CURRENT] == None:
return None
if kind == FIELD_WINDGUST and sensors[RESULTS_CURRENT][kind] == None:
return sensors[RESULTS_CURRENT][FIELD_WINDSPEED]
else:
Expand Down

0 comments on commit 4cd3616

Please sign in to comment.