Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve evohome exception handling and fix bugs #22140

Merged
merged 46 commits into from
Apr 2, 2019
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
1aaf1ae
Use latest client library, evohomeclient v0.3.1
zxdavb Mar 17, 2019
85b5706
Fix issue #22097: Failed to call service climate/turn_on...
zxdavb Mar 17, 2019
5009599
BUGFIX: handle case where a Zone doesn't have a temperature
zxdavb Mar 17, 2019
8bafd58
BUGFIX: missing exception handler, and inappropriate delint hints
zxdavb Mar 17, 2019
a188235
Improve exception handling, and also better messages
zxdavb Mar 17, 2019
89926d9
improve code (REDACT secrets); remove TODOs
zxdavb Mar 17, 2019
068018a
minor refactor - improve error message
zxdavb Mar 17, 2019
d99e627
more refactoring - improve error message
zxdavb Mar 17, 2019
41af9b2
remove TODOs
zxdavb Mar 17, 2019
21dbbd5
update to latest evohomeclient library
zxdavb Mar 17, 2019
c4a8b87
Use latest client library, evohomeclient v0.3.1
zxdavb Mar 17, 2019
62a7fd2
Fix issue #22097: Failed to call service climate/turn_on...
zxdavb Mar 17, 2019
18d2294
BUGFIX: handle case where a Zone doesn't have a temperature
zxdavb Mar 17, 2019
d65261e
BUGFIX: missing exception handler, and inappropriate delint hints
zxdavb Mar 17, 2019
b611ba0
Improve exception handling, and also better messages
zxdavb Mar 17, 2019
cc814f1
improve code (REDACT secrets); remove TODOs
zxdavb Mar 17, 2019
3971592
minor refactor - improve error message
zxdavb Mar 17, 2019
857550c
more refactoring - improve error message
zxdavb Mar 17, 2019
482bb35
remove TODOs
zxdavb Mar 17, 2019
b7cf07a
update to latest evohomeclient library
zxdavb Mar 17, 2019
976b880
fix requests for houndci-bot
zxdavb Mar 17, 2019
dcb23ed
Merge branch 'dev' of https://github.com/zxdavb/home-assistant into dev
zxdavb Mar 17, 2019
1012db9
Tidy up requests exception handling
zxdavb Mar 17, 2019
aa10ba3
Correct lint error
zxdavb Mar 18, 2019
8185df9
merge with latest upstream changes
zxdavb Mar 27, 2019
02b5b66
update to latest client library
zxdavb Mar 27, 2019
e95e526
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
zxdavb Mar 27, 2019
7424b28
minor de-lint
zxdavb Mar 27, 2019
99717af
more cleanup of exceptions, messages
zxdavb Mar 27, 2019
89b6300
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
zxdavb Mar 27, 2019
3bf4af0
refactored for new exception
zxdavb Mar 28, 2019
12035f5
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
zxdavb Mar 28, 2019
60b6746
fix error in requirements*_all.txt
zxdavb Mar 28, 2019
2448106
de-lint
zxdavb Mar 29, 2019
815e02a
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
zxdavb Mar 29, 2019
0d85a82
delint unused import
zxdavb Mar 29, 2019
9dfe773
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
zxdavb Mar 29, 2019
70a295d
import 3rd-party library only inside methods
zxdavb Mar 30, 2019
02d93d3
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
zxdavb Mar 30, 2019
0702a83
change honeywell tests
zxdavb Mar 30, 2019
d771266
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
zxdavb Mar 30, 2019
09ae5ce
delint, fix typo
zxdavb Mar 30, 2019
0652c7d
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
zxdavb Mar 30, 2019
174a99b
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
zxdavb Apr 2, 2019
3abc08e
we dont log usernames, passwords, etc.
zxdavb Apr 2, 2019
8203314
de-lint
zxdavb Apr 2, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion homeassistant/components/climate/honeywell.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
CONF_PASSWORD, CONF_USERNAME, TEMP_CELSIUS, TEMP_FAHRENHEIT,
ATTR_TEMPERATURE, CONF_REGION)

REQUIREMENTS = ['evohomeclient==0.2.8', 'somecomfort==0.5.2']
REQUIREMENTS = ['evohomeclient==0.3.1', 'somecomfort==0.5.2']

_LOGGER = logging.getLogger(__name__)

Expand Down
59 changes: 40 additions & 19 deletions homeassistant/components/evohome/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from datetime import timedelta
import logging

from requests.exceptions import HTTPError
import requests.exceptions
import voluptuous as vol

from homeassistant.const import (
Expand All @@ -21,7 +21,7 @@
from homeassistant.helpers.discovery import load_platform
from homeassistant.helpers.dispatcher import async_dispatcher_send

REQUIREMENTS = ['evohomeclient==0.2.8']
REQUIREMENTS = ['evohomeclient==0.3.1']

_LOGGER = logging.getLogger(__name__)

Expand All @@ -43,6 +43,10 @@
}),
}, extra=vol.ALLOW_EXTRA)

CONF_SECRETS = [
CONF_USERNAME, CONF_PASSWORD,
]

