Improve evohome exception handling and fix bugs (#22140)
* Use latest client library, evohomeclient v0.3.1 * Fix issue #22097: Failed to call service climate/turn_on... * BUGFIX: handle case where a Zone doesn't have a temperature * BUGFIX: missing exception handler, and inappropriate delint hints * Improve exception handling, and also better messages * improve code (REDACT secrets); remove TODOs * minor refactor - improve error message * more refactoring - improve error message * remove TODOs * update to latest evohomeclient library * Use latest client library, evohomeclient v0.3.1 * Fix issue #22097: Failed to call service climate/turn_on... * BUGFIX: handle case where a Zone doesn't have a temperature * BUGFIX: missing exception handler, and inappropriate delint hints * Improve exception handling, and also better messages * improve code (REDACT secrets); remove TODOs * minor refactor - improve error message * more refactoring - improve error message * remove TODOs * update to latest evohomeclient library * fix requests for houndci-bot * Tidy up requests exception handling * Correct lint error * update to latest client library * minor de-lint * more cleanup of exceptions, messages * refactored for new exception * fix error in requirements*_all.txt * de-lint * delint unused import * import 3rd-party library only inside methods * change honeywell tests * delint, fix typo * we dont log usernames, passwords, etc. * de-lint
This commit is contained in:
parent
16e0953f26
commit
3bd37d6a65
6 changed files with 107 additions and 78 deletions
|
@ -2,22 +2,22 @@
|
|||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
|
||||
from requests.exceptions import HTTPError
|
||||
import requests.exceptions
|
||||
|
||||
from homeassistant.components.climate import ClimateDevice
|
||||
from homeassistant.components.climate.const import (
|
||||
STATE_AUTO, STATE_ECO, STATE_MANUAL, SUPPORT_AWAY_MODE, SUPPORT_ON_OFF,
|
||||
SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE)
|
||||
from homeassistant.const import (
|
||||
CONF_SCAN_INTERVAL, HTTP_TOO_MANY_REQUESTS, PRECISION_HALVES, STATE_OFF,
|
||||
TEMP_CELSIUS)
|
||||
CONF_SCAN_INTERVAL, HTTP_SERVICE_UNAVAILABLE, HTTP_TOO_MANY_REQUESTS,
|
||||
PRECISION_HALVES, STATE_OFF, TEMP_CELSIUS)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect, dispatcher_send)
|
||||
|
||||
from . import (
|
||||
CONF_LOCATION_IDX, DATA_EVOHOME, DISPATCHER_EVOHOME, EVO_CHILD, EVO_PARENT,
|
||||
GWS, SCAN_INTERVAL_DEFAULT, TCS)
|
||||
GWS, TCS)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -81,7 +81,7 @@ async def async_setup_platform(hass, hass_config, async_add_entities,
|
|||
|
||||
# evohomeclient has exposed no means of accessing non-default location
|
||||
# (i.e. loc_idx > 0) other than using a protected member, such as below
|
||||
tcs_obj_ref = client.locations[loc_idx]._gateways[0]._control_systems[0] # noqa E501; pylint: disable=protected-access
|
||||
tcs_obj_ref = client.locations[loc_idx]._gateways[0]._control_systems[0] # noqa: E501; pylint: disable=protected-access
|
||||
|
||||
_LOGGER.debug(
|
||||
"Found Controller, id=%s [%s], name=%s (location_idx=%s)",
|
||||
|
@ -128,23 +128,43 @@ class EvoClimateDevice(ClimateDevice):
|
|||
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:
|
||||
import evohomeclient2
|
||||
raise err
|
||||
|
||||
except evohomeclient2.AuthenticationError:
|
||||
_LOGGER.error(
|
||||
"Failed to (re)authenticate with the vendor's server. "
|
||||
"This may be a temporary error. Message is: %s",
|
||||
err
|
||||
)
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
# 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)
|
||||
"Unable to connect with the vendor's server. "
|
||||
"Check your network and the vendor's status page."
|
||||
)
|
||||
|
||||
self._timers['statusUpdated'] = datetime.now() + new_interval * 3
|
||||
except requests.exceptions.HTTPError:
|
||||
if err.response.status_code == HTTP_SERVICE_UNAVAILABLE:
|
||||
_LOGGER.warning(
|
||||
"Vendor says their server is currently unavailable. "
|
||||
"This may be temporary; check the vendor's status page."
|
||||
)
|
||||
|
||||
else:
|
||||
raise err # we dont handle any other HTTPErrors
|
||||
elif err.response.status_code == HTTP_TOO_MANY_REQUESTS:
|
||||
_LOGGER.warning(
|
||||
"The vendor's API rate limit has been exceeded. "
|
||||
"So will cease 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
|
||||
|
||||
else:
|
||||
raise # we don't expect/handle any other HTTPErrors
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
|
@ -239,7 +259,8 @@ class EvoZone(EvoClimateDevice):
|
|||
@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):
|
||||
|
@ -284,9 +305,11 @@ class EvoZone(EvoClimateDevice):
|
|||
- None for PermanentOverride (i.e. indefinitely)
|
||||
"""
|
||||
try:
|
||||
import evohomeclient2
|
||||
self._obj.set_temperature(temperature, until)
|
||||
except HTTPError as err:
|
||||
self._handle_exception("HTTPError", str(err)) # noqa: E501; pylint: disable=no-member
|
||||
except (requests.exceptions.RequestException,
|
||||
evohomeclient2.AuthenticationError) as err:
|
||||
self._handle_exception(err)
|
||||
|
||||
def set_temperature(self, **kwargs):
|
||||
"""Set new target temperature, indefinitely."""
|
||||
|
@ -334,9 +357,11 @@ class EvoZone(EvoClimateDevice):
|
|||
def _set_operation_mode(self, operation_mode):
|
||||
if operation_mode == EVO_FOLLOW:
|
||||
try:
|
||||
self._obj.cancel_temp_override(self._obj)
|
||||
except HTTPError as err:
|
||||
self._handle_exception("HTTPError", str(err)) # noqa: E501; pylint: disable=no-member
|
||||
import evohomeclient2
|
||||
self._obj.cancel_temp_override()
|
||||
except (requests.exceptions.RequestException,
|
||||
evohomeclient2.AuthenticationError) as err:
|
||||
self._handle_exception(err)
|
||||
|
||||
elif operation_mode == EVO_TEMPOVER:
|
||||
_LOGGER.error(
|
||||
|
@ -496,9 +521,11 @@ class EvoController(EvoClimateDevice):
|
|||
|
||||
def _set_operation_mode(self, operation_mode):
|
||||
try:
|
||||
import evohomeclient2
|
||||
self._obj._set_status(operation_mode) # noqa: E501; pylint: disable=protected-access
|
||||
except HTTPError as err:
|
||||
self._handle_requests_exceptions(err)
|
||||
except (requests.exceptions.RequestException,
|
||||
evohomeclient2.AuthenticationError) as err:
|
||||
self._handle_exception(err)
|
||||
|
||||
def set_operation_mode(self, operation_mode):
|
||||
"""Set new target operation mode for the TCS.
|
||||
|
@ -532,10 +559,12 @@ class EvoController(EvoClimateDevice):
|
|||
loc_idx = self._params[CONF_LOCATION_IDX]
|
||||
|
||||
try:
|
||||
import evohomeclient2
|
||||
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)
|
||||
except (requests.exceptions.RequestException,
|
||||
evohomeclient2.AuthenticationError) as err:
|
||||
self._handle_exception(err)
|
||||
else:
|
||||
self._timers['statusUpdated'] = datetime.now()
|
||||
self._available = True
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue