2016-08-19 07:17:28 +00:00
|
|
|
"""
|
|
|
|
Support for Honeywell Round Connected and Honeywell Evohome thermostats.
|
|
|
|
|
|
|
|
For more details about this platform, please refer to the documentation at
|
|
|
|
https://home-assistant.io/components/climate.honeywell/
|
|
|
|
"""
|
|
|
|
import logging
|
|
|
|
import socket
|
2017-03-02 07:49:49 +00:00
|
|
|
import datetime
|
2016-08-19 07:17:28 +00:00
|
|
|
|
2017-03-02 07:49:49 +00:00
|
|
|
import requests
|
2017-04-30 05:04:49 +00:00
|
|
|
import voluptuous as vol
|
2016-09-11 07:21:16 +00:00
|
|
|
|
2017-10-29 11:32:02 +00:00
|
|
|
import homeassistant.helpers.config_validation as cv
|
2017-04-30 05:04:49 +00:00
|
|
|
from homeassistant.components.climate import (
|
|
|
|
ClimateDevice, PLATFORM_SCHEMA, ATTR_FAN_MODE, ATTR_FAN_LIST,
|
2017-11-29 10:01:28 +00:00
|
|
|
ATTR_OPERATION_MODE, ATTR_OPERATION_LIST, SUPPORT_TARGET_TEMPERATURE,
|
|
|
|
SUPPORT_AWAY_MODE, SUPPORT_OPERATION_MODE)
|
2016-08-19 07:17:28 +00:00
|
|
|
from homeassistant.const import (
|
2016-09-09 17:06:53 +00:00
|
|
|
CONF_PASSWORD, CONF_USERNAME, TEMP_CELSIUS, TEMP_FAHRENHEIT,
|
2017-10-29 11:32:02 +00:00
|
|
|
ATTR_TEMPERATURE, CONF_REGION)
|
2016-08-19 07:17:28 +00:00
|
|
|
|
Add zones to evohome component (#18428)
* Added Zones, and removed available() logic
flesh out Zones
tidy up init
some more tidying up
Nearly there - full functionality
passed txo - ready to send PR
Ready to PR, except to remove logging
Add Zones and associated functionality to evohome component
Add Zones to evohome (some more tidying up)
Add Zones to evohome (Nearly there - full functionality)
Add Zones to evohome (passed tox)
Add Zones to evohome (except to remove logging)
Add Zones and associated functionality to evohome component
Revert _LOGGER.warn to .debug, as it should be
Cleanup stupid REBASE
* removed a duplicate/unwanted code block
* tidy up comment
* use async_added_to_hass instead of bus.listen
* Pass evo_data instead of hass when instntiating
* switch to async version of setup_platform/add_entities
* Remove workaround for bug in client library
- using github version for now, as awaiting new PyPi package
* Avoid invalid-name lint - use 'zone_idx' instead of 'z'
* Fix line too long error
* remove commented-out line of code
* fix a logic error, improve REDACTION of potentially-sensitive infomation
* restore use of EVENT_HOMEASSISTANT_START to improve HA startup time
* added a docstring to _flatten_json
* Switch instantiation from component to platform
* Use v0.2.8 of client api (resolves logging bug)
* import rather than duplicate, and de-lint
* We use evohomeclient v0.2.8 now
* remove all the api logging
* Changed scan_interal to Throttle
* added a configurable scan_interval
* small code tidy-up, removed sub-function
* tidy up update() code
* minimize use of self.hass.data[]
* remove lint
* remove unwanted logging
* remove debug code
* correct a small coding error
* small tidyup of code
* remove flatten_json
* add @callback to _first_update()
* switch back to load_platform
* adhere to standards fro logging
* use new format string formatting
* minor change to comments
* convert scan_interval to timedelta from int
* restore rounding up of scan_interval
* code tidy up
* sync when in sync context
* fix typo
* remove raises not needed
* tidy up typos, etc.
* remove invalid-name lint
* tidy up exception handling
* de-lint/pretty-fy
* move 'status' to a JSON node, so theirs room for 'config', 'schedule' in the future
2018-11-27 11:17:22 +00:00
|
|
|
REQUIREMENTS = ['evohomeclient==0.2.8', 'somecomfort==0.5.2']
|
2016-08-19 07:17:28 +00:00
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
2016-09-11 07:21:16 +00:00
|
|
|
ATTR_FAN = 'fan'
|
|
|
|
ATTR_SYSTEM_MODE = 'system_mode'
|
2017-03-02 07:49:49 +00:00
|
|
|
ATTR_CURRENT_OPERATION = 'equipment_output_status'
|
2016-09-11 07:21:16 +00:00
|
|
|
|
|
|
|
CONF_AWAY_TEMPERATURE = 'away_temperature'
|
2017-03-02 07:49:49 +00:00
|
|
|
CONF_COOL_AWAY_TEMPERATURE = 'away_cool_temperature'
|
|
|
|
CONF_HEAT_AWAY_TEMPERATURE = 'away_heat_temperature'
|
2016-09-11 07:21:16 +00:00
|
|
|
|
|
|
|
DEFAULT_AWAY_TEMPERATURE = 16
|
2017-03-02 07:49:49 +00:00
|
|
|
DEFAULT_COOL_AWAY_TEMPERATURE = 30
|
|
|
|
DEFAULT_HEAT_AWAY_TEMPERATURE = 16
|
2016-09-11 07:21:16 +00:00
|
|
|
DEFAULT_REGION = 'eu'
|
|
|
|
REGIONS = ['eu', 'us']
|
|
|
|
|
|
|
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|
|
|
vol.Required(CONF_USERNAME): cv.string,
|
|
|
|
vol.Required(CONF_PASSWORD): cv.string,
|
2017-03-02 07:49:49 +00:00
|
|
|
vol.Optional(CONF_AWAY_TEMPERATURE,
|
|
|
|
default=DEFAULT_AWAY_TEMPERATURE): vol.Coerce(float),
|
|
|
|
vol.Optional(CONF_COOL_AWAY_TEMPERATURE,
|
|
|
|
default=DEFAULT_COOL_AWAY_TEMPERATURE): vol.Coerce(float),
|
|
|
|
vol.Optional(CONF_HEAT_AWAY_TEMPERATURE,
|
|
|
|
default=DEFAULT_HEAT_AWAY_TEMPERATURE): vol.Coerce(float),
|
2016-09-11 07:21:16 +00:00
|
|
|
vol.Optional(CONF_REGION, default=DEFAULT_REGION): vol.In(REGIONS),
|
|
|
|
})
|
|
|
|
|
|
|
|
|
2018-08-24 14:37:30 +00:00
|
|
|
def setup_platform(hass, config, add_entities, discovery_info=None):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Set up the Honeywell thermostat."""
|
2016-09-11 07:21:16 +00:00
|
|
|
username = config.get(CONF_USERNAME)
|
|
|
|
password = config.get(CONF_PASSWORD)
|
|
|
|
region = config.get(CONF_REGION)
|
|
|
|
|
|
|
|
if region == 'us':
|
2018-08-24 14:37:30 +00:00
|
|
|
return _setup_us(username, password, config, add_entities)
|
2017-07-06 06:30:01 +00:00
|
|
|
|
2018-08-24 14:37:30 +00:00
|
|
|
return _setup_round(username, password, config, add_entities)
|
2016-08-19 07:17:28 +00:00
|
|
|
|
|
|
|
|
2018-08-24 14:37:30 +00:00
|
|
|
def _setup_round(username, password, config, add_entities):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Set up the rounding function."""
|
2016-08-19 07:17:28 +00:00
|
|
|
from evohomeclient import EvohomeClient
|
|
|
|
|
2016-09-11 07:21:16 +00:00
|
|
|
away_temp = config.get(CONF_AWAY_TEMPERATURE)
|
2016-08-19 07:17:28 +00:00
|
|
|
evo_api = EvohomeClient(username, password)
|
|
|
|
|
|
|
|
try:
|
|
|
|
zones = evo_api.temperatures(force_refresh=True)
|
|
|
|
for i, zone in enumerate(zones):
|
2018-08-24 14:37:30 +00:00
|
|
|
add_entities(
|
2017-08-01 14:18:14 +00:00
|
|
|
[RoundThermostat(evo_api, zone['id'], i == 0, away_temp)],
|
|
|
|
True
|
2016-09-11 07:21:16 +00:00
|
|
|
)
|
2016-08-19 07:17:28 +00:00
|
|
|
except socket.error:
|
|
|
|
_LOGGER.error(
|
2016-09-11 07:21:16 +00:00
|
|
|
"Connection error logging into the honeywell evohome web service")
|
2016-08-19 07:17:28 +00:00
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
# config will be used later
|
2018-08-24 14:37:30 +00:00
|
|
|
def _setup_us(username, password, config, add_entities):
|
2017-04-30 05:04:49 +00:00
|
|
|
"""Set up the user."""
|
2016-08-19 07:17:28 +00:00
|
|
|
import somecomfort
|
|
|
|
|
|
|
|
try:
|
|
|
|
client = somecomfort.SomeComfort(username, password)
|
|
|
|
except somecomfort.AuthError:
|
2017-04-30 05:04:49 +00:00
|
|
|
_LOGGER.error("Failed to login to honeywell account %s", username)
|
2016-08-19 07:17:28 +00:00
|
|
|
return False
|
|
|
|
except somecomfort.SomeComfortError as ex:
|
2017-04-30 05:04:49 +00:00
|
|
|
_LOGGER.error("Failed to initialize honeywell client: %s", str(ex))
|
2016-08-19 07:17:28 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
dev_id = config.get('thermostat')
|
|
|
|
loc_id = config.get('location')
|
2017-03-02 07:49:49 +00:00
|
|
|
cool_away_temp = config.get(CONF_COOL_AWAY_TEMPERATURE)
|
|
|
|
heat_away_temp = config.get(CONF_HEAT_AWAY_TEMPERATURE)
|
2016-08-19 07:17:28 +00:00
|
|
|
|
2018-08-24 14:37:30 +00:00
|
|
|
add_entities([HoneywellUSThermostat(client, device, cool_away_temp,
|
|
|
|
heat_away_temp, username, password)
|
|
|
|
for location in client.locations_by_id.values()
|
|
|
|
for device in location.devices_by_id.values()
|
|
|
|
if ((not loc_id or location.locationid == loc_id) and
|
|
|
|
(not dev_id or device.deviceid == dev_id))])
|
2016-08-19 07:17:28 +00:00
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
class RoundThermostat(ClimateDevice):
|
|
|
|
"""Representation of a Honeywell Round Connected thermostat."""
|
|
|
|
|
2017-08-01 14:18:14 +00:00
|
|
|
def __init__(self, client, zone_id, master, away_temp):
|
2016-08-19 07:17:28 +00:00
|
|
|
"""Initialize the thermostat."""
|
2017-08-01 14:18:14 +00:00
|
|
|
self.client = client
|
2016-08-19 07:17:28 +00:00
|
|
|
self._current_temperature = None
|
|
|
|
self._target_temperature = None
|
2016-09-11 07:21:16 +00:00
|
|
|
self._name = 'round connected'
|
2016-08-19 07:17:28 +00:00
|
|
|
self._id = zone_id
|
|
|
|
self._master = master
|
|
|
|
self._is_dhw = False
|
|
|
|
self._away_temp = away_temp
|
|
|
|
self._away = False
|
|
|
|
|
2017-11-29 10:01:28 +00:00
|
|
|
@property
|
|
|
|
def supported_features(self):
|
|
|
|
"""Return the list of supported features."""
|
|
|
|
supported = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_AWAY_MODE)
|
|
|
|
if hasattr(self.client, ATTR_SYSTEM_MODE):
|
|
|
|
supported |= SUPPORT_OPERATION_MODE
|
|
|
|
return supported
|
|
|
|
|
2016-08-19 07:17:28 +00:00
|
|
|
@property
|
|
|
|
def name(self):
|
|
|
|
"""Return the name of the honeywell, if any."""
|
|
|
|
return self._name
|
|
|
|
|
|
|
|
@property
|
2016-10-11 07:00:29 +00:00
|
|
|
def temperature_unit(self):
|
2016-08-19 07:17:28 +00:00
|
|
|
"""Return the unit of measurement."""
|
|
|
|
return TEMP_CELSIUS
|
|
|
|
|
|
|
|
@property
|
|
|
|
def current_temperature(self):
|
|
|
|
"""Return the current temperature."""
|
|
|
|
return self._current_temperature
|
|
|
|
|
|
|
|
@property
|
|
|
|
def target_temperature(self):
|
|
|
|
"""Return the temperature we try to reach."""
|
|
|
|
if self._is_dhw:
|
|
|
|
return None
|
|
|
|
return self._target_temperature
|
|
|
|
|
2016-09-09 17:06:53 +00:00
|
|
|
def set_temperature(self, **kwargs):
|
2016-08-19 07:17:28 +00:00
|
|
|
"""Set new target temperature."""
|
2016-09-09 17:06:53 +00:00
|
|
|
temperature = kwargs.get(ATTR_TEMPERATURE)
|
|
|
|
if temperature is None:
|
|
|
|
return
|
2017-08-01 14:18:14 +00:00
|
|
|
self.client.set_temperature(self._name, temperature)
|
2016-08-19 07:17:28 +00:00
|
|
|
|
|
|
|
@property
|
2018-07-30 11:44:31 +00:00
|
|
|
def current_operation(self) -> str:
|
2016-08-19 07:17:28 +00:00
|
|
|
"""Get the current operation of the system."""
|
2017-08-01 14:18:14 +00:00
|
|
|
return getattr(self.client, ATTR_SYSTEM_MODE, None)
|
2016-08-19 07:17:28 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def is_away_mode_on(self):
|
|
|
|
"""Return true if away mode is on."""
|
|
|
|
return self._away
|
|
|
|
|
2018-07-30 11:44:31 +00:00
|
|
|
def set_operation_mode(self, operation_mode: str) -> None:
|
2016-08-19 07:17:28 +00:00
|
|
|
"""Set the HVAC mode for the thermostat."""
|
2017-08-01 14:18:14 +00:00
|
|
|
if hasattr(self.client, ATTR_SYSTEM_MODE):
|
|
|
|
self.client.system_mode = operation_mode
|
2016-08-19 07:17:28 +00:00
|
|
|
|
|
|
|
def turn_away_mode_on(self):
|
|
|
|
"""Turn away on.
|
|
|
|
|
2017-03-02 07:49:49 +00:00
|
|
|
Honeywell does have a proprietary away mode, but it doesn't really work
|
2016-08-19 07:17:28 +00:00
|
|
|
the way it should. For example: If you set a temperature manually
|
|
|
|
it doesn't get overwritten when away mode is switched on.
|
|
|
|
"""
|
|
|
|
self._away = True
|
2017-08-01 14:18:14 +00:00
|
|
|
self.client.set_temperature(self._name, self._away_temp)
|
2016-08-19 07:17:28 +00:00
|
|
|
|
|
|
|
def turn_away_mode_off(self):
|
|
|
|
"""Turn away off."""
|
|
|
|
self._away = False
|
2017-08-01 14:18:14 +00:00
|
|
|
self.client.cancel_temp_override(self._name)
|
2016-08-19 07:17:28 +00:00
|
|
|
|
|
|
|
def update(self):
|
|
|
|
"""Get the latest date."""
|
|
|
|
try:
|
|
|
|
# Only refresh if this is the "master" device,
|
|
|
|
# others will pick up the cache
|
2017-08-01 14:18:14 +00:00
|
|
|
for val in self.client.temperatures(force_refresh=self._master):
|
2016-08-19 07:17:28 +00:00
|
|
|
if val['id'] == self._id:
|
|
|
|
data = val
|
|
|
|
|
2017-09-05 11:06:28 +00:00
|
|
|
except KeyError:
|
|
|
|
_LOGGER.error("Update failed from Honeywell server")
|
|
|
|
self.client.user_data = None
|
|
|
|
return
|
|
|
|
|
2016-08-19 07:17:28 +00:00
|
|
|
except StopIteration:
|
|
|
|
_LOGGER.error("Did not receive any temperature data from the "
|
2017-04-30 05:04:49 +00:00
|
|
|
"evohomeclient API")
|
2016-08-19 07:17:28 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
self._current_temperature = data['temp']
|
|
|
|
self._target_temperature = data['setpoint']
|
2016-09-11 07:21:16 +00:00
|
|
|
if data['thermostat'] == 'DOMESTIC_HOT_WATER':
|
|
|
|
self._name = 'Hot Water'
|
2016-08-19 07:17:28 +00:00
|
|
|
self._is_dhw = True
|
|
|
|
else:
|
|
|
|
self._name = data['name']
|
|
|
|
self._is_dhw = False
|
|
|
|
|
2017-08-01 14:18:14 +00:00
|
|
|
# The underlying library doesn't expose the thermostat's mode
|
|
|
|
# but we can pull it out of the big dictionary of information.
|
|
|
|
device = self.client.devices[self._id]
|
|
|
|
self.client.system_mode = device[
|
|
|
|
'thermostat']['changeableValues']['mode']
|
|
|
|
|
2016-08-19 07:17:28 +00:00
|
|
|
|
|
|
|
class HoneywellUSThermostat(ClimateDevice):
|
|
|
|
"""Representation of a Honeywell US Thermostat."""
|
|
|
|
|
2017-03-02 07:49:49 +00:00
|
|
|
def __init__(self, client, device, cool_away_temp,
|
|
|
|
heat_away_temp, username, password):
|
2016-08-19 07:17:28 +00:00
|
|
|
"""Initialize the thermostat."""
|
|
|
|
self._client = client
|
|
|
|
self._device = device
|
2017-03-02 07:49:49 +00:00
|
|
|
self._cool_away_temp = cool_away_temp
|
|
|
|
self._heat_away_temp = heat_away_temp
|
|
|
|
self._away = False
|
|
|
|
self._username = username
|
|
|
|
self._password = password
|
2016-08-19 07:17:28 +00:00
|
|
|
|
2017-11-29 10:01:28 +00:00
|
|
|
@property
|
|
|
|
def supported_features(self):
|
|
|
|
"""Return the list of supported features."""
|
|
|
|
supported = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_AWAY_MODE)
|
|
|
|
if hasattr(self._device, ATTR_SYSTEM_MODE):
|
|
|
|
supported |= SUPPORT_OPERATION_MODE
|
|
|
|
return supported
|
|
|
|
|
2016-08-19 07:17:28 +00:00
|
|
|
@property
|
|
|
|
def is_fan_on(self):
|
|
|
|
"""Return true if fan is on."""
|
|
|
|
return self._device.fan_running
|
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self):
|
|
|
|
"""Return the name of the honeywell, if any."""
|
|
|
|
return self._device.name
|
|
|
|
|
|
|
|
@property
|
2016-10-11 07:00:29 +00:00
|
|
|
def temperature_unit(self):
|
2016-08-19 07:17:28 +00:00
|
|
|
"""Return the unit of measurement."""
|
|
|
|
return (TEMP_CELSIUS if self._device.temperature_unit == 'C'
|
|
|
|
else TEMP_FAHRENHEIT)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def current_temperature(self):
|
|
|
|
"""Return the current temperature."""
|
|
|
|
return self._device.current_temperature
|
|
|
|
|
|
|
|
@property
|
|
|
|
def target_temperature(self):
|
|
|
|
"""Return the temperature we try to reach."""
|
|
|
|
if self._device.system_mode == 'cool':
|
|
|
|
return self._device.setpoint_cool
|
2017-07-06 06:30:01 +00:00
|
|
|
return self._device.setpoint_heat
|
2016-08-19 07:17:28 +00:00
|
|
|
|
|
|
|
@property
|
2018-07-30 11:44:31 +00:00
|
|
|
def current_operation(self) -> str:
|
2016-08-19 07:17:28 +00:00
|
|
|
"""Return current operation ie. heat, cool, idle."""
|
2017-03-02 07:49:49 +00:00
|
|
|
oper = getattr(self._device, ATTR_CURRENT_OPERATION, None)
|
|
|
|
if oper == "off":
|
|
|
|
oper = "idle"
|
|
|
|
return oper
|
2016-08-19 07:17:28 +00:00
|
|
|
|
2016-09-09 17:06:53 +00:00
|
|
|
def set_temperature(self, **kwargs):
|
2016-08-19 07:17:28 +00:00
|
|
|
"""Set target temperature."""
|
2016-09-09 17:06:53 +00:00
|
|
|
temperature = kwargs.get(ATTR_TEMPERATURE)
|
|
|
|
if temperature is None:
|
|
|
|
return
|
2016-08-19 07:17:28 +00:00
|
|
|
import somecomfort
|
|
|
|
try:
|
2017-03-02 07:49:49 +00:00
|
|
|
# Get current mode
|
|
|
|
mode = self._device.system_mode
|
|
|
|
# Set hold if this is not the case
|
|
|
|
if getattr(self._device, "hold_{}".format(mode)) is False:
|
|
|
|
# Get next period key
|
|
|
|
next_period_key = '{}NextPeriod'.format(mode.capitalize())
|
|
|
|
# Get next period raw value
|
|
|
|
next_period = self._device.raw_ui_data.get(next_period_key)
|
|
|
|
# Get next period time
|
|
|
|
hour, minute = divmod(next_period * 15, 60)
|
|
|
|
# Set hold time
|
|
|
|
setattr(self._device,
|
|
|
|
"hold_{}".format(mode),
|
|
|
|
datetime.time(hour, minute))
|
|
|
|
# Set temperature
|
|
|
|
setattr(self._device,
|
|
|
|
"setpoint_{}".format(mode),
|
|
|
|
temperature)
|
2016-08-19 07:17:28 +00:00
|
|
|
except somecomfort.SomeComfortError:
|
2017-04-30 05:04:49 +00:00
|
|
|
_LOGGER.error("Temperature %.1f out of range", temperature)
|
2016-08-19 07:17:28 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def device_state_attributes(self):
|
|
|
|
"""Return the device specific state attributes."""
|
2017-03-02 07:49:49 +00:00
|
|
|
import somecomfort
|
|
|
|
data = {
|
2016-09-11 07:21:16 +00:00
|
|
|
ATTR_FAN: (self.is_fan_on and 'running' or 'idle'),
|
2017-03-02 07:49:49 +00:00
|
|
|
ATTR_FAN_MODE: self._device.fan_mode,
|
|
|
|
ATTR_OPERATION_MODE: self._device.system_mode,
|
2016-09-11 07:21:16 +00:00
|
|
|
}
|
2017-03-02 07:49:49 +00:00
|
|
|
data[ATTR_FAN_LIST] = somecomfort.FAN_MODES
|
|
|
|
data[ATTR_OPERATION_LIST] = somecomfort.SYSTEM_MODES
|
|
|
|
return data
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_away_mode_on(self):
|
|
|
|
"""Return true if away mode is on."""
|
|
|
|
return self._away
|
2016-08-19 07:17:28 +00:00
|
|
|
|
|
|
|
def turn_away_mode_on(self):
|
2017-03-02 07:49:49 +00:00
|
|
|
"""Turn away on.
|
|
|
|
|
|
|
|
Somecomfort does have a proprietary away mode, but it doesn't really
|
|
|
|
work the way it should. For example: If you set a temperature manually
|
|
|
|
it doesn't get overwritten when away mode is switched on.
|
|
|
|
"""
|
|
|
|
self._away = True
|
|
|
|
import somecomfort
|
|
|
|
try:
|
|
|
|
# Get current mode
|
|
|
|
mode = self._device.system_mode
|
|
|
|
except somecomfort.SomeComfortError:
|
|
|
|
_LOGGER.error('Can not get system mode')
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
|
|
|
|
# Set permanent hold
|
|
|
|
setattr(self._device,
|
|
|
|
"hold_{}".format(mode),
|
|
|
|
True)
|
|
|
|
# Set temperature
|
|
|
|
setattr(self._device,
|
|
|
|
"setpoint_{}".format(mode),
|
|
|
|
getattr(self, "_{}_away_temp".format(mode)))
|
|
|
|
except somecomfort.SomeComfortError:
|
|
|
|
_LOGGER.error('Temperature %.1f out of range',
|
|
|
|
getattr(self, "_{}_away_temp".format(mode)))
|
2016-08-19 07:17:28 +00:00
|
|
|
|
|
|
|
def turn_away_mode_off(self):
|
|
|
|
"""Turn away off."""
|
2017-03-02 07:49:49 +00:00
|
|
|
self._away = False
|
|
|
|
import somecomfort
|
|
|
|
try:
|
|
|
|
# Disabling all hold modes
|
|
|
|
self._device.hold_cool = False
|
|
|
|
self._device.hold_heat = False
|
|
|
|
except somecomfort.SomeComfortError:
|
|
|
|
_LOGGER.error('Can not stop hold mode')
|
2016-08-19 07:17:28 +00:00
|
|
|
|
2018-07-30 11:44:31 +00:00
|
|
|
def set_operation_mode(self, operation_mode: str) -> None:
|
2016-08-19 07:17:28 +00:00
|
|
|
"""Set the system mode (Cool, Heat, etc)."""
|
2016-09-11 07:21:16 +00:00
|
|
|
if hasattr(self._device, ATTR_SYSTEM_MODE):
|
2016-08-19 07:17:28 +00:00
|
|
|
self._device.system_mode = operation_mode
|
2016-11-06 15:53:54 +00:00
|
|
|
|
|
|
|
def update(self):
|
|
|
|
"""Update the state."""
|
2017-03-02 07:49:49 +00:00
|
|
|
import somecomfort
|
|
|
|
retries = 3
|
|
|
|
while retries > 0:
|
|
|
|
try:
|
|
|
|
self._device.refresh()
|
|
|
|
break
|
|
|
|
except (somecomfort.client.APIRateLimited, OSError,
|
|
|
|
requests.exceptions.ReadTimeout) as exp:
|
|
|
|
retries -= 1
|
|
|
|
if retries == 0:
|
|
|
|
raise exp
|
|
|
|
if not self._retry():
|
|
|
|
raise exp
|
2017-04-30 05:04:49 +00:00
|
|
|
_LOGGER.error(
|
|
|
|
"SomeComfort update failed, Retrying - Error: %s", exp)
|
2017-03-02 07:49:49 +00:00
|
|
|
|
|
|
|
def _retry(self):
|
|
|
|
"""Recreate a new somecomfort client.
|
|
|
|
|
|
|
|
When we got an error, the best way to be sure that the next query
|
|
|
|
will succeed, is to recreate a new somecomfort client.
|
|
|
|
"""
|
|
|
|
import somecomfort
|
|
|
|
try:
|
2017-04-30 05:04:49 +00:00
|
|
|
self._client = somecomfort.SomeComfort(
|
|
|
|
self._username, self._password)
|
2017-03-02 07:49:49 +00:00
|
|
|
except somecomfort.AuthError:
|
2017-04-30 05:04:49 +00:00
|
|
|
_LOGGER.error("Failed to login to honeywell account %s",
|
2017-03-02 07:49:49 +00:00
|
|
|
self._username)
|
|
|
|
return False
|
|
|
|
except somecomfort.SomeComfortError as ex:
|
2017-04-30 05:04:49 +00:00
|
|
|
_LOGGER.error("Failed to initialize honeywell client: %s",
|
2017-03-02 07:49:49 +00:00
|
|
|
str(ex))
|
|
|
|
return False
|
|
|
|
|
|
|
|
devices = [device
|
|
|
|
for location in self._client.locations_by_id.values()
|
|
|
|
for device in location.devices_by_id.values()
|
|
|
|
if device.name == self._device.name]
|
|
|
|
|
|
|
|
if len(devices) != 1:
|
2017-04-30 05:04:49 +00:00
|
|
|
_LOGGER.error("Failed to find device %s", self._device.name)
|
2017-03-02 07:49:49 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
self._device = devices[0]
|
2017-03-05 01:15:20 +00:00
|
|
|
return True
|