# These are used to help prevent E501 (line too long) violations.
GWS = 'gateways'
TCS = 'temperatureControlSystems'
Expand All @@ -69,48 +73,56 @@ def setup(hass, hass_config):
from evohomeclient2 import EvohomeClient

try:
client = EvohomeClient(
client = evo_data['client'] = EvohomeClient(
evo_data['params'][CONF_USERNAME],
evo_data['params'][CONF_PASSWORD],
debug=False
)

except HTTPError as err:
except requests.exceptions.ConnectionError:
_LOGGER.error(
"setup(): Failed to connect with the vendor's web servers. "
"The server is not contactable (maybe the vendor's fault?). "
"Unable to continue. Resolve any errors and restart HA."
)
raise # Expose the actual exception to the user

except requests.exceptions.HTTPError as err:
if err.response.status_code == HTTP_BAD_REQUEST:
_LOGGER.error(
"setup(): Failed to connect with the vendor's web servers. "
"Check your username (%s), and password are correct."
"Check your username (%s), and password are correct. "
"Unable to continue. Resolve any errors and restart HA.",
evo_data['params'][CONF_USERNAME]
)

elif err.response.status_code == HTTP_SERVICE_UNAVAILABLE:
_LOGGER.error(
"setup(): Failed to connect with the vendor's web servers. "
"The server is not contactable. Unable to continue. "
"Resolve any errors and restart HA."
"The vendor says the server is currently unavailable. "
"Unable to continue. Wait a while and restart HA."
)

elif err.response.status_code == HTTP_TOO_MANY_REQUESTS:
_LOGGER.error(
"setup(): Failed to connect with the vendor's web servers. "
"You have exceeded the api rate limit. Unable to continue. "
"Wait a while (say 10 minutes) and restart HA."
"The vendor's API rate limit has been exceeded. "
"Unable to continue. Wait a while and restart HA."
)

else:
raise # We don't expect/handle any other HTTPErrors
raise # we don't expect/handle any other HTTPErrors

return False

finally: # Redact username, password as no longer needed
evo_data['params'][CONF_USERNAME] = 'REDACTED'
evo_data['params'][CONF_PASSWORD] = 'REDACTED'
finally: # Redact any config data that's no longer needed
for parameter in CONF_SECRETS:
evo_data['params'][parameter] = 'REDACTED' \
if evo_data['params'][parameter] else None

evo_data['client'] = client
evo_data['status'] = {}

# Redact any installation data we'll never need
# Redact any installation data that's no longer needed
for loc in client.installation_info:
loc['locationInfo']['locationId'] = 'REDACTED'
loc['locationInfo']['locationOwner'] = 'REDACTED'
Expand All @@ -120,25 +132,34 @@ def setup(hass, hass_config):

# Pull down the installation configuration
loc_idx = evo_data['params'][CONF_LOCATION_IDX]

try:
evo_data['config'] = client.installation_info[loc_idx]

except IndexError:
_LOGGER.warning(
"setup(): Parameter '%s'=%s, is outside its range (0-%s)",
CONF_LOCATION_IDX, loc_idx, len(client.installation_info) - 1)
_LOGGER.error(
"setup(): Invalid configuration (unknown location). "
"Parameter '%s'=%s, is outside its valid range (0-%s). "
"Unable to continue. Fix any configuration errors and restart HA.",
CONF_LOCATION_IDX, loc_idx, len(client.installation_info) - 1
)
return False

if _LOGGER.isEnabledFor(logging.DEBUG):
tmp_loc = dict(evo_data['config'])
tmp_loc['locationInfo']['postcode'] = 'REDACTED'

if 'dhw' in tmp_loc[GWS][0][TCS][0]: # if this location has DHW...
tmp_loc[GWS][0][TCS][0]['dhw'] = '...'

_LOGGER.debug("setup(): evo_data['config']=%s", tmp_loc)

load_platform(hass, 'climate', DOMAIN, {}, hass_config)

if 'dhw' in evo_data['config'][GWS][0][TCS][0]:
_LOGGER.warning(
"setup(): DHW found, but this component doesn't support DHW."
)

@callback
def _first_update(event):
"""When HA has started, the hub knows to retrieve it's first update."""
Expand Down
69 changes: 44 additions & 25 deletions homeassistant/components/evohome/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@
)
from homeassistant.components.evohome import (
DATA_EVOHOME, DISPATCHER_EVOHOME,
CONF_LOCATION_IDX, SCAN_INTERVAL_DEFAULT,
CONF_LOCATION_IDX,
EVO_PARENT, EVO_CHILD,
GWS, TCS,
)
from homeassistant.const import (
CONF_SCAN_INTERVAL,
HTTP_TOO_MANY_REQUESTS,
HTTP_SERVICE_UNAVAILABLE, HTTP_TOO_MANY_REQUESTS,
PRECISION_HALVES,
STATE_OFF,
TEMP_CELSIUS
TEMP_CELSIUS,
)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import (
Expand Down Expand Up @@ -140,23 +140,41 @@ def _connect(self, packet):
if packet['to'] & self._type and packet['signal'] == 'refresh':
self.async_schedule_update_ha_state(force_refresh=True)

def _handle_requests_exceptions(self, err):
if err.response.status_code == HTTP_TOO_MANY_REQUESTS:
# execute a backoff: pause, and also reduce rate
old_interval = self._params[CONF_SCAN_INTERVAL]
new_interval = min(old_interval, SCAN_INTERVAL_DEFAULT) * 2
self._params[CONF_SCAN_INTERVAL] = new_interval
def _handle_exception(self, err):
try:
raise err

except requests.exceptions.ConnectionError:
zxdavb marked this conversation as resolved.
Show resolved Hide resolved
# this appears to be common with Honeywell's servers
_LOGGER.warning(
"API rate limit has been exceeded. Suspending polling for %s "
"seconds, and increasing '%s' from %s to %s seconds",
new_interval * 3, CONF_SCAN_INTERVAL, old_interval,
new_interval)

self._timers['statusUpdated'] = datetime.now() + new_interval * 3

else:
raise err # we dont handle any other HTTPErrors
"The vendor's web servers appear to be uncontactable, so "
"unable to get the latest state data during this cycle. "
"Hint: This is often a problem with the vendor's network."
)
return True

except requests.exceptions.HTTPError:
zxdavb marked this conversation as resolved.
Show resolved Hide resolved
if err.response.status_code == HTTP_TOO_MANY_REQUESTS:
_LOGGER.warning(
"The vendor's API rate limit has been exceeded, so unable"
"to get the latest state data during this polling cycle. "
"Suspending polling, and will resume after %s seconds.",
(self._params[CONF_SCAN_INTERVAL] * 3).total_seconds()
)

self._timers['statusUpdated'] = datetime.now() + \
self._params[CONF_SCAN_INTERVAL] * 3

if err.response.status_code == HTTP_SERVICE_UNAVAILABLE:
zxdavb marked this conversation as resolved.
Show resolved Hide resolved
# this appears to be common with Honeywell servers
_LOGGER.warning(
"The vendor's web servers appear unavailable, so unable "
"to get the latest state data during this polling cycle. "
"Note: This is a problem with the vendor's network."
)
return True

raise # we don't expect/handle any other HTTPErrors

@property
def name(self) -> str:
Expand Down Expand Up @@ -251,7 +269,8 @@ def target_temperature(self):
@property
def current_temperature(self):
"""Return the current temperature of the evohome Zone."""
return self._status['temperatureStatus']['temperature']
return (self._status['temperatureStatus']['temperature']
if self._status['temperatureStatus']['isAvailable'] else None)

@property
def current_operation(self):
Expand Down Expand Up @@ -298,7 +317,7 @@ def _set_temperature(self, temperature, until=None):
try:
self._obj.set_temperature(temperature, until)
except HTTPError as err:
self._handle_exception("HTTPError", str(err)) # noqa: E501; pylint: disable=no-member
self._handle_exception(err)

def set_temperature(self, **kwargs):
"""Set new target temperature, indefinitely."""
Expand Down Expand Up @@ -346,9 +365,9 @@ def set_operation_mode(self, operation_mode):
def _set_operation_mode(self, operation_mode):
if operation_mode == EVO_FOLLOW:
try:
self._obj.cancel_temp_override(self._obj)
self._obj.cancel_temp_override()
except HTTPError as err:
self._handle_exception("HTTPError", str(err)) # noqa: E501; pylint: disable=no-member
self._handle_exception(err)

elif operation_mode == EVO_TEMPOVER:
_LOGGER.error(
Expand Down Expand Up @@ -508,9 +527,9 @@ def turn_away_mode_off(self):

def _set_operation_mode(self, operation_mode):
try:
self._obj._set_status(operation_mode) # noqa: E501; pylint: disable=protected-access
self._obj._set_status(operation_mode) # noqa: E501; pylint: disable=protected-access
except HTTPError as err:
self._handle_requests_exceptions(err)
self._handle_exception(err)

def set_operation_mode(self, operation_mode):
"""Set new target operation mode for the TCS.
Expand Down Expand Up @@ -547,7 +566,7 @@ def update(self):
self._status.update(
self._client.locations[loc_idx].status()[GWS][0][TCS][0])
except HTTPError as err: # check if we've exceeded the api rate limit
self._handle_requests_exceptions(err)
self._handle_exception(err)
else:
self._timers['statusUpdated'] = datetime.now()
self._available = True
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ eternalegypt==0.0.5

# homeassistant.components.evohome
# homeassistant.components.climate.honeywell
evohomeclient==0.2.8
evohomeclient==0.3.1

# homeassistant.components.dlib_face_detect.image_processing
# homeassistant.components.dlib_face_identify.image_processing
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ ephem==3.7.6.0

# homeassistant.components.evohome
# homeassistant.components.climate.honeywell
evohomeclient==0.2.8
evohomeclient==0.3.1

# homeassistant.components.feedreader
feedparser-homeassistant==5.2.2.dev1
Expand Down